张恒

暂时加入假登录页,404页面,还未处理数据,将路由分离,其他更改见readme

......@@ -5,6 +5,7 @@ process.env.BABEL_ENV = 'renderer'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const config = require('../config')
const BabiliWebpackPlugin = require('babili-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
......@@ -134,6 +135,9 @@ let rendererConfig = {
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({ filename: 'styles.css' }),
new webpack.DefinePlugin({
'process.env': process.env.NODE_ENV === 'production' ? config.build.env : config.dev.env
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: resolve('src/index.ejs'),
......
module.exports = {
NODE_ENV: '"development"',
BASE_API: '""'
BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"'
}
......
......@@ -58,6 +58,7 @@
"element-ui": "^2.11.1",
"js-cookie": "^2.2.0",
"nedb": "^1.8.0",
"nprogress": "^0.2.0",
"vue": "^2.6.10",
"vue-electron": "^1.0.6",
"vue-router": "^3.0.7",
......
import request from '@/utils/request'
export function login (username, password) {
return request({
url: '/user/login',
method: 'post',
data: {
username,
password
}
})
}
export function getInfo (token) {
return request({
url: '/user/info',
method: 'get',
params: { token }
})
}
export function logout () {
return request({
url: '/user/logout',
method: 'post'
})
}
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1564902100209" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="840" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300"><defs><style type="text/css"></style></defs><path d="M615.6 123.6h165.5L512 589.7 242.9 123.6H63.5L512 900.4l448.5-776.9z" fill="#41B883" p-id="841"></path><path d="M781.1 123.6H615.6L512 303 408.4 123.6H242.9L512 589.7z" fill="#34495E" p-id="842"></path></svg>
\ No newline at end of file
<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"
<div v-if="!item.hidden&&item.children" class="menu-wrapper">
<router-link
v-if="hasOneShowingChild(item.children) && !onlyOneChild.children&&!item.alwaysShow"
:to="resolvePath(onlyOneChild.path)"
>
<el-menu-item
:index="resolvePath(onlyOneChild.path)"
:class="{'submenu-title-noDropdown':!isNest}"
>
<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>
<svg-icon
v-if="onlyOneChild.meta&&onlyOneChild.meta.icon"
:icon-class="onlyOneChild.meta.icon"
></svg-icon>
<span
v-if="onlyOneChild.meta&&onlyOneChild.meta.title"
slot="title"
>{{onlyOneChild.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>
<el-submenu v-else :index="item.name||item.path">
<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>
<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"
:item="child"
:key="child.path"
:base-path="resolvePath(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>
<router-link v-else :to="resolvePath(child.path)" :key="child.name">
<el-menu-item :index="resolvePath(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>
</div>
</template>
<script>
import path from "path";
export default {
name: "SidebarItem",
props: {
routes: {
type: Array
// route配置json
item: {
type: Object,
required: true
},
isNest: {
type: Boolean,
default: false
},
basePath: {
type: String,
default: ""
}
},
data() {
return {
onlyOneChild: null
};
},
methods: {
hasOneShowingChildren(children) {
hasOneShowingChild(children) {
const showingChildren = children.filter(item => {
return !item.hidden;
if (item.hidden) {
return false;
} else {
this.onlyOneChild = item;
return true;
}
});
if (showingChildren.length === 1) {
return true;
}
return false;
},
resolvePath(...paths) {
return path.resolve(this.basePath, ...paths);
}
}
};
</script>
<style lang="scss" scoped>
@import '@/styles/color.scss';
@import "@/styles/color.scss";
.menu-wrapper {
/deep/ .el-menu-item,
.el-submenu__title {
height: 46px;
line-height: 46px;
}
/deep/ a{
/deep/ a {
background-color: #ffc248;
}
/deep/ .el-menu-item.is-active{
/deep/ .el-menu-item.is-active {
margin-left: 8px;
background-color: $Theme-color!important;
background-color: $Theme-color !important;
}
/deep/ .el-menu-item{
/deep/ .el-menu-item {
padding: 0 20px 0 12px;
}
}
</style>
</style>
\ No newline at end of file
......
......@@ -9,15 +9,8 @@
:collapse="isCollapse"
active-text-color="#ffffff"
>
<div class="title" :class="{minititle:isCollapse}">
<!-- <svg-icon icon-class="logo" icon-name></svg-icon> -->
<el-image>
<div slot="error" class="image-slot">
这是logo存放位置
</div>
</el-image>
</div>
<sidebar-item :routes="routes"></sidebar-item>
<Logo :collapse="isCollapse" />
<sidebar-item v-for="route in routes" :key="route.name" :item="route" :base-path="route.path"></sidebar-item>
</el-menu>
</scroll-bar>
</template>
......@@ -26,9 +19,10 @@
import { mapGetters } from "vuex";
import SidebarItem from "./SidebarItem";
import ScrollBar from "@/components/ScrollBar";
import Logo from "./logo";
export default {
components: { SidebarItem, ScrollBar },
components: { SidebarItem, ScrollBar, Logo },
computed: {
...mapGetters(["sidebar"]),
routes() {
......
<template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
<transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<svg-icon icon-class="logo" class-name="sidebar-logo"></svg-icon>
<div class="sidebar-title">{{ title }}</div>
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
<svg-icon icon-class="logo" class-name="sidebar-logo"></svg-icon>
<div class="sidebar-title">{{ title }}</div>
</router-link>
</transition>
</div>
</template>
<script>
export default {
name: "SidebarLogo",
props: {
collapse: {
type: Boolean,
required: true
}
},
data() {
return {
title: "logo名称",
};
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.sidebarLogoFade-enter-active {
transition: opacity .28s;
}
.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
opacity: 0;
}
.sidebar-logo-container {
position: relative;
width: 100%;
height: 61px;
line-height: 61px;
text-align: center;
overflow: hidden;
background-color: #ffffff;
& > .sidebar-logo-link {
height: 100%;
width: 100%;
& > .sidebar-logo {
width: 32px;
height: 32px;
vertical-align: middle;
margin-right: 12px;
}
& > .sidebar-title {
display: inline-block;
margin: 0;
color: #333;
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 !important;
}
}
</style>
\ No newline at end of file
......@@ -32,7 +32,7 @@ export default {
classObj() {
return {
hideSidebar: !this.sidebar.opened,
openSidebar: this.sidebar.opened,
openSidebar: this.sidebar.opened
};
}
}
......
......@@ -8,6 +8,7 @@ import db from './api/operationalData'
// 引用element
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import './permission'
import './icons'
import '@/styles/index.scss'
......
import router from './router'
import store from './store'
import NProgress from 'nprogress' // Progress 进度条
import 'nprogress/nprogress.css'// Progress 进度条样式
// import { Message } from 'element-ui'
// const whiteList = ['/login'] // 不重定向白名单
router.beforeEach((to, from, next) => {
NProgress.start()
if (store.getters.token) {
if (to.path === '/login') {
next({ path: '/' })
NProgress.done()
} else {
// if (store.getters.roles.length === 0) {
// store.dispatch('GetInfo').then(res => { // 拉取用户信息
// next()
// }).catch((err) => {
// store.dispatch('FedLogOut').then(() => {
// Message.error(err || '请重新登录')
// next({ path: '/' })
// })
// })
// } else {
// next()
// }
}
} else {
// if (whiteList.indexOf(to.path) !== -1) {
// next()
// } else {
// next('/login')
// NProgress.done()
// }
next()
NProgress.done()
}
})
router.afterEach(() => {
NProgress.done() // 结束Progress
})
import Layout from '../layout'
const Login = () => import('@/views/login')
const Notfound = () => import('@/views/404')
/**
* 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,
}
**/
/**
* hidden: true 如果在模板中使用该选项,则不会在侧栏显示该路由(例如:Dashboard),如果是在第一个子路由中使用,侧栏则只显示第一个子路由的名字和图标(例如: Form)
* alwaysShow: true 如果设置为true它则会始终显示根菜单,无视自路由长度,没有设置的话,就会折叠起来(不清楚为什么没有作用,可能是我写错位置了?)
* redirect: noredirect 若设置为noredirect则顶部面包屑不能够为其重定向.
* name:'router-name' 路由名称,此项为必须填写项
* meta : {
title: 'title' 这里的名字决定了面包屑和侧栏的名字
icon: 'svg-name' 当你在svg文件夹内加入了你的图标,那么在这里填写图标名他就会显示在侧栏
}
**/
export default [
{ path: '/login', component: Login, hidden: true },
{ path: '/404', component: Notfound, hidden: true },
{
path: '/',
component: Layout,
redirect: '/dashboard',
name: 'Dashboard',
hidden: true,
children: [{
path: 'dashboard',
component: () => import('@/components/LandingPage')
}]
},
{
path: '/form',
component: Layout,
children: [
{
path: 'index',
name: 'Form',
component: () => import('@/views/form/index'),
meta: { title: '表单', icon: 'form' }
}
]
}
]
import Vue from 'vue'
import Router from 'vue-router'
// 模板
import Layout from '../layout'
Vue.use(Router)
/**
* 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,
}
**/
// 引入路由表
import routerMap from './constantRouterMap'
/**
* hidden: true 如果在模板中使用该选项,则不会在侧栏显示该路由(例如:Dashboard),如果是在第一个子路由中使用,侧栏则只显示第一个子路由的名字和图标(例如: Form)
* alwaysShow: true 如果设置为true它则会始终显示根菜单,无视自路由长度,没有设置的话,就会折叠起来(不清楚为什么没有作用,可能是我写错位置了?)
* redirect: noredirect 若设置为noredirect则顶部面包屑不能够为其重定向.
* name:'router-name' 路由名称,此项为必须填写项
* meta : {
title: 'title' 这里的名字决定了面包屑和侧栏的名字
icon: 'svg-name' 当你在svg文件夹内加入了你的图标,那么在这里填写图标名他就会显示在侧栏
}
**/
Vue.use(Router)
export default new Router({
scrollBehavior: () => ({ y: 0 }),
routes: [
{
path: '/',
component: Layout,
redirect: '/dashboard',
name: 'Dashboard',
hidden: true,
children: [{
path: 'dashboard',
component: () => import('@/components/LandingPage')
}]
},
{
path: '/form',
component: Layout,
children: [
{
path: 'index',
name: 'Form',
component: () => import('@/views/form/index'),
meta: { title: '表单', icon: 'form' }
}
]
},
{ path: '*', redirect: '/404', hidden: true }
]
routes: routerMap
})
......
import Vue from 'vue'
import Vuex from 'vuex'
// 注释这个的原因是因为会导致vuex操作失败
// import { createPersistedState, createSharedMutations } from 'vuex-electron'
import modules from './modules'
......@@ -10,10 +11,10 @@ Vue.use(Vuex)
export default new Vuex.Store({
modules,
getters,
getters
// plugins: [
// createPersistedState(),
// createSharedMutations()
// ],
strict: process.env.NODE_ENV !== 'production'
// strict: process.env.NODE_ENV !== 'production'
})
......
// 记录的数值
const Counter = {
state: {
},
mutations: {
},
actions: {
}
}
export default Counter
......@@ -2,7 +2,11 @@
* The file enables `@/store/index.js` to import all vuex modules
* in a one-shot manner. There should not be any reason to edit this file.
*/
/**
* 如果没什么大问题的话,这里的js会默认将你的所有模块全部自动导出,
* 没有什么意外无需更改这里的代码和外部的index代码,需要如果需要将值暴露给
* getter的话,还请自行手动添加,它的弊端就是不能够再这里留空文件,不然就会出现Cannot read property 'getters' of undefined的控制台报错信息
*/
const files = require.context('.', false, /\.js$/)
const modules = {}
......
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)
}
......@@ -9,6 +9,8 @@ const serves = axios.create({
// 设置请求发送之前的拦截器
serves.interceptors.request.use(config => {
console.log(config)
console.log(process.env)
// 设置发送之前数据需要做什么处理
return config
}, err => Promise.reject(err))
......
/**
* Created by jiachenpan on 16/11/18.
*/
export function isvalidUsername (str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
}
/* 合法uri */
export function validateURL (textval) {
const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return urlregex.test(textval)
}
/* 小写字母 */
export function validateLowerCase (str) {
const reg = /^[a-z]+$/
return reg.test(str)
}
/* 大写字母 */
export function validateUpperCase (str) {
const reg = /^[A-Z]+$/
return reg.test(str)
}
/* 大小写字母 */
export function validatAlphabets (str) {
const reg = /^[A-Za-z]+$/
return reg.test(str)
}
<template>
<div class="login-container">
<el-form
class="login-form"
autocomplete="on"
:model="loginForm"
:rules="loginRules"
ref="loginForm"
label-position="left"
>
<h3 class="title">vue-element-admin</h3>
<el-form-item prop="username">
<span class="svg-container svg-container_login">
<svg-icon icon-class="user" />
</span>
<el-input
name="username"
type="text"
v-model="loginForm.username"
autocomplete="on"
placeholder="用户名"
/>
</el-form-item>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password"></svg-icon>
</span>
<el-input
name="password"
:type="pwdType"
@keyup.enter.native="handleLogin"
v-model="loginForm.password"
autocomplete="on"
placeholder="密码"
></el-input>
<span class="show-pwd" @click="showPwd">
<svg-icon icon-class="eye" />
</span>
</el-form-item>
<el-form-item>
<el-button
type="primary"
style="width:100%;"
:loading="loading"
@click.native.prevent="handleLogin"
>登录</el-button>
</el-form-item>
<div class="tips">
<span style="margin-right:20px;">username: admin</span>
<span>password: admin</span>
</div>
</el-form>
</div>
</template>
<script>
import { isvalidUsername } from "@/utils/validate";
import { login } from "@/api/login";
export default {
name: "login",
data() {
const validateUsername = (rule, value, callback) => {
if (!isvalidUsername(value)) {
callback(new Error("请输入正确的用户名"));
} else {
callback();
}
};
const validatePass = (rule, value, callback) => {
if (value.length < 5) {
callback(new Error("密码不能小于5位"));
} else {
callback();
}
};
return {
loginForm: {
username: "admin",
password: "admin"
},
loginRules: {
username: [
{ required: true, trigger: "blur", validator: validateUsername }
],
password: [{ required: true, trigger: "blur", validator: validatePass }]
},
loading: false,
pwdType: "password"
};
},
methods: {
showPwd() {
if (this.pwdType === "password") {
this.pwdType = "";
} else {
this.pwdType = "password";
}
},
handleLogin() {
this.loading = true;
this.$refs.loginForm.validate(valid => {
if (valid) {
// this.$store.dispatch('Login', this.loginForm).then(() => {
this.loading = false;
this.$router.push({ path: "/" });
// login(this.loginForm).then(res=>console.log(res))
// }).catch(() => {
// this.loading = false
// })
} else {
console.log("输入错误");
return false;
}
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss">
$bg: #2d3a4b;
$light_gray: #eee;
/* reset element-ui css */
.login-container {
left: 0px;
.el-input {
display: inline-block;
height: 47px;
width: 85%;
input {
background: transparent;
border: 0px;
-webkit-appearance: none;
border-radius: 0px;
padding: 12px 5px 12px 15px;
color: $light_gray;
height: 47px;
&:-webkit-autofill {
-webkit-box-shadow: 0 0 0px 1000px $bg inset !important;
-webkit-text-fill-color: #fff !important;
}
}
}
.el-form-item {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.1);
border-radius: 5px;
color: #454545;
}
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
$bg: #2d3a4b;
$dark_gray: #889aa4;
$light_gray: #eee;
.login-container {
position: fixed;
height: 100%;
width: 100%;
background-color: $bg;
.login-form {
position: absolute;
left: 0;
right: 0;
width: 520px;
padding: 35px 35px 15px 35px;
margin: 120px auto;
}
.tips {
font-size: 14px;
color: #fff;
margin-bottom: 10px;
span {
&:first-of-type {
margin-right: 16px;
}
}
}
.svg-container {
padding: 6px 5px 6px 15px;
color: $dark_gray;
vertical-align: middle;
width: 30px;
display: inline-block;
&_login {
font-size: 20px;
}
}
.title {
font-size: 26px;
font-weight: 400;
color: $light_gray;
margin: 0px auto 40px auto;
text-align: center;
font-weight: bold;
}
.show-pwd {
position: absolute;
right: 10px;
top: 7px;
font-size: 16px;
color: $dark_gray;
cursor: pointer;
user-select: none;
}
}
</style>