login
This commit is contained in:
parent
dbb5c5d2bc
commit
398deb691c
|
|
@ -2,7 +2,7 @@
|
|||
VITE_PUBLIC_PATH = '/'
|
||||
|
||||
# 是否启用MOCK
|
||||
VITE_APP_USE_MOCK = true
|
||||
VITE_APP_USE_MOCK = false
|
||||
|
||||
# proxy
|
||||
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 验证码图片
|
||||
|
|
|
|||
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>
|
||||
<n-layout-header class="layout__header" bordered>
|
||||
<!-- <SideMenu menu-mode="horizontal" /> -->
|
||||
<log-out />
|
||||
<div class="header__logo">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SideMenu from '@/layout/components/Menu/index.vue'
|
||||
import LogOut from '../Logout/index.vue'
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent, reactive, toRefs, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
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({
|
||||
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>
|
||||
|
||||
|
|
@ -19,8 +81,11 @@ export default defineComponent({
|
|||
.layout__header {
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.header__logo{
|
||||
height: 18px;
|
||||
}
|
||||
.user_msg {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { getUser } from '@/api/user'
|
||||
import { removeToken } from '@/utils/token'
|
||||
import { userLogin, loginOut } from '@/api/login'
|
||||
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', {
|
||||
persist: true,
|
||||
|
|
@ -15,24 +19,51 @@ export const useUserStore = defineStore('user', {
|
|||
}
|
||||
},
|
||||
actions: {
|
||||
async getUserInfo() {
|
||||
/* 登录 */
|
||||
async getLoginToken(form) {
|
||||
try {
|
||||
const res = await getUser()
|
||||
const res = await userLogin(form)
|
||||
if (res.code === 0) {
|
||||
this.userInfo = res.data
|
||||
return Promise.resolve(res.msg)
|
||||
/* 设置token */
|
||||
setToken(res.data.access_token)
|
||||
this.getUserInfo()
|
||||
} else {
|
||||
return Promise.reject(res.msg)
|
||||
return Promise.reject(res)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return Promise.reject(error.msg)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
removeToken()
|
||||
this.userInfo = {}
|
||||
/* 获取用户信息 */
|
||||
async getUserInfo() {
|
||||
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 = {}) {
|
||||
this.userInfo = { ...this.userInfo, ...userInfo }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,11 +34,12 @@ export function setupInterceptor(service) {
|
|||
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
const { method } = response?.config
|
||||
const { code } = response?.data
|
||||
const { currentRoute } = router
|
||||
switch (code) {
|
||||
case 0:
|
||||
if (!response.config.hideMessage) {
|
||||
if (method !== 'get') {
|
||||
$message.success(response.data.msg)
|
||||
}
|
||||
break
|
||||
|
|
|
|||
|
|
@ -1,47 +1,68 @@
|
|||
<template>
|
||||
<div class="login_bg">
|
||||
<n-form
|
||||
ref="formRef"
|
||||
:model="loginForm"
|
||||
:rules="rules"
|
||||
label-placement="left"
|
||||
label-width="auto"
|
||||
require-mark-placement="right-hanging"
|
||||
:style="{
|
||||
maxWidth: '640px',
|
||||
backgroundColor: '#fff',
|
||||
padding: '20px',
|
||||
borderRadius: '10px'
|
||||
}"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<n-form-item label="用户名" path="username">
|
||||
<n-input v-model:value="loginForm.username" placeholder="请输入用户名" />
|
||||
</n-form-item>
|
||||
<n-form-item label="密码" path="password">
|
||||
<n-input v-model:value="loginForm.password" type="password" placeholder="请输入用户名" />
|
||||
</n-form-item>
|
||||
<n-form-item label="验证码" path="captcha">
|
||||
<n-input v-model:value="loginForm.captcha" placeholder="请输入验证码" />
|
||||
<img v-if="captcha" :src="captcha" alt="" @click="changeCode">
|
||||
</n-form-item>
|
||||
<n-form-item path="remember">
|
||||
<n-checkbox v-model:checked="loginForm.remember" size="medium" label="记住密码" />
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button @click="handleLogin">登录</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<div class="login">
|
||||
<div class="login__container">
|
||||
<div class="container__left" />
|
||||
<div class="container__right">
|
||||
<div class="right__title">汤山林场巡检管理平台</div>
|
||||
<div class="right__form">
|
||||
<n-form
|
||||
ref="formRef"
|
||||
:model="loginForm"
|
||||
:rules="rules"
|
||||
label-placement="left"
|
||||
require-mark-placement="right-hanging"
|
||||
:style="{maxWidth: '300px'}"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<n-form-item path="username">
|
||||
<n-input v-model:value="loginForm.username" placeholder="请输入用户名">
|
||||
<template #prefix>
|
||||
<img src="@/assets/icon/username.png" alt="">
|
||||
</template>
|
||||
</n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="password">
|
||||
<n-input v-model:value="loginForm.password" type="password" placeholder="请输入用户名">
|
||||
<template #prefix>
|
||||
<img src="@/assets/icon/password.png" alt="">
|
||||
</template>
|
||||
</n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="captcha">
|
||||
<n-input v-model:value="loginForm.captcha" placeholder="请输入验证码">
|
||||
<template #prefix>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userLogin, userCaptcha } from '@/api/login/index.js'
|
||||
import { setToken } from '@/utils/token'
|
||||
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 {
|
||||
name: 'LoginPage',
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
console.log(userStore)
|
||||
const data = reactive({
|
||||
loginForm: {
|
||||
username: '',
|
||||
|
|
@ -50,78 +71,140 @@ export default {
|
|||
captcha: '',
|
||||
remember: false
|
||||
},
|
||||
captcha: '',
|
||||
rules: {
|
||||
username: {
|
||||
required: true,
|
||||
trigger: ['blur', 'input'],
|
||||
message: '请输入用户名'
|
||||
},
|
||||
password: {
|
||||
required: true,
|
||||
trigger: ['blur', 'input'],
|
||||
message: '请输入密码'
|
||||
},
|
||||
captcha: {
|
||||
required: true,
|
||||
trigger: ['blur', 'input'],
|
||||
message: '请输入验证码'
|
||||
}
|
||||
username: { required: true, message: '请输入用户名', trigger: ['blur', 'input'] },
|
||||
password: { required: true, message: '请输入密码', trigger: ['blur', 'input'] },
|
||||
captcha: { required: true, message: '请输入验证码', trigger: ['blur', 'input'] }
|
||||
}
|
||||
})
|
||||
onMounted(() => {
|
||||
changeCode(data.loginForm)
|
||||
changeCode()
|
||||
data.loginForm = Object.assign(
|
||||
data.loginForm,
|
||||
JSON.parse(localStorage.getItem('login-form'))
|
||||
)
|
||||
})
|
||||
// 获取图形验证码
|
||||
const captcha = ref('')
|
||||
async function changeCode(form) {
|
||||
const params = {
|
||||
username: form.username,
|
||||
password: form.password,
|
||||
captcha: form.captcha
|
||||
}
|
||||
const captForm = await userCaptcha(params)
|
||||
captcha.value = captForm.data.captcha
|
||||
async function changeCode() {
|
||||
const captForm = await userCaptcha()
|
||||
data.captcha = captForm.data.captcha
|
||||
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 {
|
||||
...toRefs(data),
|
||||
captcha,
|
||||
formRef,
|
||||
handleLogin,
|
||||
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>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login_bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.login{
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
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()
|
||||
rolesOptions.value = dataToSelect(res.data, { label: 'name', value: 'id' })
|
||||
}
|
||||
getRolesOption()
|
||||
// getRolesOption()
|
||||
const data = [
|
||||
{
|
||||
label: '用户账号',
|
||||
|
|
|
|||
Loading…
Reference in New Issue