login
This commit is contained in:
parent
dbb5c5d2bc
commit
398deb691c
|
|
@ -2,7 +2,7 @@
|
||||||
VITE_PUBLIC_PATH = '/'
|
VITE_PUBLIC_PATH = '/'
|
||||||
|
|
||||||
# 是否启用MOCK
|
# 是否启用MOCK
|
||||||
VITE_APP_USE_MOCK = true
|
VITE_APP_USE_MOCK = false
|
||||||
|
|
||||||
# proxy
|
# proxy
|
||||||
VITE_PROXY = [["/api-local","http://127.0.0.1:8002/api"],["/api-mock","http://127.0.0.1:8003"]]
|
VITE_PROXY = [["/api-local","http://127.0.0.1:8002/api"],["/api-mock","http://127.0.0.1:8003"]]
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,17 @@ export function userLogin(data = {}) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* params
|
||||||
|
* @returns 当前登录人信息
|
||||||
|
*/
|
||||||
|
export function getUser() {
|
||||||
|
return request({
|
||||||
|
url: '/index/getUserInfo',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取验证码
|
* 获取验证码
|
||||||
* @returns 验证码图片
|
* @returns 验证码图片
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 517 B |
Binary file not shown.
|
After Width: | Height: | Size: 623 B |
Binary file not shown.
|
After Width: | Height: | Size: 648 B |
|
|
@ -1,17 +1,79 @@
|
||||||
<template>
|
<template>
|
||||||
<n-layout-header class="layout__header" bordered>
|
<n-layout-header class="layout__header" bordered>
|
||||||
<!-- <SideMenu menu-mode="horizontal" /> -->
|
<div class="header__logo">
|
||||||
<log-out />
|
<n-image height="18" src="/logo.png" preview-disabled />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-dropdown trigger="hover" :options="options" @select="handleSelect">
|
||||||
|
<div class="user_msg">
|
||||||
|
<n-image
|
||||||
|
class="user_avatar"
|
||||||
|
:src="userInfo.avatar"
|
||||||
|
preview-disabled
|
||||||
|
/>
|
||||||
|
<span class="user_name">{{ userInfo.realname }}</span>
|
||||||
|
</div>
|
||||||
|
</n-dropdown>
|
||||||
</n-layout-header>
|
</n-layout-header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SideMenu from '@/layout/components/Menu/index.vue'
|
import { defineComponent, reactive, toRefs, computed } from 'vue'
|
||||||
import LogOut from '../Logout/index.vue'
|
import { useRouter } from 'vue-router'
|
||||||
import { defineComponent } from 'vue'
|
import { EditOutlined, LogoutOutlined } from '@vicons/antd'
|
||||||
|
import { renderIcon } from '@/utils'
|
||||||
|
import { useUserStore } from '@/store/modules/user.js'
|
||||||
|
import { useSettingStore } from '@/store/modules/setting.js'
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'LayoutHeader',
|
name: 'LayoutHeader',
|
||||||
components: { SideMenu, LogOut }
|
setup() {
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const settingStore = useSettingStore()
|
||||||
|
const data = reactive({
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '修改密码',
|
||||||
|
key: 'edit',
|
||||||
|
icon: renderIcon(EditOutlined)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '退出登录',
|
||||||
|
key: 'out',
|
||||||
|
icon: renderIcon(LogoutOutlined)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
userInfo: {
|
||||||
|
avatar: '',
|
||||||
|
realname: '管理员'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const getLogoWidth = computed(() => {
|
||||||
|
return settingStore.getSidebarSetting.width - 30
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleSelect(key) {
|
||||||
|
switch (key) {
|
||||||
|
case 'out':
|
||||||
|
await logOut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logOut() {
|
||||||
|
const res = await userStore.userLogout()
|
||||||
|
if (res.code === 0) {
|
||||||
|
router.replace('/login')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
getLogoWidth,
|
||||||
|
handleSelect
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -19,8 +81,11 @@ export default defineComponent({
|
||||||
.layout__header {
|
.layout__header {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.header__logo{
|
||||||
|
height: 18px;
|
||||||
}
|
}
|
||||||
.user_msg {
|
.user_msg {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { getUser } from '@/api/user'
|
import { userLogin, loginOut } from '@/api/login'
|
||||||
import { removeToken } from '@/utils/token'
|
import { getUser } from '@/api/login/index.js'
|
||||||
|
import { setToken, removeToken } from '@/utils/token'
|
||||||
|
|
||||||
|
import { useTagsMenuStore } from './tagsMenu.js'
|
||||||
|
import { usePermissionStore } from './permission.js'
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', {
|
export const useUserStore = defineStore('user', {
|
||||||
persist: true,
|
persist: true,
|
||||||
|
|
@ -15,24 +19,51 @@ export const useUserStore = defineStore('user', {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async getUserInfo() {
|
/* 登录 */
|
||||||
|
async getLoginToken(form) {
|
||||||
try {
|
try {
|
||||||
const res = await getUser()
|
const res = await userLogin(form)
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
this.userInfo = res.data
|
/* 设置token */
|
||||||
return Promise.resolve(res.msg)
|
setToken(res.data.access_token)
|
||||||
|
this.getUserInfo()
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject(res.msg)
|
return Promise.reject(res)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
return Promise.reject(error)
|
||||||
return Promise.reject(error.msg)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
logout() {
|
/* 获取用户信息 */
|
||||||
removeToken()
|
async getUserInfo() {
|
||||||
this.userInfo = {}
|
const res = await getUser()
|
||||||
|
if (res.code === 0) {
|
||||||
|
this.setUserInfo(res.data)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
async userLogout() {
|
||||||
|
try {
|
||||||
|
const res = await loginOut()
|
||||||
|
if (res.code === 0) {
|
||||||
|
removeToken()
|
||||||
|
this.reset()
|
||||||
|
return Promise.resolve(res)
|
||||||
|
} else {
|
||||||
|
return Promise.reject(res)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.$reset()
|
||||||
|
const tagsMenuStore = useTagsMenuStore()
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
|
tagsMenuStore.$reset()
|
||||||
|
permissionStore.$reset()
|
||||||
|
},
|
||||||
|
|
||||||
setUserInfo(userInfo = {}) {
|
setUserInfo(userInfo = {}) {
|
||||||
this.userInfo = { ...this.userInfo, ...userInfo }
|
this.userInfo = { ...this.userInfo, ...userInfo }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,12 @@ export function setupInterceptor(service) {
|
||||||
|
|
||||||
service.interceptors.response.use(
|
service.interceptors.response.use(
|
||||||
(response) => {
|
(response) => {
|
||||||
|
const { method } = response?.config
|
||||||
const { code } = response?.data
|
const { code } = response?.data
|
||||||
const { currentRoute } = router
|
const { currentRoute } = router
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 0:
|
case 0:
|
||||||
if (!response.config.hideMessage) {
|
if (method !== 'get') {
|
||||||
$message.success(response.data.msg)
|
$message.success(response.data.msg)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,68 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="login_bg">
|
<div class="login">
|
||||||
<n-form
|
<div class="login__container">
|
||||||
ref="formRef"
|
<div class="container__left" />
|
||||||
:model="loginForm"
|
<div class="container__right">
|
||||||
:rules="rules"
|
<div class="right__title">汤山林场巡检管理平台</div>
|
||||||
label-placement="left"
|
<div class="right__form">
|
||||||
label-width="auto"
|
<n-form
|
||||||
require-mark-placement="right-hanging"
|
ref="formRef"
|
||||||
:style="{
|
:model="loginForm"
|
||||||
maxWidth: '640px',
|
:rules="rules"
|
||||||
backgroundColor: '#fff',
|
label-placement="left"
|
||||||
padding: '20px',
|
require-mark-placement="right-hanging"
|
||||||
borderRadius: '10px'
|
:style="{maxWidth: '300px'}"
|
||||||
}"
|
@keyup.enter="handleLogin"
|
||||||
@keyup.enter="handleLogin"
|
>
|
||||||
>
|
<n-form-item path="username">
|
||||||
<n-form-item label="用户名" path="username">
|
<n-input v-model:value="loginForm.username" placeholder="请输入用户名">
|
||||||
<n-input v-model:value="loginForm.username" placeholder="请输入用户名" />
|
<template #prefix>
|
||||||
</n-form-item>
|
<img src="@/assets/icon/username.png" alt="">
|
||||||
<n-form-item label="密码" path="password">
|
</template>
|
||||||
<n-input v-model:value="loginForm.password" type="password" placeholder="请输入用户名" />
|
</n-input>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="验证码" path="captcha">
|
<n-form-item path="password">
|
||||||
<n-input v-model:value="loginForm.captcha" placeholder="请输入验证码" />
|
<n-input v-model:value="loginForm.password" type="password" placeholder="请输入用户名">
|
||||||
<img v-if="captcha" :src="captcha" alt="" @click="changeCode">
|
<template #prefix>
|
||||||
</n-form-item>
|
<img src="@/assets/icon/password.png" alt="">
|
||||||
<n-form-item path="remember">
|
</template>
|
||||||
<n-checkbox v-model:checked="loginForm.remember" size="medium" label="记住密码" />
|
</n-input>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item>
|
<n-form-item path="captcha">
|
||||||
<n-button @click="handleLogin">登录</n-button>
|
<n-input v-model:value="loginForm.captcha" placeholder="请输入验证码">
|
||||||
</n-form-item>
|
<template #prefix>
|
||||||
</n-form>
|
<img src="@/assets/icon/verify.png" alt="">
|
||||||
|
</template>
|
||||||
|
<template #suffix>
|
||||||
|
<img v-if="captcha" class="captcha_img" :src="captcha" @click="changeCode">
|
||||||
|
</template>
|
||||||
|
</n-input>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item path="remember">
|
||||||
|
<n-checkbox v-model:checked="loginForm.remember" size="medium" label="记住密码" />
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
</div>
|
||||||
|
<div class="right__button" @click="handleLogin">登录</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="login__copyright">copyright©2022拓恒技术有限公司</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { userLogin, userCaptcha } from '@/api/login/index.js'
|
|
||||||
import { setToken } from '@/utils/token'
|
|
||||||
import { ref, toRefs, reactive, onMounted } from 'vue'
|
import { ref, toRefs, reactive, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { userCaptcha } from '@/api/login/index.js'
|
||||||
|
import { useUserStore } from '@/store/modules/user.js'
|
||||||
export default {
|
export default {
|
||||||
name: 'LoginPage',
|
name: 'LoginPage',
|
||||||
setup() {
|
setup() {
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
console.log(userStore)
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
loginForm: {
|
loginForm: {
|
||||||
username: '',
|
username: '',
|
||||||
|
|
@ -50,78 +71,140 @@ export default {
|
||||||
captcha: '',
|
captcha: '',
|
||||||
remember: false
|
remember: false
|
||||||
},
|
},
|
||||||
|
captcha: '',
|
||||||
rules: {
|
rules: {
|
||||||
username: {
|
username: { required: true, message: '请输入用户名', trigger: ['blur', 'input'] },
|
||||||
required: true,
|
password: { required: true, message: '请输入密码', trigger: ['blur', 'input'] },
|
||||||
trigger: ['blur', 'input'],
|
captcha: { required: true, message: '请输入验证码', trigger: ['blur', 'input'] }
|
||||||
message: '请输入用户名'
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
required: true,
|
|
||||||
trigger: ['blur', 'input'],
|
|
||||||
message: '请输入密码'
|
|
||||||
},
|
|
||||||
captcha: {
|
|
||||||
required: true,
|
|
||||||
trigger: ['blur', 'input'],
|
|
||||||
message: '请输入验证码'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
changeCode(data.loginForm)
|
changeCode()
|
||||||
|
data.loginForm = Object.assign(
|
||||||
|
data.loginForm,
|
||||||
|
JSON.parse(localStorage.getItem('login-form'))
|
||||||
|
)
|
||||||
})
|
})
|
||||||
// 获取图形验证码
|
// 获取图形验证码
|
||||||
const captcha = ref('')
|
async function changeCode() {
|
||||||
async function changeCode(form) {
|
const captForm = await userCaptcha()
|
||||||
const params = {
|
data.captcha = captForm.data.captcha
|
||||||
username: form.username,
|
|
||||||
password: form.password,
|
|
||||||
captcha: form.captcha
|
|
||||||
}
|
|
||||||
const captForm = await userCaptcha(params)
|
|
||||||
captcha.value = captForm.data.captcha
|
|
||||||
data.loginForm.key = captForm.data.key
|
data.loginForm.key = captForm.data.key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formRef = ref('')
|
||||||
|
|
||||||
|
function handleLogin() {
|
||||||
|
formRef.value.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
const { username, password, remember } = data.loginForm
|
||||||
|
if (remember) {
|
||||||
|
localStorage.setItem('login-form', JSON.stringify({ username, password, remember }))
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('login-form')
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调用store中的方法,登录以及存储token和用户信息 */
|
||||||
|
userStore.getLoginToken(this.loginForm)
|
||||||
|
const toPath = decodeURIComponent((route.query?.redirect || '/'))
|
||||||
|
route.name === '/login' ? router.replace('/') : router.replace(toPath)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(data),
|
...toRefs(data),
|
||||||
captcha,
|
formRef,
|
||||||
|
handleLogin,
|
||||||
changeCode
|
changeCode
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleLogin() {
|
|
||||||
userLogin(this.loginForm).then((res) => {
|
|
||||||
if (res.code === 0) {
|
|
||||||
// 登录成功存储token,跳转首页
|
|
||||||
setToken(res.data.access_token)
|
|
||||||
this.goPage()
|
|
||||||
} else {
|
|
||||||
console.log(res)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 登录成功跳转
|
|
||||||
goPage() {
|
|
||||||
// const query = this.$route.query
|
|
||||||
// const path = query && query.from ? query.from : '/'
|
|
||||||
// console.log(path)
|
|
||||||
// this.$router.replace(path)
|
|
||||||
this.$router.replace('/home')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.login_bg {
|
.login{
|
||||||
width: 100%;
|
box-sizing: border-box;
|
||||||
height: 100%;
|
width: 100%;
|
||||||
display: flex;
|
height: 100%;
|
||||||
justify-content: center;
|
display: flex;
|
||||||
align-items: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-image: url("../../assets/back/login.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
}
|
||||||
|
.login__container {
|
||||||
|
width: 1460px;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
border-radius: 20px;
|
||||||
|
.container__left{
|
||||||
|
width: 50%;
|
||||||
|
align-self: stretch;
|
||||||
|
background-image: url("../../assets/back/form.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
}
|
}
|
||||||
</style>
|
.container__right{
|
||||||
|
flex: 1;
|
||||||
|
padding: 137px 0 236px 0;
|
||||||
|
.right__title{
|
||||||
|
font-size: 44px;
|
||||||
|
font-family: PingFang SC-Semibold, PingFang SC;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1890FF;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.right__form{
|
||||||
|
padding: 34px 0 20px 0;
|
||||||
|
.n-form{
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.n-input:not(.n-input--autosize) {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
::v-deep(.n-input__suffix){
|
||||||
|
cursor: pointer;
|
||||||
|
transform: translate(12px);
|
||||||
|
}
|
||||||
|
.captcha_img {
|
||||||
|
width: 90px;
|
||||||
|
height: 100%;
|
||||||
|
border-left: 1px solid rgb(224, 224, 230);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right__button{
|
||||||
|
width: 300px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #1890ff;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login__copyright {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1200px) {
|
||||||
|
.login__container{
|
||||||
|
max-width: 730px;
|
||||||
|
}
|
||||||
|
.container__left{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ const getRolesOption = async function() {
|
||||||
const res = await getRoleAll()
|
const res = await getRoleAll()
|
||||||
rolesOptions.value = dataToSelect(res.data, { label: 'name', value: 'id' })
|
rolesOptions.value = dataToSelect(res.data, { label: 'name', value: 'id' })
|
||||||
}
|
}
|
||||||
getRolesOption()
|
// getRolesOption()
|
||||||
const data = [
|
const data = [
|
||||||
{
|
{
|
||||||
label: '用户账号',
|
label: '用户账号',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue