提交 4ea5e8df authored 作者: 蔡芬's avatar 蔡芬

新增左侧菜单

上级 1579e677
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
"axios": "^0.24.0", "axios": "^0.24.0",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"element-ui": "^2.15.6", "element-ui": "^2.15.6",
"js-cookie": "^3.0.1",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-i18n": "^8.26.7", "vue-i18n": "^8.26.7",
"vue-router": "^3.2.0", "vue-router": "^3.2.0",
......
@import './mixin.less';
@import './transition.less';
@import './sidebar.less';
ul { ul {
list-style:none; list-style:none;
margin: 0; margin: 0;
......
@mixin clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
@mixin scrollBar {
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}
@mixin relative {
position: relative;
width: 100%;
height: 100%;
}
#app {
// 主体区域
.main-container {
min-height: 100%;
transition: margin-left .28s;
margin-left: 180px;
}
// 侧边栏
.sidebar-container {
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
}
transition: width .28s;
width: 180px !important;
height: 100%;
position: fixed;
font-size: 0px;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
a {
display: inline-block;
width: 100%;
}
.svg-icon {
margin-right: 16px;
}
.el-menu {
border: none;
width: 100% !important;
}
}
.hideSidebar {
.sidebar-container {
// width: 36px !important;
width: 0 !important;
}
.main-container {
// margin-left: 36px;
margin-left: 0;
}
.submenu-title-noDropdown {
padding-left: 10px !important;
position: relative;
.el-tooltip {
padding: 0 10px !important;
}
}
.el-submenu {
&>.el-submenu__title {
padding-left: 10px !important;
&>span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
.el-submenu__icon-arrow {
display: none;
}
}
}
}
.sidebar-container .nest-menu .el-submenu>.el-submenu__title,
.sidebar-container .el-submenu .el-menu-item {
min-width: 180px !important;
background-color: #1f2d3d !important;
&:hover {
background-color: #001528 !important;
}
}
.el-menu--collapse .el-menu .el-submenu {
min-width: 180px !important;
}
//适配移动端
// .mobile {
// .main-container {
// margin-left: 0px;
// }
// .sidebar-container {
// top: 50px;
// transition: transform .28s;
// width: 180px !important;
// }
// &.hideSidebar {
// .sidebar-container {
// transition-duration: 0.3s;
// transform: translate3d(-180px, 0, 0);
// }
// }
// }
.withoutAnimation {
.main-container,
.sidebar-container {
transition: none;
}
}
}
//globl transition css
/*fade*/
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.28s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
/*fade*/
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all .5s;
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-move {
transition: all .5s;
}
.breadcrumb-leave-active {
position: absolute;
}
import Vue from 'vue'; import Vue from 'vue';
import Config from '../config.js'; import Config from '../config.js';
import {post,get,patch,put,del} from './index'; import {post,get,patch,put,del} from '../utils/request';
Vue.prototype.$post=post; Vue.prototype.$post=post;
Vue.prototype.$get=get; Vue.prototype.$get=get;
Vue.prototype.$patch=patch; Vue.prototype.$patch=patch;
......
import * as request from '@/libs/utils/request'
export function login(username, password) {
return request.post({
url: '/user/login',
data: {
username,
password
}
})
}
export function getInfo(token) {
return request.get({
url: '/user/info',
params: { token }
})
}
export function logout() {
return request.post({
url: '/user/logout'
})
}
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
...@@ -2,7 +2,7 @@ import * as Vue from 'vue' ...@@ -2,7 +2,7 @@ import * as Vue from 'vue'
import axios from 'axios'; import axios from 'axios';
import Config from '../config.js'; import Config from '../config.js';
import { Message } from 'element-ui';// 我用的mint的框架来弹出我的错误返回 大家可以用别的提示 import { Message } from 'element-ui';
// import router from '../router' // import router from '../router'
// Message('这是一条消息提示'); // Message('这是一条消息提示');
...@@ -83,7 +83,7 @@ axios.interceptors.response.use( ...@@ -83,7 +83,7 @@ axios.interceptors.response.use(
* @param params * @param params
* @returns {Promise} * @returns {Promise}
*/ */
export function get(url,params={}){ export function get({url,params={}}){
return new Promise((resolve,reject) => { return new Promise((resolve,reject) => {
axios.get(url,{ axios.get(url,{
params:params params:params
...@@ -114,7 +114,7 @@ axios.interceptors.response.use( ...@@ -114,7 +114,7 @@ axios.interceptors.response.use(
* @param data * @param data
* @returns {Promise} * @returns {Promise}
*/ */
export function post(url,data = {}){ export function post({url,data = {}}){
return new Promise((resolve,reject) => { return new Promise((resolve,reject) => {
axios.post(url,data) axios.post(url,data)
.then(response => { .then(response => {
...@@ -140,7 +140,7 @@ export function post(url,data = {}){ ...@@ -140,7 +140,7 @@ export function post(url,data = {}){
* @param data * @param data
* @returns {Promise} * @returns {Promise}
*/ */
export function patch(url,data = {}){ export function patch({url,data = {}}){
return new Promise((resolve,reject) => { return new Promise((resolve,reject) => {
axios.patch(url,data) axios.patch(url,data)
.then(response => { .then(response => {
...@@ -165,7 +165,7 @@ export function patch(url,data = {}){ ...@@ -165,7 +165,7 @@ export function patch(url,data = {}){
* @param data * @param data
* @returns {Promise} * @returns {Promise}
*/ */
export function put(url,data = {}){ export function put({url,data = {}}){
return new Promise((resolve,reject) => { return new Promise((resolve,reject) => {
axios.put(url,data) axios.put(url,data)
.then(response => { .then(response => {
...@@ -184,7 +184,7 @@ export function put(url,data = {}){ ...@@ -184,7 +184,7 @@ export function put(url,data = {}){
}) })
} }
export function del(url,data = {}){ export function del({url,data = {}}){
return new Promise((resolve,reject) => { return new Promise((resolve,reject) => {
axios.delete(url,data) axios.delete(url,data)
.then(response => { .then(response => {
......
<template> <template>
<div id="app" ref="appcontainer" :style="style"> <div id="app" ref="appcontainer" :style="style" >
<div id="app-bar" v-if="!is_web"> <div id="app-bar" v-if="!is_web">
<div id="app-title"> <div id="app-title">
飞稻设计平台 飞稻设计平台
...@@ -28,16 +28,13 @@ ...@@ -28,16 +28,13 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <div id="nav"> <router-view></router-view>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div> -->
<router-view />
</div> </div>
</template> </template>
<script> <script>
const isWeb = process.env.VUE_APP_IS_WEB; const isWeb = process.env.VUE_APP_IS_WEB;
const { remote } = !isWeb ? window.require('electron') : {}; const { remote } = !isWeb ? window.require('electron') : {};
import { mapState } from 'vuex';
export default { export default {
data() { data() {
return { return {
...@@ -48,12 +45,15 @@ export default { ...@@ -48,12 +45,15 @@ export default {
}; };
}, },
computed: { computed: {
...mapState({
isLoading: state => state.app.loading,
}),
is_web() { is_web() {
return process.env.VUE_APP_IS_WEB; return process.env.VUE_APP_IS_WEB;
} }
}, },
mounted() { mounted() {
console.log(process,process.env.VUE_APP_IS_WEB,isWeb,this.is_web) console.log(process,process.env.VUE_APP_IS_WEB,isWeb,this.is_web, this.$router)
this.style.height = document.documentElement.clientHeight + 'px'; this.style.height = document.documentElement.clientHeight + 'px';
window.onresize = () => { window.onresize = () => {
this.style.height = document.documentElement.clientHeight + 'px'; this.style.height = document.documentElement.clientHeight + 'px';
...@@ -72,10 +72,14 @@ export default { ...@@ -72,10 +72,14 @@ export default {
@import '../assets/css/normalize.css'; /*引入样式*/ @import '../assets/css/normalize.css'; /*引入样式*/
</style> </style>
<style lang="less"> <style lang="less">
@import '../assets/css/common.less'; /*引入公共样式*/
#app-bar { #app-bar {
background-color: #263238; background-color: #e7eaed;
color: #B2CCD6; color: #57646c;
display: flex; display: flex;
align-items: center;
justify-content: space-between;
// 如果你想让某区域可以拖动,将其设为drag // 如果你想让某区域可以拖动,将其设为drag
// 但是设为drag的区域无法被点击,请注意 // 但是设为drag的区域无法被点击,请注意
// 可以设置为no-drag来恢复点击 // 可以设置为no-drag来恢复点击
...@@ -83,7 +87,7 @@ export default { ...@@ -83,7 +87,7 @@ export default {
// But the area set as drag cannot be clicked, please note // But the area set as drag cannot be clicked, please note
// can be set to no-drag to resume clicks // can be set to no-drag to resume clicks
-webkit-app-region: drag; -webkit-app-region: drag;
height: 32px; height: 40px;
} }
#app-action { #app-action {
padding: 0; padding: 0;
...@@ -95,21 +99,14 @@ export default { ...@@ -95,21 +99,14 @@ export default {
.app-action-button { .app-action-button {
padding: 0 8px; padding: 0 8px;
color: #FFF;
transition: .2s ease; transition: .2s ease;
opacity: 0.7; opacity: 0.7;
line-height: 32px; line-height: 40px;
} }
.app-action-button:hover { .app-action-button:hover {
background-color: #436677; background-color: #cfd2d5;
opacity: 1; opacity: 1;
} }
.app-action-button:active {
background-color: #3A4A52;
}
@import '../assets/css/common.less'; /*引入公共样式*/
</style> </style>
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path" v-if="item.meta.title">
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{item.meta.title}}</span>
<router-link v-else :to="item.redirect||item.path">{{item.meta.title}}</router-link>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
export default {
created() {
this.getBreadcrumb()
},
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
methods: {
getBreadcrumb() {
let matched = this.$route.matched.filter(item => item.name)
const first = matched[0]
// if (first && first.name !== 'dashboard') {
// matched = [{ path: '/dashboard', meta: { title: 'Dashboard' }}].concat(matched)
// }
this.levelList = matched
}
}
}
</script>
<style lang="less" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 10px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>
<template>
<div>
<svg t="1492500959545" @click="toggleClick" class="hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
<path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
p-id="1692"></path>
<path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
p-id="1693"></path>
<path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
p-id="1694"></path>
</svg>
</div>
</template>
<script>
export default {
name: 'hamburger',
props: {
isActive: {
type: Boolean,
default: false
},
toggleClick: {
type: Function,
default: null
}
}
}
</script>
<style scoped>
.hamburger {
display: inline-block;
cursor: pointer;
width: 20px;
height: 20px;
transform: rotate(90deg);
transition: .38s;
transform-origin: 50% 50%;
}
.hamburger.is-active {
transform: rotate(0deg);
}
</style>
<template>
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll" >
<div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
<slot></slot>
</div>
</div>
</template>
<script>
const delta = 15
export default {
name: 'scrollBar',
data() {
return {
top: 0
}
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 3
const $container = this.$refs.scrollContainer
const $containerHeight = $container.offsetHeight
const $wrapper = this.$refs.scrollWrapper
const $wrapperHeight = $wrapper.offsetHeight
if (eventDelta > 0) {
this.top = Math.min(0, this.top + eventDelta)
} else {
if ($containerHeight - delta < $wrapperHeight) {
if (this.top < -($wrapperHeight - $containerHeight + delta)) {
this.top = this.top
} else {
this.top = Math.max(this.top + eventDelta, $containerHeight - $wrapperHeight - delta)
}
} else {
this.top = 0
}
}
}
}
}
</script>
<style lang="less" scoped>
.scroll-container {
position: relative;
width: 100%;
height: 100%;
background-color: #304156;
.scroll-wrapper {
position: absolute;
width: 100%!important;
}
}
</style>
...@@ -4,7 +4,7 @@ import router from "./router"; ...@@ -4,7 +4,7 @@ import router from "./router";
import store from "./store"; import store from "./store";
import VueI18n from 'vue-i18n'; import VueI18n from 'vue-i18n';
import { Button, Drawer, Message, MessageBox, Row, Col, Form, import { Button, Drawer, Message, MessageBox, Row, Col, Form,
FormItem, Input, Menu, MenuItem, Submenu, Dropdown, DropdownItem, DropdownMenu, Select, Option, Tabs, TabPane, Tree, Upload, Dialog } from 'element-ui'; FormItem, Input, Menu, MenuItem, Submenu, Dropdown, DropdownItem, DropdownMenu, Select, Option, Tabs, TabPane, Tree, Upload, Dialog, Breadcrumb, BreadcrumbItem,Loading } from 'element-ui';
Vue.config.productionTip = false; Vue.config.productionTip = false;
...@@ -32,8 +32,12 @@ Vue.use(TabPane); ...@@ -32,8 +32,12 @@ Vue.use(TabPane);
Vue.use(Tree); Vue.use(Tree);
Vue.use(Upload); Vue.use(Upload);
Vue.use(Dialog); Vue.use(Dialog);
Vue.use(Breadcrumb);
Vue.use(BreadcrumbItem);
Vue.use(Loading.directive);
Vue.prototype.$loading = Loading.service;
Vue.prototype.$confirm = MessageBox.confirm; Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$message = Message; Vue.prototype.$message = Message;
......
...@@ -5,6 +5,7 @@ import routes from './routes'; ...@@ -5,6 +5,7 @@ import routes from './routes';
Vue.use(VueRouter); Vue.use(VueRouter);
const router = new VueRouter({ const router = new VueRouter({
// scrollBehavior: () => ({ y: 0 }),
routes, routes,
}); });
......
import Home from "../views/Home.vue";
import Login from "../views/Account/Login.vue"; import Login from "../views/Account/Login.vue";
import Layout from '../views/layout/Layout'
/**
* hidden: true if `hidden:true` will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu, whatever its child routes length
* if not set alwaysShow, only more than one route under the children
* it will becomes nested mode, otherwise not show the root menu
* redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
title: 'title' the name show in submenu and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar,
}
**/
export default [ export default [
{ path: '/404', component: () => import('../views/404.vue'), hidden: true },
{ {
path: "/", path: "/",
name: "Home", name: "Home",
component: Home, component: Layout,
}, redirect: '/home',
{ hidden: false,
path: "/about", children: [{
name: "About", path: 'home',
// route level code-splitting name: "Home",
// this generates a separate chunk (about.[hash].js) for this route meta: { title: 'Home', icon: 'example' },
// which is lazy-loaded when the route is visited. component: () => import('../views/Home.vue')
component: () => }]
import(/* webpackChunkName: "about" */ "../views/About.vue"),
}, },
{ {
path: "/login", path: "/login",
name: "Login", name: "Login",
component: Login, component: Login,
hidden: false,
meta: { title: 'Login', icon: 'example' },
// beforeEnter: (to, from, next) => { //路由独享的守卫 // beforeEnter: (to, from, next) => { //路由独享的守卫
// next(); // next();
// } // }
...@@ -27,13 +43,27 @@ export default [ ...@@ -27,13 +43,27 @@ export default [
{ {
path: "/course", path: "/course",
name: "Course", name: "Course",
component: () => component: Layout,
import(/* webpackChunkName: "about" */ "../views/Course/index.vue"), redirect: '/course/create',
}, meta: { title: 'Course', icon: 'example' },
{ hidden: false,
path: "/course/:type", children: [{
name: "CourseCreate", path: 'list',
component: () => name: "list",
import(/* webpackChunkName: "about" */ "../views/Course/create.vue"), meta: { title: 'list', icon: 'example' },
component: () => import('../views/Course/index.vue')
},{
path: ':type',
name: "编辑",
meta: { title: '编辑', icon: 'example' },
component: () => import('../views/Course/create.vue')
}]
}, },
// {
// path: "/course/:type",
// name: "CourseCreate",
// component: () =>
// import(/* webpackChunkName: "about" */ "../views/Course/create.vue"),
// },
{ path: '*', redirect: '/404', hidden: true }
] ]
\ No newline at end of file
const state = {
main: 0,
};
const mutations = {
DECREMENT_MAIN_COUNTER(state) {
state.main -= 1;
},
INCREMENT_MAIN_COUNTER(state) {
state.main += 1;
},
};
const actions = {
someAsyncTask({ commit }) {
// do something async
commit('INCREMENT_MAIN_COUNTER');
},
};
export default {
state,
mutations,
actions,
};
\ No newline at end of file
import Cookies from 'js-cookie'
const app = {
state: {
sidebar: {
opened: !+Cookies.get('sidebarStatus'),
withoutAnimation: false
},
device: 'desktop',
loading: false,
},
mutations: {
TOGGLE_SIDEBAR: state => {
if (state.sidebar.opened) {
Cookies.set('sidebarStatus', 1)
} else {
Cookies.set('sidebarStatus', 0)
}
state.sidebar.opened = !state.sidebar.opened
},
CLOSE_SIDEBAR: (state, withoutAnimation) => {
Cookies.set('sidebarStatus', 1)
state.sidebar.opened = false
state.sidebar.withoutAnimation = withoutAnimation
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
},
toggle_refresh: (state, status) => {
state.loading = status
}
},
actions: {
ToggleSideBar: ({ commit }) => {
commit('TOGGLE_SIDEBAR')
},
CloseSideBar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
},
ToggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
}
}
}
export default app
import { login, logout, getInfo } from '@/libs/api/login'
import { getToken, setToken, removeToken } from '@/libs/utils/auth'
const user = {
state: {
token: getToken(),
name: '',
avatar: '',
roles: []
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
},
actions: {
// 登录
Login({ commit }, userInfo) {
const username = userInfo.username.trim()
return new Promise((resolve, reject) => {
login(username, userInfo.password).then(response => {
const data = response.data
setToken(data.token)
commit('SET_TOKEN', data.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const data = response.data
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
commit('SET_ROLES', data.roles)
} else {
reject('getInfo: roles must be a non-null array !')
}
commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar)
resolve(response)
}).catch(error => {
reject(error)
})
})
},
// 登出
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
}).catch(error => {
reject(error)
})
})
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
removeToken()
commit('SET_TOKEN', '')
resolve()
})
}
}
}
export default user
<template>
<div style="background:#f0f2f5;margin-top: -20px;">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" :src="img_404" alt="404">
<img class="pic-404__child left" :src="img_404_cloud" alt="404">
<img class="pic-404__child mid" :src="img_404_cloud" alt="404">
<img class="pic-404__child right" :src="img_404_cloud" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">版权所有<a class="link-type" href="https://wallstreetcn.com" target='_blank'>华尔街见闻</a></div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告</div>
<a href="/" class="bullshit__return-home">返回首页</a>
</div>
</div>
</div>
</template>
<script>
import img_404 from '@/assets/404_images/404.png'
import img_404_cloud from '@/assets/404_images/404_cloud.png'
export default {
data() {
return {
img_404,
img_404_cloud
}
},
computed: {
message() {
return '特朗普说这个页面你不能进......'
}
}
}
</script>
<style lang="less" scoped>
.wscn-http404 {
position: relative;
width: 1200px;
margin: 20px auto 60px;
padding: 0 100px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
padding: 150px 0;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 150px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #1482f0;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
</template> </template>
<script> <script>
import { loginApi } from '@/libs/request/api.js'; // import { loginApi } from '@/libs/request/api.js';
export default { export default {
data() { data() {
var validateUserName = (rule, value, callback) => { var validateUserName = (rule, value, callback) => {
......
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
<script> <script>
// @ is an alias to /src // @ is an alias to /src
import { loginApi } from "@/libs/request/api.js"; // import { loginApi } from "@/libs/request/api.js";
import LiItem from "@/renderer/components/Home/li-item.vue"; import LiItem from "@/renderer/components/Home/li-item.vue";
import ToolItem from "@/renderer/components/Home/tool-item.vue"; import ToolItem from "@/renderer/components/Home/tool-item.vue";
......
<template>
<div class="app-wrapper" :class="classObj">
<sidebar class="sidebar-container" :style="siderbarStyle"></sidebar>
<div class="main-container">
<navbar></navbar>
<app-main></app-main>
</div>
</div>
</template>
<script>
import { Navbar, Sidebar, AppMain } from './components'
import ResizeMixin from './mixin/ResizeHandler'
export default {
name: 'layout',
components: {
Navbar,
Sidebar,
AppMain
},
mixins: [ResizeMixin],
computed: {
sidebar() {
return this.$store.state.app.sidebar
},
device() {
return this.$store.state.app.device
},
siderbarStyle() {
return {
top: process.env.VUE_APP_IS_WEB == 'true' ? 0 : '40px'
}
},
classObj() {
return {
hideSidebar: !this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile'
}
}
}
}
</script>
<style lang="less" scoped>
@import "../../../assets/css/mixin.less";
.app-wrapper {
// @include clearfix;
position: relative;
height: 100%;
width: 100%;
}
</style>
<template>
<section class="app-main">
<transition name="fade" mode="out-in">
<!-- <router-view :key="key"></router-view> -->
<router-view></router-view>
</transition>
</section>
</template>
<script>
export default {
name: 'AppMain',
computed: {
// key() {
// return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
// }
}
}
</script>
<template>
<el-menu class="navbar" mode="horizontal">
<hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
<breadcrumb></breadcrumb>
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<img class="user-avatar" :src="avatar+'?imageView2/1/w/80/h/80'">
<i class="el-icon-caret-bottom"></i>
</div>
<el-dropdown-menu class="user-dropdown" slot="dropdown">
<router-link class="inlineBlock" to="/">
<el-dropdown-item>
Home
</el-dropdown-item>
</router-link>
<el-dropdown-item divided>
<span @click="logout" style="display:block;">LogOut</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-menu>
</template>
<script>
import { mapState } from 'vuex'
import Breadcrumb from '@/renderer/components/Breadcrumb'
import Hamburger from '@/renderer/components/Hamburger'
export default {
components: {
Breadcrumb,
Hamburger
},
computed: {
...mapState({
sidebar: state => state.app.sidebar,
avatar: state => state.user.avatar,
}),
},
methods: {
toggleSideBar() {
this.$store.dispatch('ToggleSideBar')
},
logout() {
this.$store.dispatch('LogOut').then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug
})
}
}
}
</script>
<style lang="less" scoped>
.navbar {
height: 50px;
line-height: 50px;
border-radius: 0px !important;
.hamburger-container {
line-height: 58px;
height: 50px;
float: left;
padding: 0 10px;
}
.screenfull {
position: absolute;
right: 90px;
top: 16px;
color: red;
}
.avatar-container {
height: 50px;
display: inline-block;
position: absolute;
right: 35px;
.avatar-wrapper {
cursor: pointer;
margin-top: 5px;
position: relative;
.user-avatar {
width: 40px;
height: 40px;
border-radius: 10px;
}
.el-icon-caret-bottom {
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
}
</style>
<template>
<div class="menu-wrapper">
<template v-for="item in routes" v-if="!item.hidden&&item.children">
<router-link v-if="hasOneShowingChildren(item.children) && !item.children[0].children&&!item.alwaysShow" :to="item.path+'/'+item.children[0].path"
:key="item.children[0].name">
<el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
<!-- <svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon> -->
<span v-if="item.children[0].meta&&item.children[0].meta.title" slot="title">{{item.children[0].meta.title}}</span>
</el-menu-item>
</router-link>
<el-submenu v-else :index="item.name||item.path" :key="item.name">
<template slot="title">
<!-- <svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon> -->
<span v-if="item.meta&&item.meta.title" slot="title">{{item.meta.title}}</span>
</template>
<template v-for="child in item.children" v-if="!child.hidden">
<sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
<el-menu-item :index="item.path+'/'+child.path">
<!-- <svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon> -->
<span v-if="child.meta&&child.meta.title" slot="title">{{child.meta.title}}</span>
</el-menu-item>
</router-link>
</template>
</el-submenu>
</template>
</div>
</template>
<script>
export default {
name: 'SidebarItem',
props: {
routes: {
type: Array
},
isNest: {
type: Boolean,
default: false
}
},
methods: {
hasOneShowingChildren(children) {
const showingChildren = children.filter(item => {
return !item.hidden
})
if (showingChildren.length === 1) {
return true
}
return false
}
}
}
</script>
<template>
<scroll-bar>
<el-menu
mode="vertical"
:show-timeout="200"
:default-active="$route.path"
:collapse="isCollapse"
background-color="#304156"
text-color="#bfcbd9"
active-text-color="#409EFF"
>
<sidebar-item :routes="routes"></sidebar-item>
</el-menu>
</scroll-bar>
</template>
<script>
import { mapGetters, mapState } from 'vuex'
import SidebarItem from './SidebarItem'
import ScrollBar from '@/renderer/components/ScrollBar'
export default {
components: { SidebarItem, ScrollBar },
computed: {
...mapState({
sidebar: state => state.app.sidebar,
}),
routes() {
return this.$router.options.routes
},
isCollapse() {
return !this.sidebar.opened
}
}
}
</script>
export { default as Navbar } from './Navbar'
export { default as Sidebar } from './Sidebar'
export { default as AppMain } from './AppMain'
import store from '@/renderer/store'
const { body } = document
const WIDTH = 1024
const RATIO = 3
export default {
watch: {
$route(route) {
if (this.device === 'mobile' && this.sidebar.opened) {
store.dispatch('CloseSideBar', { withoutAnimation: false })
}
}
},
beforeMount() {
window.addEventListener('resize', this.resizeHandler)
},
mounted() {
const isMobile = this.isMobile()
if (isMobile) {
store.dispatch('ToggleDevice', 'mobile')
store.dispatch('CloseSideBar', { withoutAnimation: true })
}
},
methods: {
isMobile() {
const rect = body.getBoundingClientRect()
return rect.width - RATIO < WIDTH
},
resizeHandler() {
if (!document.hidden) {
const isMobile = this.isMobile()
store.dispatch('ToggleDevice', isMobile ? 'mobile' : 'desktop')
if (isMobile) {
store.dispatch('CloseSideBar', { withoutAnimation: true })
}
}
}
}
}
...@@ -6988,6 +6988,11 @@ js-beautify@^1.6.12: ...@@ -6988,6 +6988,11 @@ js-beautify@^1.6.12:
glob "^7.1.3" glob "^7.1.3"
nopt "^5.0.0" nopt "^5.0.0"
js-cookie@^3.0.1:
version "3.0.1"
resolved "https://registry.nlark.com/js-cookie/download/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414"
integrity sha1-njm0xsL1ZWNwjX0x9vXyGHOpJBQ=
js-message@1.0.7: js-message@1.0.7:
version "1.0.7" version "1.0.7"
resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.7.tgz#fbddd053c7a47021871bb8b2c95397cc17c20e47" resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.7.tgz#fbddd053c7a47021871bb8b2c95397cc17c20e47"
......
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论