wangqiujuan0808 2024-03-25 23:18:43 +08:00
parent d86054b091
commit ed8381de51
9 changed files with 2720 additions and 1124 deletions

3609
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,4 +21,4 @@
"sass": "^1.72.0", "sass": "^1.72.0",
"vite": "^5.1.6" "vite": "^5.1.6"
} }
} }

View File

@ -1,11 +1,62 @@
<template> <template>
<div class="common-layout"> <div class="common-layout">
<el-container> <el-container>
<el-header>INNcom 清单生成器</el-header> <el-header v-if="state.showMenu">INNcom </el-header>
<el-main><router-view /></el-main> <el-main><router-view /></el-main>
</el-container> </el-container>
</div> </div>
</template> </template>
<script>
import { onUnmounted, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { pathMap, localGet } from '@/utils';
export default {
name: 'App',
setup() {
const noMenu = ['/login']
const router = useRouter();
const state = reactive({
showMenu: true,
currentPath: '/dashboard'
})
// 退
if (window.history && window.history.pushState) {
history.pushState(null, null, document.URL);
window.addEventListener('popstate', () => {
if (!localGet('token')) {
state.showMenu = false
}
}, false);
}
const unwatch = router.beforeEach((to, from, next) => {
if (to.path == '/login') {
// /login
next()
} else {
// /login token
if (!localGet('token')) {
//
next({ path: '/login' })
} else {
//
next()
}
}
state.showMenu = !noMenu.includes(to.path)
state.currentPath = to.path
document.title = pathMap[to.name]
})
onUnmounted(() => {
unwatch();
})
return {
state
}
}
}
</script>
<style scoped> <style scoped>
.logo { .logo {

View File

@ -1,11 +1,13 @@
import axios from "axios"; import axios from "axios";
import { pathMap, localGet, localRemove } from '@/utils';
axios.defaults.withCredentials = true; axios.defaults.withCredentials = true;
import { ElMessage } from "element-plus";
import router from "@/router/index";
// 请求头headers 信息 // 请求头headers 信息
axios.defaults.headers["X-Requested-With"] = "XMLHttpRequest"; axios.defaults.headers["X-Requested-With"] = "XMLHttpRequest";
axios.defaults.headers = { axios.defaults.headers = {
name: "cc", name: localGet('name'),
token: "", token: localGet('token'),
}; };
@ -13,6 +15,14 @@ axios.defaults.headers = {
// 请求拦截器,内部根据返回值,重新组装,统一管理。 // 请求拦截器,内部根据返回值,重新组装,统一管理。
axios.interceptors.response.use( axios.interceptors.response.use(
(res) => { (res) => {
console.log(111,res);
if(res.data.code === 2001) {
ElMessage.error(res.data.message || "401 Unauthorized");
router.push({ path: "/login" });
localRemove('token');
localRemove('name');
return;
}
return res.data; return res.data;
}, },
(error) => { (error) => {

View File

@ -2,6 +2,7 @@ import { createRouter, createWebHashHistory } from 'vue-router';
import Home from '../views/Home.vue'; import Home from '../views/Home.vue';
import Project from '../views/Project.vue'; import Project from '../views/Project.vue';
import Room from '../views/Room.vue'; import Room from '../views/Room.vue';
import Login from '../views/Login.vue';
const routes = [ const routes = [
{ {
@ -13,6 +14,11 @@ const routes = [
name: 'home', name: 'home',
component: Home component: Home
}, },
{
path: '/login',
name: 'login',
component: Login
},
{ {
path: '/project', path: '/project',
name: 'project', name: 'project',

24
src/utils/index.js Normal file
View File

@ -0,0 +1,24 @@
export function localGet(key) {
const value = window.localStorage.getItem(key)
try {
return JSON.parse(window.localStorage.getItem(key))
} catch (error) {
return value
}
}
export function localSet(key, value) {
window.localStorage.setItem(key, JSON.stringify(value))
}
export function localRemove(key) {
window.localStorage.removeItem(key)
}
export const pathMap = {
login: '登录',
home: 'INNcom 清单生成器',
project: 'INNcom 清单生成器',
room: 'INNcom 清单生成器',
weather: 'INNcom 清单生成器',
}

View File

@ -198,7 +198,7 @@ onMounted(() => {
height: 800px; height: 800px;
overflow: auto; overflow: auto;
::v-deep>.el-card__body { :deep(>.el-card__body) {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;

126
src/views/Login.vue Normal file
View File

@ -0,0 +1,126 @@
<template>
<div class="login-body">
<div class="login-container">
<div class="head">
<div class="name">
<div class="title">登录</div>
</div>
</div>
<el-form label-position="top" :rules="rules" :model="ruleForm" ref="loginForm" class="login-form">
<el-form-item label="账号" prop="username">
<el-input type="text" v-model.trim="ruleForm.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" v-model.trim="ruleForm.password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button style="width: 100%" type="primary" @click="submitForm"></el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { reactive, ref, toRefs } from 'vue'
import myApi from "@/api/myApi.js";
import { localSet } from '@/utils';
// const bcryptjs = require('bcryptjs')
import bcrypt from 'bcryptjs'
export default {
name: 'Login',
setup() {
const loginForm = ref(null)
const state = reactive({
ruleForm: {
username: '',
password: ''
},
checked: true,
rules: {
username: [
{ required: 'true', message: '账户不能为空', trigger: 'blur' }
],
password: [
{ required: 'true', message: '密码不能为空', trigger: 'blur' }
]
}
})
const submitForm = async () => {
loginForm.value.validate(async (valid) => {
if (valid) {
// const res = await myApi.login(param);
const encryptedPassword = await bcrypt.hash(state.ruleForm.password, 10);
localSet('name', state.ruleForm.username);
localSet('token', encryptedPassword);
window.location.href = '/';
} else {
console.log('error submit!!')
return false;
}
})
}
const resetForm = () => {
loginForm.value.resetFields();
}
return {
...toRefs(state),
loginForm,
submitForm,
resetForm
}
}
}
</script>
<style scoped>
.login-body {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
/* background-color: #fff; */
/* background-image: linear-gradient(25deg, #077f7c, #3aa693, #5ecfaa, #7ffac2); */
/* background: url('../assets/bg.png') no-repeat; */
background-size: 100% 100%;
}
.login-container {
width: 420px;
height: 400px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0px 21px 41px 0px rgba(0, 0, 0, 0.2);
}
.head {
display: flex;
justify-content: center;
align-items: center;
padding: 40px 0 20px 0;
}
.head img {
width: 100px;
height: 100px;
margin-right: 20px;
}
.head .title {
font-size: 28px;
color: #1BAEAE;
font-weight: bold;
}
.head .tips {
font-size: 12px;
color: #999;
}
.login-form {
width: 70%;
margin: 0 auto;
}
</style>
<style>
.el-form--label-top .el-form-item__label {
padding: 0;
}
.login-form .el-form-item {
margin-bottom: 20px;
}
</style>

View File

@ -366,7 +366,7 @@ const checkList = reactive([{
>div:first-child { >div:first-child {
width: 20%; width: 20%;
::v-deep .el-card__body { :deep(.el-card__body) {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 95%; height: 95%;
@ -408,7 +408,7 @@ const checkList = reactive([{
>div:nth-child(2) { >div:nth-child(2) {
width: 79%; width: 79%;
::v-deep .el-card__body { :deep(.el-card__body) {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 95%; height: 95%;
@ -507,7 +507,7 @@ const checkList = reactive([{
padding: 5px; padding: 5px;
} }
::v-deep .el-textarea__inner { :deep(.el-textarea__inner) {
height: 80px !important; height: 80px !important;
} }