From 07b5dcb4905a15d0b39a51219d51c57fbd5ca4d1 Mon Sep 17 00:00:00 2001 From: hcong <1050828145@qq.com> Date: 星期四, 28 十一月 2024 13:35:04 +0800 Subject: [PATCH] 1. 新增登录页面 2. 新增登录接口 3. 新增stores/userToken.js 保存登录状态登录和退出登录 和 stores/activeCheck.js 保存登录超时和延时函数 4. components/core/Header完善退出登录点击事件 5. 新增cookie工具类 6. 新增登录工具类 --- src/stores/activeCheck.js | 29 +++ src/assets/image/pwd_icon.png | 0 src/components.d.ts | 13 - src/utils/loginUtil.js | 30 +++ src/api/loginApi.js | 29 +++ src/assets/image/login_btn.jpg | 0 src/router/index.js | 41 +++ src/views/LoginView.vue | 229 +++++++++++++++++++++++++ src/api/index.js | 15 + src/utils/cookieUtil.js | 17 + src/assets/image/background_main.png | 0 src/stores/userToken.js | 51 +++++ src/assets/image/background_img.jpg | 0 src/components/core/Header.vue | 21 ++ src/App.vue | 29 +- 15 files changed, 468 insertions(+), 36 deletions(-) diff --git a/src/App.vue b/src/App.vue index 998905c..1f2cb0b 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,20 +1,20 @@ <template> - <el-config-provider :locale="locale"> + <template v-if="$route.name == 'loginView'"> + <el-scrollbar> + <div class="el-main__content"> + <Content></Content> + </div> + </el-scrollbar> + </template> + <el-config-provider v-else :locale="locale"> <el-container class="el-container"> - <el-aside class="el-aside" - ><SiderMenu - :collapse="isCollapsed" - @nav-page="navPage" - ></SiderMenu - ></el-aside> + <el-aside class="el-aside"> + <SiderMenu :collapse="isCollapsed" @nav-page="navPage"></SiderMenu> + </el-aside> <el-container> - <el-header class="el-header" - ><Header - :navTitles="navTitles" - :collapse="isCollapsed" - @collapsed-sider="collapsedSider" - ></Header - ></el-header> + <el-header class="el-header"> + <Header :navTitles="navTitles" :collapse="isCollapsed" @collapsed-sider="collapsedSider"></Header> + </el-header> <el-main class="el-main"> <el-scrollbar> <div class="el-main__content"> @@ -87,6 +87,7 @@ /* background-color: aqua; */ /* overflow: auto; */ } + .back-top { display: flex; align-items: center; diff --git a/src/api/index.js b/src/api/index.js index 22e0559..06fc8f8 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,5 +1,8 @@ import axios from 'axios'; +import pinia from '../stores/index' import { ElMessage } from 'element-plus'; +import { useActiveCheck } from '@/stores/activeCheck'; +import { router } from '@/router/index'; const debug = false; @@ -32,12 +35,24 @@ }); $fytz.imgUrl = `${ip2_file}images/`; +const activeCheck = useActiveCheck(pinia); + +function resetLoginTime() { + if (activeCheck.isActive()) { + // 閲嶇疆鐧诲綍鏃堕檺 + activeCheck.updateLoginTime() + } +} //娣诲姞鎷︽埅鍣� [$fysp, $fytz].forEach((i) => { // 娣诲姞璇锋眰鎷︽埅鍣� i.interceptors.request.use( function (config) { // 鍦ㄥ彂閫佽姹備箣鍓嶅仛浜涗粈涔� + // 娣诲姞鐧诲綍楠岃瘉 + if (router.currentRoute._value.fullPath !== '/common/loginView') { + resetLoginTime() + } // if (import.meta.env.DEV) { // console.log('==>璇锋眰寮�濮�'); // console.log(`${config.baseURL}${config.url}`); diff --git a/src/api/loginApi.js b/src/api/loginApi.js new file mode 100644 index 0000000..edab6b3 --- /dev/null +++ b/src/api/loginApi.js @@ -0,0 +1,29 @@ +import { $fysp } from './index'; + +export default { + /** + * 鐧诲綍鎺ュ彛 + */ + login({ username, password }) { + return $fysp + .post(`/userinfo/login`, { + acountname: username, + departmentname: '', + dguid: '', + extension1: '', + extension2: '', + extension3: '', + guid: '', + isenable: true, + password: password, + realname: '', + remark: '', + telephone: '', + usertype: '', + usertypeid: '', + wechatid: '', + workno: '' + }) + .then((res) => res.data); + } +}; diff --git a/src/assets/image/background_img.jpg b/src/assets/image/background_img.jpg new file mode 100644 index 0000000..c224a19 --- /dev/null +++ b/src/assets/image/background_img.jpg Binary files differ diff --git a/src/assets/image/background_main.png b/src/assets/image/background_main.png new file mode 100644 index 0000000..09d98b9 --- /dev/null +++ b/src/assets/image/background_main.png Binary files differ diff --git a/src/assets/image/login_btn.jpg b/src/assets/image/login_btn.jpg new file mode 100644 index 0000000..43e619d --- /dev/null +++ b/src/assets/image/login_btn.jpg Binary files differ diff --git a/src/assets/image/pwd_icon.png b/src/assets/image/pwd_icon.png new file mode 100644 index 0000000..862ac8f --- /dev/null +++ b/src/assets/image/pwd_icon.png Binary files differ diff --git a/src/components.d.ts b/src/components.d.ts index d9c2fb8..53d74c1 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -20,7 +20,6 @@ ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] ElButton: typeof import('element-plus/es')['ElButton'] - ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup'] ElCalendar: typeof import('element-plus/es')['ElCalendar'] ElCard: typeof import('element-plus/es')['ElCard'] ElCascader: typeof import('element-plus/es')['ElCascader'] @@ -36,9 +35,6 @@ ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] - ElDropdown: typeof import('element-plus/es')['ElDropdown'] - ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] - ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] ElEmpty: typeof import('element-plus/es')['ElEmpty'] ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] @@ -47,20 +43,13 @@ ElImage: typeof import('element-plus/es')['ElImage'] ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] ElInput: typeof import('element-plus/es')['ElInput'] - ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElLink: typeof import('element-plus/es')['ElLink'] ElMain: typeof import('element-plus/es')['ElMain'] ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup'] ElOption: typeof import('element-plus/es')['ElOption'] - ElPageHeader: typeof import('element-plus/es')['ElPageHeader'] - ElPagination: typeof import('element-plus/es')['ElPagination'] - ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm'] ElPopover: typeof import('element-plus/es')['ElPopover'] - ElRadio: typeof import('element-plus/es')['ElRadio'] - ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] - ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRow: typeof import('element-plus/es')['ElRow'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElSegmented: typeof import('element-plus/es')['ElSegmented'] @@ -69,7 +58,6 @@ ElStep: typeof import('element-plus/es')['ElStep'] ElSteps: typeof import('element-plus/es')['ElSteps'] ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] - ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTabPane: typeof import('element-plus/es')['ElTabPane'] @@ -77,7 +65,6 @@ ElTag: typeof import('element-plus/es')['ElTag'] ElText: typeof import('element-plus/es')['ElText'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] - ElTransfer: typeof import('element-plus/es')['ElTransfer'] ElTree: typeof import('element-plus/es')['ElTree'] ElUpload: typeof import('element-plus/es')['ElUpload'] Footer: typeof import('./components/core/Footer.vue')['default'] diff --git a/src/components/core/Header.vue b/src/components/core/Header.vue index 3c742e0..8129a04 100644 --- a/src/components/core/Header.vue +++ b/src/components/core/Header.vue @@ -10,13 +10,21 @@ </el-col> <el-col :span="12" class="logout"> <FYBgTaskDialog></FYBgTaskDialog> - <el-button icon="SwitchButton">閫�鍑虹櫥褰�</el-button> + <el-button icon="SwitchButton" @click="logout">閫�鍑虹櫥褰�</el-button> </el-col> </el-row> </template> <script> +import { useUserStore } from '@/stores/userToken' +import { useRouter } from 'vue-router'; +import { ElNotification } from 'element-plus'; export default { + setup() { + const userStore = useUserStore() + const router = useRouter() + return { userStore, router } + }, name: 'CoreHeader', props: { collapse: { @@ -49,6 +57,17 @@ collapsedSider() { this.isCollapsed = !this.isCollapsed; this.$emit('collapsedSider', this.isCollapsed); + }, + logout() { + this.userStore.logout() + this.router.push('/common/loginView') + ElNotification({ + title: `閫�鍑烘垚鍔焋, + message: `閫�鍑烘垚鍔焋, + type: 'success', + // offset: 170, + position: 'bottom-left', + }); } } }; diff --git a/src/router/index.js b/src/router/index.js index 6a36171..4b29d64 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -2,7 +2,9 @@ import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router' import pinia from '../stores/index' import { useLoadingStore } from '../stores/loadingStore' - +import { useActiveCheck } from '@/stores/activeCheck'; +import { useUserStore } from '@/stores/userToken'; +import loginUtil from '../utils/loginUtil'; const routes = [ // { // //鏁存敼瀹℃牳 @@ -208,19 +210,42 @@ name: 'docTest', path: '/common/docTest', component: () => import('@/views/DocTest.vue') + }, + { + //鐧婚檰 + name: 'loginView', + path: '/common/loginView', + component: () => import('@/views/LoginView.vue') } -] +]; const router = createRouter({ // history: createWebHistory(import.meta.env.BASE_URL) history: createWebHashHistory(), routes: routes -}) +}); -const loadingStore = useLoadingStore(pinia) +const loadingStore = useLoadingStore(pinia); +const activeCheck = useActiveCheck(pinia); +const userStore = useUserStore(pinia); // eslint-disable-next-line no-unused-vars router.afterEach((to, from) => { - loadingStore.clearLoading() -}) - -export { router, routes } + loadingStore.clearLoading(); +}); +function loginJudge() { + // 濡傛灉鏄湭鐧诲綍 灏濊瘯浠巆ookie鐧诲綍 + if (!userStore.isLoggedIn()) { + loginUtil.loginFromCookie(); + } + // 濡傛灉鐧诲綍瓒呮椂 璺宠浆鍒扮櫥褰曢〉闈� + if (!activeCheck.isActive()) { + router.push('/common/loginView'); + } +} +router.beforeEach((to, from) => { + // 娣诲姞鐧诲綍楠岃瘉 + if (to.fullPath !== '/common/loginView') { + loginJudge(); + } +}); +export { router, routes }; diff --git a/src/stores/activeCheck.js b/src/stores/activeCheck.js new file mode 100644 index 0000000..1d6fd43 --- /dev/null +++ b/src/stores/activeCheck.js @@ -0,0 +1,29 @@ +import { defineStore } from 'pinia'; +// 鐧诲綍鏃堕檺 鍗曚綅涓簃in +const maxActiveTime = 15; +export const useActiveCheck = defineStore('activeCheck', { + state: () => ({ + loginTime: null + }), + actions: { + // 鏇存柊鐧婚檰鏃堕棿涓哄綋鍓嶆椂闂� + updateLoginTime() { + this.loginTime = new Date(); + }, + // 鍒ゆ柇鐧婚檰鏄惁瓒呮椂 + isActive() { + if (this.loginTime == null) { + return false; + } + const now = new Date(); + // 鏈�鏃╃殑鏈夋晥鏃堕棿 + const earliestActiveTime = now.getTime() - maxActiveTime * 60 * 1000; + const currLoginTime = this.loginTime.getTime(); + return currLoginTime > earliestActiveTime; + }, + clearLoginTime() { + this.loginTime = null; + } + }, + getters: {} +}); diff --git a/src/stores/userToken.js b/src/stores/userToken.js new file mode 100644 index 0000000..58fcb4b --- /dev/null +++ b/src/stores/userToken.js @@ -0,0 +1,51 @@ +import loginApi from '@/api/loginApi.js'; +import loginUtil from '@/utils/loginUtil.js'; +import pinia from './index'; +import { defineStore } from 'pinia'; +import { useActiveCheck } from './activeCheck'; +export const useUserStore = defineStore('userToken', { + state: () => ({ + user: null + }), + actions: { + login({ username, password, user = null }, onSuccess, onError) { + // 閫氳繃浼犻�� user 瀵硅薄鐧诲綍 + if (user && user != null && user != {}) { + loginUtil.addUserCookie(user); + this.user = user; + const activeCheck = useActiveCheck(pinia); + activeCheck.updateLoginTime(); + onSuccess && onSuccess(); + return; + } + // 閫氳繃浼犻�� 鐢ㄦ埛鍚嶅瘑鐮� 鐧诲綍 + loginApi + .login({ username, password }) + .then((res) => { + if (res.guid != 'null') { + loginUtil.addUserCookie(res); + this.user = res; + const activeCheck = useActiveCheck(pinia); + activeCheck.updateLoginTime(); + onSuccess(); + } else { + onError(); + } + }) + .catch((error) => { + onError(); + throw error; + }); + }, + logout() { + this.user = null; + const activeCheck = useActiveCheck(pinia); + activeCheck.clearLoginTime(); + loginUtil.deleteUserCookie(); + }, + isLoggedIn() { + return this.user != null; + } + }, + getters: {} +}); diff --git a/src/utils/cookieUtil.js b/src/utils/cookieUtil.js new file mode 100644 index 0000000..d40baf0 --- /dev/null +++ b/src/utils/cookieUtil.js @@ -0,0 +1,17 @@ +export default { + + //璇籆ookie + getCookie(objName) { + //鑾峰彇鎸囧畾鍚嶇О鐨刢ookie鐨勫�� + var arrStr = document.cookie.split(';'); + for (var i = 0; i < arrStr.length; i++) { + var temp = arrStr[i].split('='); + if (temp[0].trim() == objName) return unescape(temp[1]); //瑙g爜 + } + return ''; + }, + // 鍒犻櫎 + deleteCookie(name) { + document.cookie = name + '=' + '1;expires=Thu, 01 Jan 1970 00:00:00 GMT'; + } +}; diff --git a/src/utils/loginUtil.js b/src/utils/loginUtil.js new file mode 100644 index 0000000..3713b09 --- /dev/null +++ b/src/utils/loginUtil.js @@ -0,0 +1,30 @@ +import cookieUtil from './cookieUtil'; +import pinia from '../stores/index'; +import { useUserStore } from '../stores/userToken'; +export default { + // 娣诲姞cookie涓敤鎴风櫥褰曚俊鎭� + addUserCookie(user) { + /**娣诲姞璁剧疆cookie**/ + let userObj = 'user=' + escape(JSON.stringify(user)); + var expires = new Date(); + expires.setTime(expires.getTime() + 0.5 * 24 * 60 * 60 * 1000); + //path=/锛岃〃绀篶ookie鑳藉湪鏁翠釜缃戠珯涓嬩娇鐢紝path=/temp锛岃〃绀篶ookie鍙兘鍦╰emp鐩綍涓嬩娇鐢� + // path = ';path=/html' + //GMT(Greenwich Mean Time)鏄牸鏋楀凹娌诲钩鏃讹紝鐜板湪鐨勬爣鍑嗘椂闂达紝鍗忚皟涓栫晫鏃舵槸UTC + //鍙傛暟days鍙兘鏄暟瀛楀瀷 + var _expires = ';expires=' + expires.toGMTString(); + document.cookie = userObj + _expires; + }, + // 鍒犻櫎cookie涓敤鎴风櫥褰曚俊鎭� + deleteUserCookie() { + cookieUtil.deleteCookie('user') + }, + // 浠巆ookie鐧诲綍 + loginFromCookie() { + const userStore = useUserStore(pinia); + const userCookie = cookieUtil.getCookie('user'); + if (userCookie && userCookie != undefined && userCookie != {}) { + userStore.login({ user: JSON.parse(userCookie) }); + } + } +}; diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue new file mode 100644 index 0000000..2e06649 --- /dev/null +++ b/src/views/LoginView.vue @@ -0,0 +1,229 @@ +<template> + <div class="container"> + <div class="header"> + <h1 class="header-top">鐢熸�佺幆澧冪嚎涓婄洃绠″ぇ鏁版嵁绠$悊骞冲彴</h1> + </div> + <div class="main"> + <div class="main-login"> + <div class="width100 main-top-p"><span>鐢ㄦ埛鐧诲綍</span></div> + <el-form :model="formObj" :rules="rules" ref="formRef"> + <el-form-item prop="username"> + <el-input v-model="formObj.username" placeholder="璇疯緭鍏ョ敤鎴峰悕" class="user-input"></el-input> + </el-form-item> + <el-form-item prop="password"> + <el-input v-model="formObj.password" placeholder="璇疯緭鍏ュ瘑鐮�" type='password' + class="user-input"></el-input> + </el-form-item> + </el-form> + <div class="width100 media-footer"> + <label class="pwd-label"> + <input type="checkbox" class="check-pwd" />璁颁綇瀵嗙爜 + </label> + <span class="change-pwd">蹇樿瀵嗙爜锛�</span> + </div> + <div class="footer width100"> + <el-button class="login-btn u-loginBtn" @click="onSubmit(false)">鐧诲綍</el-button> + </div> + </div> + </div> + </div> +</template> +<script setup> +import { useFormConfirm } from '@/composables/formConfirm'; +import { reactive } from 'vue'; +import { useRouter } from 'vue-router'; +import pinia from '@/stores/index'; +import { useUserStore } from '@/stores/userToken' +import { ElNotification } from 'element-plus'; + +const router = useRouter(); +const rules = reactive({ + username: [ + { + required: true, + message: '鐢ㄦ埛鍚嶄笉鑳戒负绌�', + trigger: 'change' + } + ], + password: [ + { + required: true, + message: '瀵嗙爜涓嶈兘涓虹┖', + trigger: 'change' + } + ], +}) +const userStore = useUserStore(pinia); +function login() { + userStore.login({ username: formObj.value.username, password: formObj.value.password }, () => { + router.push('/fysp/procheck') + ElNotification({ + title: `鐧诲綍鎴愬姛`, + message: `鐧诲綍鎴愬姛`, + type: 'success', + // offset: 170, + position: 'bottom-left', + }); + }, () => { + router.push('/common/loginView') + ElNotification({ + title: `鐧诲綍澶辫触`, + message: `鐢ㄦ埛鍚嶆垨瀵嗙爜閿欒`, + type: 'error', + // offset: 170, + position: 'bottom-left', + }); + }) +} + +//琛ㄥ崟鎿嶄綔鍑芥暟 +const { formObj, formRef, onSubmit } = + useFormConfirm({ + submit: { + do: login + } + }); +</script> +<style scoped> +html { + font-size: 62.5%; +} + +body { + margin: 0 !important; +} + +.container { + width: 100vw; + height: 100vh; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + background-image: url(@/assets/image/background_img.jpg); +} + +.header { + margin-top: 5rem; +} + +.header-top { + color: #fefffd; + font-size: 5rem; + letter-spacing: 0.5rem; +} + +.main { + width: 30rem; + height: 29rem; + opacity: 0.8; + background-color: #092367; + background-image: url(@/assets/image/background_main.png); + background-size: 100%; +} + +.main-top-p { + text-align: center; + font-size: 2rem; + color: white; + letter-spacing: 0.5rem; +} + +.background-img { + width: 100%; + height: 100%; + +} + +.main-login { + padding: 5rem; + padding-bottom: 0.5rem; + display: flex; + flex-direction: column; + align-items: center; +} + +.width100 { + width: 100%; + margin: 1rem 0; +} + +.media-footer { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 1rem; + margin-top: 3rem; +} + +.pwd-label { + color: white; +} + +.user-input::-webkit-input-placeholder { + color: white; + font-size: 2rem; + line-height: 2rem; +} + +.user-input { + /* margin-top: 2rem; */ + border: 2px solid #046eb0; + border-radius: 8px; + width: 100%; + font-size: 1rem; + color: white; + /* padding: 0.8rem 0 0 4rem; */ + background-color: #03286a; + +} + +.check-pwd::before { + border: 1px solid #0270ae; + background-color: #042866; + +} + +.change-pwd { + color: #00aded; + +} + +.login-btn { + width: 100%; + padding: 0.8rem; + font-size: 1rem; + border-radius: 9px; + color: white; + background-image: url(@/assets/image/login_btn.jpg); + background-size: 100%; + opacity: 1; + border: none; + /* margin-top: 3rem; */ +} + +.icon-user { + background-image: url(@/assets/image/user_icon.png); + + + + background-repeat: no-repeat; + /*璁剧疆鍥剧墖涓嶉噸澶�*/ + + height: 100%; + +} + +.icon-pwd { + background-image: url(@/assets/image/pwd_icon.png); + + background-repeat: no-repeat; + /*璁剧疆鍥剧墖涓嶉噸澶�*/ + + height: 80%; +} + +::v-deep .el-input__inner { + width: 300px; +} +</style> \ No newline at end of file -- Gitblit v1.9.3