feat:迁移vue-element-admin登录页面

This commit is contained in:
有来技术
2021-11-19 23:55:14 +08:00
parent a4fe3987c3
commit abf2cce2cb
14 changed files with 339 additions and 369 deletions

View File

@@ -16,6 +16,7 @@
"devDependencies": {
"@types/node": "^16.11.7",
"@vitejs/plugin-vue": "^1.9.3",
"sass": "^1.43.4",
"typescript": "^4.4.3",
"vite": "^2.6.4",
"vue-tsc": "^0.3.0"

View File

@@ -1,14 +1,12 @@
<template>
<router-view/>
<div id="app">
<router-view />
</div>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
<script>
export default {
name: 'App'
}
</style>
</script>

View File

@@ -2,6 +2,7 @@ import { createApp } from 'vue'
import App from './App.vue'
import router from "./router";
import {store,key} from './store'
import '@styles/index.scss'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

View File

@@ -3,6 +3,11 @@ import Layout from '@/layout/index.vue'
const routes: Array<RouteRecordRaw> = [
{
path: '/login',
name: 'Login',
component: () => import('@views/login/index.vue'),
meta: {title: '登录'}
}, {
path: '/',
component: Layout,
redirect: '/dashboard',

View File

@@ -1,9 +1,8 @@
// 接口类型声明
export interface UserState {
token: string,
name: string,
nickname: string,
avatar: string,
introduction: string,
roles: string[],
perms: string[]
}

View File

@@ -1,32 +1,42 @@
import {Module} from "vuex";
import {UserState, RootStateTypes} from "@store/interface";
import {Local} from "@utils/storage";
import {login} from "@api/login"
import {rejects} from "assert";
import {getUserInfo, login,logout} from "@api/login"
const getDefaultState = () => {
return {
token: Local.get('token'),
nickname: '',
avatar: '',
roles: [],
perms: []
}
}
const userModule: Module<UserState, RootStateTypes> = {
namespaced: true,
state: {
token: Local.get('token') || '',
name: '',
nickname: '',
avatar: '',
introduction: '',
roles: [],
perms: []
},
mutations: {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState())
},
SET_TOKEN(state: UserState, token: string) {
state.token = token
},
SET_NAME(state: UserState, name: string) {
state.name = name
SET_NICKNAME(state: UserState, nickname: string) {
state.nickname = nickname
},
SET_AVATAR(state: UserState, avatar: string) {
state.avatar = avatar
},
SET_INTRODUCTION(state: UserState, introduction: string) {
state.introduction = introduction
},
SET_ROLES(state: UserState, roles: string[]) {
state.roles = roles
},
@@ -35,21 +45,77 @@ const userModule: Module<UserState, RootStateTypes> = {
}
},
actions: {
// 登录
login({commit}, userInfo: { username: string, password: string }) {
const {username, password} = userInfo
/**
* 用户登录请求
* @param userInfo 登录用户信息
* username: 用户名
* password: 密码
* code: 验证码
* uuid: 匹配正确验证码的 key
*/
login({commit}, userInfo: { username: string, password: string, code: string, uuid: string }) {
const {username, password, code, uuid} = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
login(
{
username: username.trim(),
password: password,
grant_type: 'captcha',
code: code,
uuid: uuid
}
).then(response => {
const {access_token, token_type} = response.data
const accessToken = token_type + " " + access_token
Local.set("token",accessToken)
commit('SET_TOKEN',accessToken)
Local.set("token", accessToken)
commit('SET_TOKEN', accessToken)
}).catch(error => {
reject(error)
})
})
},
/**
* 获取用户信息(昵称、头像、角色集合、权限集合)
*/
getUserInfo({commit, state}) {
return new Promise(((resolve, reject) => {
getUserInfo().then(response => {
const {data} = response
if (!data) {
return reject('Verification failed, please Login again.')
}
const {nickname, avatar, roles, perms} = data
if (!roles || roles.length <= 0) {
reject('getUserInfo: roles must be a non-null array!')
}
commit('SET_NICKNAME', nickname)
commit('SET_AVATAR', avatar)
commit('SET_ROLES', roles)
commit('SET_PERMS', perms)
resolve(data)
}).catch(error => {
reject(error)
})
})
)
},
/**
* 注销
*/
logout({commit,state}){
return new Promise(((resolve, reject) => {
logout().then(()=>{
Local.remove('token')
commit('RESET_STATE')
}).catch(error=>{
reject(error)
})
}))
}
}
}

View File

@@ -1,99 +0,0 @@
@import './variables.scss';
@mixin colorBtn($color) {
background: $color;
&:hover {
color: $color;
&:before,
&:after {
background: $color;
}
}
}
.blue-btn {
@include colorBtn($blue)
}
.light-blue-btn {
@include colorBtn($light-blue)
}
.red-btn {
@include colorBtn($red)
}
.pink-btn {
@include colorBtn($pink)
}
.green-btn {
@include colorBtn($green)
}
.tiffany-btn {
@include colorBtn($tiffany)
}
.yellow-btn {
@include colorBtn($yellow)
}
.pan-btn {
font-size: 14px;
color: #fff;
padding: 14px 36px;
border-radius: 8px;
border: none;
outline: none;
transition: 600ms ease all;
position: relative;
display: inline-block;
&:hover {
background: #fff;
&:before,
&:after {
width: 100%;
transition: 600ms ease all;
}
}
&:before,
&:after {
content: '';
position: absolute;
top: 0;
right: 0;
height: 2px;
width: 0;
transition: 400ms ease all;
}
&::after {
right: inherit;
top: inherit;
left: 0;
bottom: 0;
}
}
.custom-button {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
color: #fff;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: 0;
margin: 0;
padding: 10px 15px;
font-size: 14px;
border-radius: 4px;
}

View File

@@ -15,36 +15,6 @@
display: none;
}
.cell {
.el-tag {
margin-right: 0px;
}
}
.small-padding {
.cell {
padding-left: 5px;
padding-right: 5px;
}
}
.fixed-width {
.el-button--mini {
padding: 7px 10px;
min-width: 60px;
}
}
.status-col {
.cell {
padding: 0 10px;
text-align: center;
.el-tag {
margin-right: 0px;
}
}
}
// to fixed https://github.com/ElemeFE/element/issues/2461
.el-dialog {
@@ -73,11 +43,6 @@
}
}
// fix date-picker ui bug in filter-item
.el-range-editor.el-input__inner {
display: inline-flex !important;
}
// to fix el-date-picker css style
.el-range-separator {
box-sizing: content-box;

View File

@@ -1,31 +0,0 @@
/**
* I think element-ui's default theme color is too light for long-term use.
* So I modified the default color and you can modify it to your liking.
**/
/* theme color */
$--color-primary: #1890ff;
$--color-success: #13ce66;
$--color-warning: #ffba00;
$--color-danger: #ff4949;
// $--color-info: #1E1E1E;
$--button-font-weight: 400;
// $--color-text-regular: #1f2d3d;
$--border-color-light: #dfe4ed;
$--border-color-lighter: #e6ebf5;
$--table-border: 1px solid #dfe6ec;
/* icon font path, required */
$--font-path: "~element-ui/lib/theme-chalk/fonts";
@import "~element-ui/packages/theme-chalk/src/index";
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
theme: $--color-primary;
}

