Merge pull request #7 from FairyEver/dev

page loading style bug fixed

Former-commit-id: f5cc02262e30055b31d343b1678958015cb5bc13
Former-commit-id: e252235a1af4d7f450d85606c99559e086cb8585
Former-commit-id: ae92929f00578470a7b5ab2318e00506ed0892b6
This commit is contained in:
李杨
2018-06-09 22:22:14 +08:00
committed by GitHub
62 changed files with 831 additions and 482 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.css linguist-language=JavaScript

View File

@@ -4,12 +4,12 @@ module.exports = {
'/': {
lang: 'en-US',
title: 'D2 Admin',
description: 'An elegant template for management system'
description: 'Elegant management system front-end integration'
},
'/zh/': {
lang: 'zh-CN',
title: 'D2 Admin',
description: '做一个优雅的管理系统模板'
description: '优雅的管理系统前端集成方案'
}
},
themeConfig: {
@@ -58,6 +58,7 @@ function sideBarGuide (title) {
children: [
'',
'q-a',
'pr',
'change-log'
]
}
@@ -87,7 +88,8 @@ function sideBarComponents (title) {
'icon-select',
'icon-svg',
'icon',
'markdown'
'markdown',
'locally-valid'
]
}
]

View File

@@ -4,10 +4,10 @@ heroImage: /logo@2x.png
actionText: Get Started →
actionLink: /zh/guide/
features:
- title: Advocate succinct
details: On the visual basis of element UI, we have added some customization.
- title: Vue-Powered
details: Enjoy the dev experience of Vue + webpack, Ultra fast virtual DOM and the most economical optimization.
- title: Rich integration
details: The plug-ins and components that are likely to be used are ready for you. Some come from third parties, others are designed for d2admin.
- title: ElementUI component library
details: Integrated and robust ElementUI, and use any ElementUI component at will.
footer: MIT Licensed | Copyright © 2018-present FairyEver
@@ -15,11 +15,9 @@ footer: MIT Licensed | Copyright © 2018-present FairyEver
**The English document will be launched in the future. Please move to the Chinese document.**
**英文文档会在后推出,现请移步右上角中文文档**
**英文文档会在中文文档大致完成后推出,现请移步中文文档**
```
// chone
git clone https://github.com/FairyEver/d2admin-vue-element.git
// install package
npm i
// run

View File

@@ -4,18 +4,16 @@ heroImage: /logo@2x.png
actionText: 快速上手 →
actionLink: /zh/guide/
features:
- title: 简洁至上
details: 在 element UI 的视觉基础上稍加定制。
- title: Vue驱动
details: 享受 Vue + webpack 的开发体验,超快虚拟 DOM 和最省心的优化。
- title: 丰富集成
details: 已经为你准备好了很可能用到的插件和组件,有的来自第三方,有的专为 d2admin 设计。
- title: ElementUI组件库
details: 集成完善且强大的 ElementUI随意搭配使用任何 ElementUI 组件。
footer: MIT Licensed | Copyright © 2018-present FairyEver
---
```
// 克隆仓库
git clone https://github.com/FairyEver/d2admin-vue-element.git
// 安装依赖
npm i
// 运行

View File

