diff --git a/package.json b/package.json index 95aff6fe..3005ed75 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/App.vue b/src/App.vue index 35ba8bd6..cb237d29 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,14 +1,12 @@ - - + + + - + + diff --git a/src/main.ts b/src/main.ts index 23c90d1a..1c5ea85f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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' diff --git a/src/router/index.ts b/src/router/index.ts index 83c4b056..d83850d0 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -3,6 +3,11 @@ import Layout from '@/layout/index.vue' const routes: Array = [ { + path: '/login', + name: 'Login', + component: () => import('@views/login/index.vue'), + meta: {title: '登录'} + }, { path: '/', component: Layout, redirect: '/dashboard', diff --git a/src/store/interface.ts b/src/store/interface.ts index b90ea070..8350855b 100644 --- a/src/store/interface.ts +++ b/src/store/interface.ts @@ -1,9 +1,8 @@ // 接口类型声明 export interface UserState { token: string, - name: string, + nickname: string, avatar: string, - introduction: string, roles: string[], perms: string[] } diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts index 632d168c..4b1d280b 100644 --- a/src/store/modules/user.ts +++ b/src/store/modules/user.ts @@ -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 = { 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 = { } }, 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) + }) + })) } + } } diff --git a/src/styles/btn.scss b/src/styles/btn.scss deleted file mode 100644 index e6ba1a8e..00000000 --- a/src/styles/btn.scss +++ /dev/null @@ -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; -} diff --git a/src/styles/element-ui.scss b/src/styles/element-ui.scss index 49474de3..00624119 100644 --- a/src/styles/element-ui.scss +++ b/src/styles/element-ui.scss @@ -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; diff --git a/src/styles/element-variables.scss b/src/styles/element-variables.scss deleted file mode 100644 index 5bdc4dad..00000000 --- a/src/styles/element-variables.scss +++ /dev/null @@ -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; -} diff --git a/src/styles/index.scss b/src/styles/index.scss index 96095ef6..e63d7642 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -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; -} diff --git a/src/styles/mixin.scss b/src/styles/mixin.scss index 06fa0612..36b74bbd 100644 --- a/src/styles/mixin.scss +++ b/src/styles/mixin.scss @@ -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; - } -} diff --git a/src/styles/variables.scss b/src/styles/variables.scss index a19c27c1..be557726 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -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; diff --git a/src/views/login/index.vue b/src/views/login/index.vue new file mode 100644 index 00000000..f3f7e023 --- /dev/null +++ b/src/views/login/index.vue @@ -0,0 +1,233 @@ + + + + + Login Form + + + + + + + + + + + + + + + + + + + + Login + + + username: admin + password: any + + + + + + + + + + + diff --git a/vite.config.ts b/vite.config.ts index dfb0fd4a..c3a92319 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -45,7 +45,11 @@ export default defineConfig({ { find:"@views", replacement: path.resolve("./src/views") - } + }, + { + find:"@styles", + replacement: path.resolve("./src/styles") + }, ] } })