View File

@@ -3,9 +3,10 @@
@import './transition.scss';
@import './element-ui.scss';
@import './sidebar.scss';
@import './btn.scss';
body {
margin: 0;
padding: 0;
height: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
@@ -32,14 +33,6 @@ html {
box-sizing: inherit;
}
.no-padding {
padding: 0px !important;
}
.padding-content {
padding: 4px 0;
}
a:focus,
a:active {
outline: none;
@@ -57,34 +50,6 @@ div:focus {
outline: none;
}
.fr {
float: right;
}
.fl {
float: left;
}
.pr-5 {
padding-right: 5px;
}
.pl-5 {
padding-left: 5px;
}
.block {
display: block;
}
.pointer {
cursor: pointer;
}
.inlineBlock {
display: block;
}
.clearfix {
&:after {
visibility: hidden;
@@ -96,96 +61,7 @@ div:focus {
}
}
aside {
background: #eef1f6;
padding: 8px 24px;
margin-bottom: 20px;
border-radius: 2px;
display: block;
line-height: 32px;
font-size: 16px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
color: #2c3e50;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
a {
color: #337ab7;
cursor: pointer;
&:hover {
color: rgb(32, 160, 255);
}
}
}
//main-container全局样式
// main-container global css
.app-container {
padding: 20px;
}
.components-container {
margin: 30px 50px;
position: relative;
}
.pagination-container {
margin-top: 30px;
}
.text-center {
text-align: center
}
.sub-navbar {
height: 50px;
line-height: 50px;
position: relative;
width: 100%;
text-align: right;
padding-right: 20px;
transition: 600ms ease position;
background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
.subtitle {
font-size: 20px;
color: #fff;
}
&.draft {
background: #d0d0d0;
}
&.deleted {
background: #d0d0d0;
}
}
.link-type,
.link-type:focus {
color: #337ab7;
cursor: pointer;
&:hover {
color: rgb(32, 160, 255);
}
}
.filter-container {
padding-bottom: 10px;
.filter-item {
display: inline-block;
vertical-align: middle;
margin-bottom: 10px;
}
}
//refine vue-multiselect plugin
.multiselect {
line-height: 16px;
}
.multiselect--active {
z-index: 1000 !important;
}

View File

@@ -26,41 +26,3 @@
width: 100%;
height: 100%;
}
@mixin pct($pct) {
width: #{$pct};
position: relative;
margin: 0 auto;
}
@mixin triangle($width, $height, $color, $direction) {
$width: $width/2;
$color-border-style: $height solid $color;
$transparent-border-style: $width solid transparent;
height: 0;
width: 0;
@if $direction==up {
border-bottom: $color-border-style;
border-left: $transparent-border-style;
border-right: $transparent-border-style;
}
@else if $direction==right {
border-left: $color-border-style;
border-top: $transparent-border-style;
border-bottom: $transparent-border-style;
}
@else if $direction==down {
border-top: $color-border-style;
border-left: $transparent-border-style;
border-right: $transparent-border-style;
}
@else if $direction==left {
border-right: $color-border-style;
border-top: $transparent-border-style;
border-bottom: $transparent-border-style;
}
}

View File

@@ -1,17 +1,7 @@
// base color
$blue:#324157;
$light-blue:#3A71A8;
$red:#C03639;
$pink: #E65D6E;
$green: #30B08F;
$tiffany: #4AB7BD;
$yellow:#FEC171;
$panGreen: #30B08F;
// sidebar
$menuText:#bfcbd9;
$menuActiveText:#409EFF;
$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951
$subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951
$menuBg:#304156;
$menuHover:#263445;

233
src/views/login/index.vue Normal file
View File

@@ -0,0 +1,233 @@
<template>
<div class="login-container">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">
<div class="title-container">
<h3 class="title">Login Form</h3>
</div>
<el-form-item prop="username">
<span class="svg-container">
<svg-icon icon-class="user" />
</span>
<el-input
ref="username"
v-model="loginForm.username"
placeholder="Username"
name="username"
type="text"
tabindex="1"
auto-complete="on"
/>
</el-form-item>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password" />
</span>
<el-input
:key="passwordType"
ref="password"
v-model="loginForm.password"
:type="passwordType"
placeholder="Password"
name="password"
tabindex="2"
auto-complete="on"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd" @click="showPwd">
<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
</span>
</el-form-item>
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>
<div class="tips">
<span style="margin-right:20px;">username: admin</span>
<span> password: any</span>
</div>
</el-form>
</div>
</template>
<script>
export default {
name: 'Login',
data() {
const validatePassword = (rule, value, callback) => {
if (value.length < 6) {
callback(new Error('The password can not be less than 6 digits'))
} else {
callback()
}
}
return {
loginForm: {
username: 'admin',
password: '111111'
},
loginRules: {
username: [{ required: true, trigger: 'blur'}],
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
},
loading: false,
passwordType: 'password',
redirect: undefined
}
},
watch: {
$route: {
handler: function(route) {
this.redirect = route.query && route.query.redirect
},
immediate: true
}
},
methods: {
showPwd() {
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus()
})
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
// 暂跳转首页控制台,后续整合登录
this.$router.push({ path: '/' })
return false
this.$store.dispatch('user/login', this.loginForm).then(() => {
this.$router.push({ path: this.redirect || '/' })
this.loading = false
}).catch(() => {
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
}
}
}
</script>
<style lang="scss">
/* 修复input 背景不协调 和光标变色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
$bg:#283443;
$light_gray:#fff;
$cursor: #fff;
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
.login-container .el-input input {
color: $cursor;
}
}
/* reset element-ui css */
.login-container {
.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;
caret-color: $cursor;
&:-webkit-autofill {
box-shadow: 0 0 0px 1000px $bg inset !important;
-webkit-text-fill-color: $cursor !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 lang="scss" scoped>
$bg:#2d3a4b;
$dark_gray:#889aa4;
$light_gray:#eee;
.login-container {
min-height: 100%;
width: 100%;
background-color: $bg;
overflow: hidden;
.login-form {
position: relative;
width: 520px;
max-width: 100%;
padding: 160px 35px 0;
margin: 0 auto;
overflow: hidden;
}
.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;
}
.title-container {
position: relative;
.title {
font-size: 26px;
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>

View File

@@ -45,7 +45,11 @@ export default defineConfig({
{
find:"@views",
replacement: path.resolve("./src/views")
}
},
{
find:"@styles",
replacement: path.resolve("./src/styles")
},
]
}
})