@@ -6,7 +6,7 @@
| 参数名 | 介绍 | 必选 | 值类型 | 可选值 | 默认值 |
| --- | --- | --- | --- | --- | --- |
| type | 容器类型 | 非 | String | card ghost card-full | card |
| type | 容器类型 | 非 | String | card ghost full | card |
| responsive | 响应式宽度 | 非 | Boolean | | false |
::: tip
@@ -66,7 +66,7 @@ export default {
``` vue
<template>
<Container type="card-full">
<Container type="full">
<template slot="header">
可选的 header 内容 ...
</template>

View File

@@ -0,0 +1,19 @@
# 局部组件
局部组件是指在某个组件内注册的组件,它一般只在一个地方用到,全局无法访问
## D2MenuItem
::: tip 出现位置
src/components/core/MainLayout/components/SideMenu/index.vue
:::
该组件会根据数据渲染 `<el-menu-item>`
## D2Submenu
::: tip 出现位置
src/components/core/MainLayout/components/SideMenu/index.vue
:::
该组件会根据数据渲染 `<el-submenu>` 结构,并且在 `<el-submenu>` 中再次根据数据类型递归自身,最终生成 elementUI 的菜单组件结构,支持嵌套多级

View File

@@ -4,6 +4,11 @@ d2admin-vue-element以下简称 d2admin是一个管理系统前端模板
[Github仓库](https://github.com/FairyEver/d2admin-vue-element) - [预览地址](http://d2admin.fairyever.com/)
<div>
<iframe src="//ghbtns.com/github-btn.html?user=FairyEver&repo=d2admin-vue-element&type=star&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>
<iframe src="//ghbtns.com/github-btn.html?user=FairyEver&repo=d2admin-vue-element&type=fork&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>
</div>
::: tip
因为现在集成了很多的插件和组件,首次加载会占用较多的时间,虽然已经做了首屏加载动画,还是建议您在发布的时候一定要删除没有用到的代码。比如项目没有用到图表,最好将集成的图表库以及封装的图表组件删除。
:::

View File

@@ -6,6 +6,9 @@
这是正在开发的版本,还没有正式发布
:::
* [ 修改 ] 顶栏和侧边栏菜单新的结构生成方式,新的方案使用 `数据` + `递归组件` 实现无限制多级菜单
* [ 修改 ] 路由注册回归最简单的写法
## v1.0.0
[https://github.com/FairyEver/d2admin-vue-element/releases/tag/v1.0.0](https://github.com/FairyEver/d2admin-vue-element/releases/tag/v1.0.0)

13
docs/zh/guide/pr.md Normal file
View File

@@ -0,0 +1,13 @@
# 贡献指南
## 如何贡献代码
首先在[项目主页](https://github.com/FairyEver/d2admin-vue-element) fork 本仓库,然后进行你的开发,请确保你的开发是在 dev 分支进行。本地测试无误后方可 pull request我在检查通过后会合并代码到原始仓库。
## 要求
本项目代码需要符合[vue.js 风格指南](https://cn.vuejs.org/v2/style-guide/)中[优先级A](https://cn.vuejs.org/v2/style-guide/#%E4%BC%98%E5%85%88%E7%BA%A7-A-%E7%9A%84%E8%A7%84%E5%88%99%EF%BC%9A%E5%BF%85%E8%A6%81%E7%9A%84-%E8%A7%84%E9%81%BF%E9%94%99%E8%AF%AF)和[优先级B](https://cn.vuejs.org/v2/style-guide/#%E4%BC%98%E5%85%88%E7%BA%A7-B-%E7%9A%84%E8%A7%84%E5%88%99%EF%BC%9A%E5%BC%BA%E7%83%88%E6%8E%A8%E8%8D%90-%E5%A2%9E%E5%BC%BA%E5%8F%AF%E8%AF%BB%E6%80%A7)的要求以及本项目中的 ESlint 校验
::: tip
由于项目初始没有仔细遵循风格指南可能项目代码中已经存在不符合规范的代码欢迎批评指正或者直接pr
:::

View File

@@ -10,7 +10,7 @@
margin: 0px;
padding: 0px;
}
.loading-group {
.d2-app-loading-group {
height: 100%;
width: 100%;
display: flex;
@@ -27,7 +27,7 @@
background: -webkit-linear-gradient(to top, #D7DDE8, #757F9A);
background: linear-gradient(to top, #D7DDE8, #757F9A);
}
.title {
.d2-app-loading-title {
color: #FFF;
font-weight: bold;
font-size: 14px;
@@ -35,56 +35,56 @@
margin-bottom: 10px;
letter-spacing: 0.2em;
}
.sub-title {
.d2-app-loading-sub-title {
color: #FFF;
font-size: 10px;
}
.sk-cube-grid {
.d2-app-loading-grid {
width: 30px;
height: 30px;
}
.sk-cube-grid .sk-cube {
.d2-app-loading-grid .d2-app-loading {
width: 10px;
height: 10px;
background-color: #FFF;
float: left;
-webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
-webkit-animation: d2-app-loading-grid-scale-delay 1.3s infinite ease-in-out;
animation: d2-app-loading-grid-scale-delay 1.3s infinite ease-in-out;
}
.sk-cube-grid .sk-cube1 {
.d2-app-loading-grid .d2-app-loading1 {
border-top-left-radius: 4px;
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s; }
.sk-cube-grid .sk-cube2 {
.d2-app-loading-grid .d2-app-loading2 {
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s; }
.sk-cube-grid .sk-cube3 {
.d2-app-loading-grid .d2-app-loading3 {
border-top-right-radius: 4px;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s; }
.sk-cube-grid .sk-cube4 {
.d2-app-loading-grid .d2-app-loading4 {
-webkit-animation-delay: 0.1s;
animation-delay: 0.1s; }
.sk-cube-grid .sk-cube5 {
.d2-app-loading-grid .d2-app-loading5 {
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s; }
.sk-cube-grid .sk-cube6 {
.d2-app-loading-grid .d2-app-loading6 {
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s; }
.sk-cube-grid .sk-cube7 {
.d2-app-loading-grid .d2-app-loading7 {
border-bottom-left-radius: 4px;
-webkit-animation-delay: 0s;
animation-delay: 0s; }
.sk-cube-grid .sk-cube8 {
.d2-app-loading-grid .d2-app-loading8 {
-webkit-animation-delay: 0.1s;
animation-delay: 0.1s; }
.sk-cube-grid .sk-cube9 {
.d2-app-loading-grid .d2-app-loading9 {
border-bottom-right-radius: 4px;
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s; }
@-webkit-keyframes sk-cubeGridScaleDelay {
@-webkit-keyframes d2-app-loading-grid-scale-delay {
0%, 70%, 100% {
-webkit-transform: scale3D(1, 1, 1);
transform: scale3D(1, 1, 1);
@@ -94,7 +94,7 @@
}
}
@keyframes sk-cubeGridScaleDelay {
@keyframes d2-app-loading-grid-scale-delay {
0%, 70%, 100% {
-webkit-transform: scale3D(1, 1, 1);
transform: scale3D(1, 1, 1);
@@ -107,20 +107,20 @@
</head>
<body>
<div id="app">
<div class="loading-group">
<div class="sk-cube-grid">
<div class="sk-cube sk-cube1"></div>
<div class="sk-cube sk-cube2"></div>
<div class="sk-cube sk-cube3"></div>
<div class="sk-cube sk-cube4"></div>
<div class="sk-cube sk-cube5"></div>
<div class="sk-cube sk-cube6"></div>
<div class="sk-cube sk-cube7"></div>
<div class="sk-cube sk-cube8"></div>
<div class="sk-cube sk-cube9"></div>
<div class="d2-app-loading-group">
<div class="d2-app-loading-grid">
<div class="d2-app-loading d2-app-loading1"></div>
<div class="d2-app-loading d2-app-loading2"></div>
<div class="d2-app-loading d2-app-loading3"></div>
<div class="d2-app-loading d2-app-loading4"></div>
<div class="d2-app-loading d2-app-loading5"></div>
<div class="d2-app-loading d2-app-loading6"></div>
<div class="d2-app-loading d2-app-loading7"></div>
<div class="d2-app-loading d2-app-loading8"></div>
<div class="d2-app-loading d2-app-loading9"></div>
</div>
<div class="title">加载中</div>
<div class="sub-title">初次加载会比较慢,请耐心等待</div>
<div class="d2-app-loading-title">加载中</div>
<div class="d2-app-loading-sub-title">初次加载会比较慢,请耐心等待</div>
</div>
</div>
</body>

View File

@@ -23,10 +23,12 @@
"github-markdown-css": "^2.10.0",
"highlight.js": "^9.12.0",
"js-cookie": "^2.2.0",
"lodash.get": "^4.4.2",
"marked": "^0.3.9",
"mockjs": "^1.0.1-beta3",
"papaparse": "^4.3.6",
"particles.js": "^2.0.0",
"path-posix": "^1.0.0",
"quill": "^1.3.4",
"simplemde": "^1.11.2",
"timeago.js": "^3.0.2",

View File

@@ -1 +1,13 @@
// element 样式补丁
// element 样式补丁
.el-card {
box-shadow: none;
&:hover {
// box-shadow: 0 0 8px 0 rgba(232,237,250,.6), 0 2px 4px 0 rgba(232,237,250,.5);
box-shadow: none;
}
}
.el-menu--horizontal {
border-bottom: none;
}

View File

@@ -1,129 +0,0 @@
@import '~@/assets/style/public.scss';
// 默认主题
.layout-main {
&.classic {
height: 100vh;
width: 100vw;
background-color: #EFF4F8;
// [全局设置]
// 关闭所有卡片的阴影
.el-card {
box-shadow: none;
&:hover {
box-shadow: 0 0 8px 0 rgba(232,237,250,.6), 0 2px 4px 0 rgba(232,237,250,.5);
}
}
// [布局]
.el-header {
padding: 0px;
.logo-group {
transition: width .3s;
float: left;
text-align: center;
img {
height: 60px;
}
}
.toggle-sidemenu-btn {
float: left;
height: 60px;
width: 60px;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
cursor: pointer;
i {
font-size: 20px;
color: $color-text-normal;
margin-top: 4px;
&:hover {
color: $color-primary;
}
}
}
// 顶栏菜单
.el-menu {
float: left;
border-bottom: none;
background-color: transparent;
.el-menu-item {
border-bottom: none;
border-top: 2px solid #EFF4F8;
&:hover {
background-color: transparent;
}
&.is-active {
border-bottom: none;
border-top: 2px solid $color-primary;
background-color: transparent;
}
}
}
// 顶栏右侧的按钮
.btn-group {
float: right;
height: 60px;
display: flex;
align-items: center;
.btn-text {
color: $color-text-normal;
}
.el-dropdown {
user-select: none;
cursor: pointer;
}
}
}
// 顶栏下面
.el-container {
// 侧边栏
.el-aside {
transition: width .3s;
overflow: inherit;
.dd-side-menu-empty {
background-color: rgba(#000, .03);
margin: $margin;
margin-top: 0px;
border-radius: 4px;
line-height: 100px;
text-align: center;
color: $color-text-sub;
}
.el-menu {
background-color: transparent;
border-right: none;
&.el-menu--collapse {
.el-submenu {
.el-menu {
background-color: transparent;
}
}
.el-submenu__title {
text-align: center;
}
}
.el-menu-item {
&:focus {
background-color: rgba(#000, .05);
}
&:hover {
background-color: rgba(#000, .05);
}
}
.el-submenu__title {
&:hover {
background-color: rgba(#000, .05);
}
}
}
}
.el-main {
padding: 0px;
position: relative;
overflow: hidden;
}
}
}
}

View File

@@ -0,0 +1,132 @@
@import '../theme.scss';
$theme-name: 'star';
$theme-bg-color: #EFF4F8;
$theme-bg-image: '/static/image/bg/star.jpg';
$theme-container-full-border-color: #d8dfea;
.theme-#{$theme-name} {
.theme {
background-color: $theme-bg-color;
background-image: url($theme-bg-image);
background-size: cover;
background-position: center;
}
// 菜单项目
@mixin theme-menu-hover-style {
background-color: #eff4f8;
}
.el-submenu__title:hover {
@include theme-menu-hover-style;
}
.el-menu-item:hover {
@include theme-menu-hover-style;
}
.el-menu--horizontal .el-menu-item:not(.is-disabled):hover {
@include theme-menu-hover-style;
}
.el-menu--horizontal .el-menu .el-submenu__title:hover {
@include theme-menu-hover-style;
}
// [组件] dd-container-full
.dd-container-full {
.dd-container-full__header {
border-bottom: 1px solid $theme-container-full-border-color;
}
.dd-container-full__footer {
border-top: 1px solid $theme-container-full-border-color;
}
}
// 顶栏
.el-header {
// 切换按钮
.toggle-aside-btn {
i {
color: #FFF;
&:hover {
color: #FFF;
}
}
}
// 顶栏菜单
.el-menu {
.el-menu-item {
transition: border-top-color 0s;
color: #FFF;
&:hover {
background-color: rgba(#FFF, .3);
}
&:focus {
background-color: rgba(#FFF, .3);
}
&.is-active {
background-color: rgba(#000, .3);
}
}
.el-submenu {
.el-submenu__title {
transition: border-top-color 0s;
color: #FFF;
&:hover {
background-color: rgba(#FFF, .3);
}
&:focus {
background-color: rgba(#FFF, .3);
}
.el-submenu__icon-arrow {
color: #FFF;
}
}
}
}
// 顶栏右侧
.dd-header-right {
.btn-text {
color: #FFF;
}
.el-dropdown {
.el-dropdown-link {
color: #FFF;
}
}
}
}
// [布局] 顶栏下面
.el-container {
// 侧边栏
.el-aside {
// [菜单] 正常状态
.el-menu {
.el-menu-item {
color: #FFF;
&:hover {
background-color: rgba(#FFF, .3);
}
&:focus {
background-color: rgba(#FFF, .3);
}
i {
color: #FFF;
}
&.is-active {
background-color: rgba(#000, .3);
}
}
}
.el-submenu {
.el-submenu__title {
color: #FFF;
&:hover {
background-color: rgba(#FFF, .3);
}
i {
color: #FFF;
}
.el-submenu__icon-arrow {
color: #FFF;
}
}
}
}
}
}

View File

@@ -0,0 +1,148 @@
@import '~@/assets/style/public.scss';
// 主题公用
.theme {
height: 100vh;
width: 100vw;
// [布局] 顶栏
.el-header {
padding: 0px;
// logo区域
.logo-group {
transition: width .3s;
float: left;
text-align: center;
img {
height: 60px;
}
}
// 折叠侧边栏切换按钮
.toggle-aside-btn {
float: left;
height: 60px;
width: 60px;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
cursor: pointer;
i {
font-size: 20px;
margin-top: 4px;
}
}
// [菜单] 顶栏
.el-menu {
float: left;
border-bottom: none;
background-color: transparent;
.el-menu-item {
border-bottom: none;
}
.el-submenu {
.el-submenu__title {
border-bottom: none;
}
}
}
// 顶栏右侧的按钮
.dd-header-right {
float: right;
height: 60px;
display: flex;
align-items: center;
.el-dropdown {
user-select: none;
cursor: pointer;
}
}
}
// [布局] 顶栏下面
.el-container {
// 侧边栏
.el-aside {
transition: width .3s;
overflow: inherit;
// 空菜单
.dd-side-menu-empty {
background-color: rgba(#000, .03);
margin: $margin;
margin-top: 0px;
border-radius: 4px;
line-height: 100px;
text-align: center;
color: $color-text-sub;
}
// [菜单] 正常状态
.el-menu {
background-color: transparent;
border-right: none;
.el-menu-item {
i {
margin-right: 5px;
font-size: 20px;
}
}
}
.el-submenu {
.el-submenu__title {
i {
margin-right: 5px;
font-size: 20px;
}
.el-submenu__icon-arrow {
margin-top: -10px;
}
}
}
// [菜单] 折叠状态
.el-menu--collapse {
background-color: transparent;
.el-submenu__title {
text-align: center;
}
}
}
.el-main {
padding: 0px;
position: relative;
overflow: hidden;
}
}
// [组件] dd-container-full
.dd-container-full {
position: absolute;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
background-color: #FFF;
overflow: hidden;
color: #303133;
&:hover {
box-shadow: none;
}
.dd-container-full__header {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
padding: 18px 20px;
box-sizing: border-box;
}
.dd-container-full__body {
position: absolute;
padding: 20px;
left: 0px;
right: 0px;
bottom: 0px;
overflow: auto;
}
.dd-container-full__footer {
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
padding: 18px 20px;
box-sizing: border-box;
}
}
}

View File

@@ -1,112 +0,0 @@
<template>
<div class="dd-card-full" :style="cardStyle">
<div v-if="$slots.header" class="dd-card-full__header" ref="header">
<slot name="header"></slot>
</div>
<div class="dd-card-full__body" :style="bodyStyle">
<slot></slot>
</div>
<div v-if="$slots.footer" class="dd-card-full__footer" ref="footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<script>
export default {
props: {
// 定位 上 右 下 左
top: {
type: Number,
required: false,
default: 0
},
right: {
type: Number,
required: false,
default: 0
},
bottom: {
type: Number,
required: false,
default: 0
},
left: {
type: Number,
required: false,
default: 0
}
},
data () {
return {
headerHeight: 0,
footerHeight: 0
}
},
mounted () {
this.headerHeight = this.$slots.header ? this.$refs.header.offsetHeight : 0
this.footerHeight = this.$slots.footer ? this.$refs.footer.offsetHeight : 0
},
computed: {
cardStyle () {
return {
top: `${this.top}px`,
right: `${this.right}px`,
bottom: `${this.bottom}px`,
left: `${this.left}px`
}
},
bodyStyle () {
return {
top: `${this.headerHeight}px`,
bottom: `${this.footerHeight}px`
}
}
}
}
</script>
<style lang="scss" scoped>
$border-color: #d8dfea;
.dd-card-full {
position: absolute;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-top: 1px solid $border-color;
border-left: 1px solid $border-color;
border-right: 1px solid $border-color;
background-color: #fff;
overflow: hidden;
color: #303133;
&:hover {
box-shadow: 0 0 8px 0 rgba(232,237,250,.6), 0 2px 4px 0 rgba(232,237,250,.5);
}
.dd-card-full__header {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
padding: 18px 20px;
border-bottom: 1px solid $border-color;
box-sizing: border-box;
}
.dd-card-full__body {
position: absolute;
padding: 20px;
left: 0px;
right: 0px;
bottom: 0px;
overflow: auto;
}
.dd-card-full__footer {
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
padding: 18px 20px;
border-top: 1px solid $border-color;
box-sizing: border-box;
}
}
</style>

View File

@@ -10,12 +10,12 @@
<slot name="header"></slot>
<slot></slot>
</div>
<!-- [card-full] 撑满 -->
<card-full v-if="type === 'card-full'" :right="10" :bottom="0">
<!-- [container-full] 撑满 -->
<container-full v-if="type === 'full'" :right="20" :bottom="0">
<slot v-if="$slots.header" name="header" slot="header"></slot>
<slot></slot>
<slot v-if="$slots.footer" name="footer" slot="footer"></slot>
</card-full>
</container-full>
</div>
</template>
@@ -34,6 +34,9 @@ export default {
required: false,
default: false
}
},
components: {
containerFull: () => import('../ContainerFull/index.vue')
}
}
</script>

View File

@@ -0,0 +1,67 @@
<template>
<div class="dd-container-full" :style="cardStyle">
<div v-if="$slots.header" class="dd-container-full__header" ref="header">
<slot name="header"></slot>
</div>
<div class="dd-container-full__body" :style="bodyStyle">
<slot></slot>
</div>
<div v-if="$slots.footer" class="dd-container-full__footer" ref="footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<script>
export default {
props: {
// 定位 上 右 下 左
top: {
type: Number,
required: false,
default: 0
},
right: {
type: Number,
required: false,
default: 0
},
bottom: {
type: Number,
required: false,
default: 0
},
left: {
type: Number,
required: false,
default: 0
}
},
data () {
return {
headerHeight: 0,
footerHeight: 0
}
},
mounted () {
this.headerHeight = this.$slots.header ? this.$refs.header.offsetHeight : 0
this.footerHeight = this.$slots.footer ? this.$refs.footer.offsetHeight : 0
},
computed: {
cardStyle () {
return {
top: `${this.top}px`,
right: `${this.right}px`,
bottom: `${this.bottom}px`,
left: `${this.left}px`
}
},
bodyStyle () {
return {
top: `${this.headerHeight}px`,
bottom: `${this.footerHeight}px`
}
}
}
}
</script>

View File

@@ -0,0 +1,19 @@
<template>
<el-menu-item :index="menu.path">
<i v-if="menu.icon" :class="`fa fa-${menu.icon}`"></i>
<span slot="title">{{menu.title}}</span>
</el-menu-item>
</template>
<script>
export default {
name: 'D2MenuItem',
props: {
menu: {
type: Object,
required: false,
default: () => {}
}
}
}
</script>

View File

@@ -0,0 +1,28 @@
<template>
<el-submenu :index="menu.path">
<template slot="title">
<i v-if="menu.icon" :class="`fa fa-${menu.icon}`"></i>
<span slot="title">{{menu.title}}</span>
</template>
<template v-for="(child, childIndex) in menu.children">
<d2-menu-item v-if="child.children === undefined" :menu="child" :key="childIndex"/>
<d2-submenu v-else :menu="child" :key="childIndex"/>
</template>
</el-submenu>
</template>
<script>
export default {
name: 'D2Submenu',
props: {
menu: {
type: Object,
required: false,
default: () => {}
}
},
components: {
D2MenuItem: () => import('../D2MenuItem/index.vue')
}
}
</script>

View File

@@ -1,75 +1,25 @@
<template>
<el-menu
class="el-menu-demo"
mode="horizontal">
<el-menu-item index="index" @click.native="active({name: 'index'})">首页</el-menu-item>
<el-menu-item
v-for="(item, index) in menu"
:key="index"
:index="String(index)"
@click.native="active(item)">
{{item.title}}
</el-menu-item>
mode="horizontal"
:router="true">
<template v-for="(menu, menuIndex) in menus">
<d2-menu-item v-if="menu.children === undefined" :menu="menu" :key="menuIndex"/>
<d2-submenu v-else :menu="menu" :key="menuIndex"/>
</template>
</el-menu>
</template>
<script>
import { mapMutations } from 'vuex'
import { menu, router } from '@/router/menu/index.js'
import menus from '@/menu/index.js'
export default {
name: 'HeaderMenu',
components: {
D2MenuItem: () => import('../D2MenuItem/index.vue'),
D2Submenu: () => import('../D2Submenu/index.vue')
},
data () {
return {
menu,
router
}
},
computed: {
// 当前路由的name
// 仅仅是返回当前的name而已
routeName () {
return this.$route.name
},
// 不管当前路由是不是顶级菜单 都返回这个路由所属的顶级菜单对象的name
// 如果返回 null 代表这个路由不是在菜单里显示的路由
routeTopLevelName () {
if (this.router.find(e => e.name === this.routeName)) {
return this.routeName
} else {
const find = this.router.find(e => e.children.find(child => child.name === this.routeName))
return find ? find.name : null
}
},
// 返回当前对象对应的顶级菜单下的所有子菜单 这些菜单可以在侧边栏菜单中直接使用
// 如果返回 null 代表这个路由没有对应的一级路由也就没有菜单
routeTopLevelMenu () {
return this.routeTopLevelName ? this.menu.find(e => e.name === this.routeTopLevelName).children : null
}
},
watch: {
routeName () {
this.refreshSideMenu()
}
},
mounted () {
this.refreshSideMenu()
},
methods: {
...mapMutations([
'setSideMenu'
]),
// 更新一次侧边栏
refreshSideMenu () {
if (this.routeTopLevelMenu) {
this.setSideMenu({
sideMenu: this.routeTopLevelMenu
})
}
},
// 跳转到某个路由
active (item) {
this.$router.push({
name: item.name
})
menus
}
}
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="btn-group">
<div class="dd-header-right">
<FullScreen></FullScreen>
<UserDropdown></UserDropdown>
</div>
@@ -7,6 +7,7 @@
<script>
export default {
name: 'HeaderRight',
components: {
FullScreen: () => import('./components/FullScreen.vue'),
UserDropdown: () => import('./components/UserDropdown.vue')

View File

@@ -1,49 +1,22 @@
<template>
<div>
<el-menu
v-if="sideMenu.filter(e => e.title).length > 0"
class="dd-side-menu"
:collapse="collapse"
:unique-opened="true">
<template v-for="(menu, index) in sideMenu">
<!-- 没有子菜单的菜单项 -->
<el-menu-item
v-if="!menu.children && menu.title"
:key="index"
:index="`${menu.title}${index}`"
@click.native="$router.push({name: menu.name})">
<i v-if="menu.icon" :class="'fa fa-' + menu.icon"></i>
<span slot="title">{{menu.title}}</span>
</el-menu-item>
<!-- 有子菜单的项目 -->
<el-submenu
v-if="menu.children"
:key="index"
:index="`${menu.title}${index}`">
<template slot="title">
<i v-if="menu.icon" :class="'fa fa-' + menu.icon"></i>
<span slot="title">{{menu.title}}</span>
</template>
<el-menu-item
v-for="(menuItem, menuItemIndex) in menu.children"
:key="menuItemIndex"
:index="`${menuItem.name}${menuItemIndex}`"
@click.native="$router.push({name: menuItem.name})">
<i v-if="menuItem.icon" :class="'fa fa-' + menuItem.icon"></i>
{{menuItem.title}}
</el-menu-item>
</el-submenu>
:unique-opened="true"
:router="true">
<template v-for="(menu, menuIndex) in menus">
<d2-menu-item v-if="menu.children === undefined" :menu="menu" :key="menuIndex"/>
<d2-submenu v-else :menu="menu" :key="menuIndex"/>
</template>
</el-menu>
<div v-if="sideMenu.filter(e => e.title).length === 0 && !collapse" class="dd-side-menu-empty">
<!-- <div v-if="sideMenu.filter(e => e.title).length === 0 && !collapse" class="dd-side-menu-empty">
没有菜单
</div>
</div> -->
</div>
</template>
<script>
import { mapState } from 'vuex'
import { menu, router } from '@/router/menu/index.js'
import { side } from '@/menu/index.js'
export default {
props: {
collapse: {
@@ -52,23 +25,23 @@ export default {
default: false
}
},
components: {
D2MenuItem: () => import('../D2MenuItem/index.vue'),
D2Submenu: () => import('../D2Submenu/index.vue')
},
data () {
return {
menu,
router
menus: []
}
},
computed: {
...mapState({
sideMenu: state => state.menu.sideMenu
})
watch: {
'$route.matched': {
handler (val) {
const path = val[0].path
this.menus = side.filter(menu => menu.path === path)
},
immediate: true
}
}
}
</script>
<style lang="scss">
.dd-side-menu:not(.el-menu--collapse) {
width: 200px;
min-height: 400px;
}
</style>

View File

@@ -1,23 +1,27 @@
<template>
<el-container class="layout-main" :class="theme">
<el-container class="layout-main theme">
<!-- 顶栏 -->
<el-header>
<div class="logo-group" :style="logoGroupStyle">
<div class="logo-group" :style="{width: collapse ? asideWidthCollapse : asideWidth}">
<img v-if="collapse" src="@/assets/image/logo/header-icon-only.png">
<img v-else src="@/assets/image/logo/header.png">
</div>
<div class="toggle-sidemenu-btn" @click="toggleAside">
<Icon name="bars"></Icon>
<div class="toggle-aside-btn" @click="collapse = !collapse">
<Icon name="bars"/>
</div>
<HeaderMenu></HeaderMenu>
<HeaderRight></HeaderRight>
<HeaderMenu/>
<HeaderRight/>
</el-header>
<!-- 下面 主体 -->
<el-container>
<el-aside :style="asideStyle">
<SideMenu :collapse="collapse"></SideMenu>
<!-- 主体 侧边栏 -->
<el-aside :style="{width: collapse ? asideWidthCollapse : asideWidth}">
<SideMenu :collapse="collapse"/>
</el-aside>
<!-- 主体 -->
<el-main>
<transition name="fade-transverse">
<router-view></router-view>
<router-view/>
</transition>
</el-main>
</el-container>
@@ -33,25 +37,11 @@ export default {
},
data () {
return {
theme: 'classic',
collapse: false
}
},
computed: {
logoGroupStyle () {
return {
width: `${this.collapse ? '65' : '200'}px`
}
},
asideStyle () {
return {
width: `${this.collapse ? '65' : '200'}px`
}
}
},
methods: {
toggleAside () {
this.collapse = !this.collapse
collapse: false,
// [侧边栏宽度] 正常状态
asideWidth: '200px',
// [侧边栏宽度] 折叠状态
asideWidthCollapse: '65px'
}
}
}
@@ -59,6 +49,5 @@ export default {
<style lang="scss">
// 主题
@import '~@/assets/style/theme/classic.scss';
@import '~@/assets/style/theme/star/index.scss';
</style>

View File

@@ -1,6 +1,5 @@
import Vue from 'vue'
Vue.component('CardFull', resolve => { require(['@/components/core/CardFull'], resolve) })
Vue.component('Container', resolve => { require(['@/components/core/Container'], resolve) })
Vue.component('CountUp', resolve => { require(['@/components/core/CountUp'], resolve) })
Vue.component('Highlight', resolve => { require(['@/components/core/Highlight'], resolve) })

View File

@@ -57,5 +57,8 @@ new Vue({
i18n,
router,
template: '<App/>',
components: { App }
components: { App },
mounted () {
document.body.className = 'theme-star'
}
})

142
src/menu/index.js Normal file
View File

@@ -0,0 +1,142 @@
// 路由菜单 插件
const demoPlugins = {
path: '/demo/plugins',
title: '插件',
icon: 'plug',
children: (pre => [
{ path: `${pre}index`, title: '插件首页' },
{
path: `${pre}mock`,
title: '模拟数据',
children: [
{ path: `${pre}mock/ajax`, title: '拦截异步请求' },
{ path: `${pre}mock/dpd`, title: 'DPD规则' },
{ path: `${pre}mock/dtd`, title: 'DTD规则' }
]
},
{
path: `${pre}import`,
title: '导入',
children: [
{ path: `${pre}import/csv`, title: 'csv' },
{ path: `${pre}import/xlsx`, title: 'xlsx' }
]
},
{
path: `${pre}export`,
title: '导出',
children: [
{ path: `${pre}export/table`, title: '表格' },
{ path: `${pre}export/txt`, title: '文本' }
]
},
{
path: `${pre}i18n`,
title: '多国语',
children: [
{ path: `${pre}i18n/demo1`, title: '示例1' },
{ path: `${pre}i18n/demo2`, title: '示例2' }
]
},
{ path: `${pre}build`, title: '环境区分' },
{ path: `${pre}clipboard-polyfill`, title: '剪贴板访问' },
{ path: `${pre}js-cookie`, title: 'cookie读写' },
{ path: `${pre}timeago`, title: '计算已经过去的时间' }
])('/demo/plugins/')
}
// 路由菜单 组件
const demoComponents = {
path: '/demo/components',
title: '组件',
icon: 'puzzle-piece',
children: (pre => [
{ path: `${pre}index`, title: '组件首页' },
{
path: `${pre}container`,
title: '布局容器',
children: [
{ path: `${pre}container/full`, title: '填满' },
{ path: `${pre}container/ghost`, title: '隐形' },
{ path: `${pre}container/normal`, title: '一般' }
]
},
{
path: `${pre}layout/grid`,
title: '高级布局',
children: [
{ path: `${pre}layout/grid`, title: '拖拽位置和大小' },
{ path: `${pre}layout/splitpane`, title: '区域划分' }
]
},
{
path: `${pre}icon`,
title: '图标',
children: [
{ path: `${pre}icon/icon`, title: '图标组件' },
{ path: `${pre}icon/list`, title: 'FontAwesome' },
{ path: `${pre}icon/select`, title: '图标选择器' },
{ path: `${pre}icon/svg`, title: 'SVG图标组件' }
]
},
{ path: `${pre}countup`, title: '数字动画' },
{ path: `${pre}editor-quill`, title: '富文本编辑器' },
{ path: `${pre}editor-simpleMDE`, title: 'markdown编辑器' },
{ path: `${pre}highlight`, title: '代码高亮显示' },
{ path: `${pre}markdown`, title: 'markdown解析' }
])('/demo/components/')
}
// 路由菜单 图表
const demoChart = {
path: '/demo/chart',
title: '图表',
icon: 'pie-chart',
children: (pre => [
{ path: `${pre}index`, title: '图表首页' },
{
path: `${pre}demo`,
title: '单图示例',
children: [
{ path: `${pre}demo/areaBase`, title: '区域折线图' },
{ path: `${pre}demo/barBase`, title: '条形图' },
{ path: `${pre}demo/columnBase`, title: '柱形图' },
{ path: `${pre}demo/lineBase`, title: '折线图' },
{ path: `${pre}demo/lineStep`, title: '阶梯折线图' },
{ path: `${pre}demo/nightingaleRoseBase`, title: '南丁格尔玫瑰图' },
{ path: `${pre}demo/PieBase`, title: '饼图' },
{ path: `${pre}demo/radarBase`, title: '雷达图' }
]
},
{ path: `${pre}all`, title: '示例' },
{ path: `${pre}dynamicSize`, title: '动态尺寸与可拖拽' }
])('/demo/chart/')
}
// 菜单 侧边栏
export const side = [
demoPlugins,
demoComponents,
demoChart
]
// 菜单 顶栏
export default [
{
path: '/index',
title: '首页'
},
{
path: '/demo',
title: '功能',
children: [
demoPlugins,
demoComponents,
demoChart
]
},
{
path: '/demo/business',
title: '示例业务界面'
}
]

View File

@@ -1,5 +1,5 @@
<template>
<Container type="card-full">
<Container type="full">
<PageIndexArticle
title="I AM D2ADMIN"
sub-title="追求简约美感 & 上手即用的后台管理系统模板">

View File

@@ -1,5 +1,5 @@
<template>
<Container type="card-full">
<Container type="full">
<PageIndexArticle
title="示例业务页面"
sub-title="如果用不到 建议删除相关代码">

View File

@@ -1,5 +1,5 @@
<template>
<Container type="card-full">
<Container type="full">
<template slot="header">
用户中心
</template>

View File

@@ -1,5 +1,5 @@
<template>
<Container type="card-full">
<Container type="full">
<PageIndexArticle
title="内置 G2 图表库"
sub-title="如果用不到 建议删除相关代码和依赖">

View File

@@ -1,5 +1,5 @@
<template>
<Container type="card-full">
<Container type="full">
<template slot="header">
我是插入到 header 中的内容
</template>

View File

@@ -1,6 +0,0 @@
<template>
<Container>
<template slot="header">页面容器组件 文档</template>
<Markdown url="/static/md/组件 - 页面容器.md"></Markdown>
</Container>
</template>

View File

@@ -1,5 +1,5 @@
<template>
<Container type="card-full">
<Container type="full">
<PageIndexArticle
title="内置组件"
sub-title="D2Admin 为你提供了一些上手即用的组件">

View File

@@ -1,9 +0,0 @@
<template>
<Container>
<PageHeader
slot="header"
title="文档">
</PageHeader>
<markdown url="/static/md/组件 - markdown - 文档.md"></markdown>
</Container>
</template>

View File

@@ -1,5 +1,5 @@
<template>
<Container type="card-full">
<Container type="full">
<template slot="header">
多环境发布
</template>

View File

@@ -1,5 +1,5 @@
<template>
<Container type="card-full">
<Container type="full">
<PageIndexArticle
title="插件演示"
sub-title="D2Admin 集成了许多实用插件">

View File

@@ -1,10 +0,0 @@
<template>
<Container>
<PageHeader
slot="header"
title="使用方法"
url="http://kazupon.github.io/vue-i18n/en/">
</PageHeader>
<Markdown url="/static/md/插件 - 多国语.md"></Markdown>
</Container>
</template>

View File

@@ -2,19 +2,113 @@ import Vue from 'vue'
import VueRouter from 'vue-router'
import Cookies from 'js-cookie'
// 在菜单中显示的那部分路由
import * as menu from '@/router/menu/index.js'
// 不在菜单中显示的那部分路由
import invisible from './invisible/index.js'
Vue.use(VueRouter)
let router = new VueRouter({
routes: [
...menu.router,
...invisible
]
})
const routes = [
// 首页
{
path: '/',
redirect: { name: 'index' },
component: () => import('@/components/core/MainLayout/index.vue'),
children: [
{
path: 'index',
name: 'index',
meta: { requiresAuth: true },
component: () => import('@/pages/core/index/index.vue')
}
]
},
{
path: '/demo/components',
name: 'demo-components',
meta: { requiresAuth: true },
redirect: { name: 'demo-components-index' },
component: () => import('@/components/core/MainLayout/index.vue'),
children: (pre => [
{ path: 'container/full', name: `${pre}container-full`, component: () => import('@/pages/demo/components/container/full.vue') },
{ path: 'container/ghost', name: `${pre}container-ghost`, component: () => import('@/pages/demo/components/container/ghost.vue') },
{ path: 'container/normal', name: `${pre}container-normal`, component: () => import('@/pages/demo/components/container/normal.vue') },
{ path: 'countup', name: `${pre}countup`, component: () => import('@/pages/demo/components/countup/index.vue') },
{ path: 'editor-quill', name: `${pre}editor-quill`, component: () => import('@/pages/demo/components/editor-quill/index.vue') },
{ path: 'editor-simpleMDE', name: `${pre}editor-simpleMDE`, component: () => import('@/pages/demo/components/editor-simpleMDE/index.vue') },
{ path: 'highlight', name: `${pre}highlight`, component: () => import('@/pages/demo/components/highlight/index.vue') },
{ path: 'icon/icon', name: `${pre}icon-icon`, component: () => import('@/pages/demo/components/icon/icon.vue') },
{ path: 'icon/list', name: `${pre}icon-list`, component: () => import('@/pages/demo/components/icon/list.vue') },
{ path: 'icon/select', name: `${pre}icon-select`, component: () => import('@/pages/demo/components/icon/select.vue') },
{ path: 'icon/svg', name: `${pre}icon-svg`, component: () => import('@/pages/demo/components/icon/svg.vue') },
{ path: 'index', name: `${pre}index`, component: () => import('@/pages/demo/components/index/index.vue') },
{ path: 'layout/grid', name: `${pre}layout-grid`, component: () => import('@/pages/demo/components/layout/grid.vue') },
{ path: 'layout/splitpane', name: `${pre}layout-splitpane`, component: () => import('@/pages/demo/components/layout/splitpane.vue') },
{ path: 'markdown', name: `${pre}markdown`, component: () => import('@/pages/demo/components/markdown/index.vue') }
])('demo-components-')
},
{
path: '/demo/plugins',
name: 'demo-plugins',
meta: { requiresAuth: true },
redirect: { name: 'demo-plugins-index' },
component: () => import('@/components/core/MainLayout/index.vue'),
children: (pre => [
{ path: 'build', name: `${pre}build`, component: () => import('@/pages/demo/plugins/build/index.vue') },
{ path: 'clipboard-polyfill', name: `${pre}clipboard-polyfill`, component: () => import('@/pages/demo/plugins/clipboard-polyfill/index.vue') },
{ path: 'export/table', name: `${pre}export-table`, component: () => import('@/pages/demo/plugins/export/table.vue') },
{ path: 'export/txt', name: `${pre}export-txt`, component: () => import('@/pages/demo/plugins/export/txt.vue') },
{ path: 'i18n/demo1', name: `${pre}i18n-demo1`, component: () => import('@/pages/demo/plugins/i18n/demo1.vue') },
{ path: 'i18n/demo2', name: `${pre}i18n-demo2`, component: () => import('@/pages/demo/plugins/i18n/demo2.vue') },
{ path: 'import/csv', name: `${pre}import-csv`, component: () => import('@/pages/demo/plugins/import/csv.vue') },
{ path: 'import/xlsx', name: `${pre}import-xlsx`, component: () => import('@/pages/demo/plugins/import/xlsx.vue') },
{ path: 'index', name: `${pre}index`, component: () => import('@/pages/demo/plugins/index/index.vue') },
{ path: 'js-cookie', name: `${pre}js-cookie`, component: () => import('@/pages/demo/plugins/js-cookie/index.vue') },
{ path: 'mock/ajax', name: `${pre}mock-ajax`, component: () => import('@/pages/demo/plugins/mock/ajax.vue') },
{ path: 'mock/dpd', name: `${pre}mock-dpd`, component: () => import('@/pages/demo/plugins/mock/dpd.vue') },
{ path: 'mock/dtd', name: `${pre}mock-dtd`, component: () => import('@/pages/demo/plugins/mock/dtd.vue') },
{ path: 'timeago', name: `${pre}timeago`, component: () => import('@/pages/demo/plugins/timeago/index.vue') }
])('demo-plugins-')
},
{
path: '/demo/chart',
name: 'demo-chart',
meta: { requiresAuth: true },
redirect: { name: 'demo-chart-index' },
component: () => import('@/components/core/MainLayout/index.vue'),
children: (pre => [
{ path: 'all', name: `${pre}all`, component: () => import('@/pages/demo/chart/all/index.vue') },
{ path: 'demo/areaBase', name: `${pre}demo-areaBase`, component: () => import('@/pages/demo/chart/demo/areaBase.vue') },
{ path: 'demo/barBase', name: `${pre}demo-barBase`, component: () => import('@/pages/demo/chart/demo/barBase.vue') },
{ path: 'demo/columnBase', name: `${pre}demo-columnBase`, component: () => import('@/pages/demo/chart/demo/columnBase.vue') },
{ path: 'demo/lineBase', name: `${pre}demo-lineBase`, component: () => import('@/pages/demo/chart/demo/lineBase.vue') },
{ path: 'demo/lineStep', name: `${pre}demo-lineStep`, component: () => import('@/pages/demo/chart/demo/lineStep.vue') },
{ path: 'demo/nightingaleRoseBase', name: `${pre}demo-nightingaleRoseBase`, component: () => import('@/pages/demo/chart/demo/nightingaleRoseBase.vue') },
{ path: 'demo/PieBase', name: `${pre}demo-PieBase`, component: () => import('@/pages/demo/chart/demo/PieBase.vue') },
{ path: 'demo/radarBase', name: `${pre}demo-radarBase`, component: () => import('@/pages/demo/chart/demo/radarBase.vue') },
{ path: 'dynamicSize', name: `${pre}dynamicSize`, component: () => import('@/pages/demo/chart/dynamicSize/index.vue') },
{ path: 'index', name: `${pre}index`, component: () => import('@/pages/demo/chart/index/index.vue') }
])('demo-chart-')
},
{
path: '/demo/business',
name: 'demo-business',
meta: { requiresAuth: true },
redirect: { name: 'demo-business-index' },
component: () => import('@/components/core/MainLayout/index.vue'),
children: (pre => [
{ path: 'index', name: `${pre}index`, component: () => import('@/pages/demo/business/index/index.vue') }
])('demo-business-')
},
// 登陆
{
path: '/login',
name: 'login',
component: () => import('@/pages/core/login/index.vue')
}
]
// routes[3].children.forEach(e => {
// console.log(`{ path: \`\${pre}${e.path}\`, title: 'title' }`)
// })
let router = new VueRouter({ routes })
router.beforeEach((to, from, next) => {
// 需要身份校验

View File

@@ -58,12 +58,12 @@ export const menu = {
{
title: '撑满',
icon: 'file-o',
path: 'container/card-full',
name: 'demo-components-container-card-full',
path: 'container/full',
name: 'demo-components-container-full',
meta: {
requiresAuth: true
},
component: resolve => { require(['@/pages/demo/components/container/card-full.vue'], resolve) }
component: resolve => { require(['@/pages/demo/components/container/full.vue'], resolve) }
}
]
},

View File

@@ -3,12 +3,14 @@ import Vuex from 'vuex'
import menu from './modules/menu'
import fullScreen from './modules/fullScreen'
import theme from './modules/theme'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
menu,
fullScreen
fullScreen,
theme
}
})

View File

@@ -0,0 +1,11 @@
export default {
state: {
themeName: 'star'
},
mutations: {
// 设置主题
setTheme (state, themeName) {
state.themeName = themeName
}
}
}

View File

@@ -0,0 +1 @@
5d5d5cae76bf221fe4df90af31aba1800c621bc3