update
This commit is contained in:
parent
83cca9fbd1
commit
be7002e3d6
@ -12,4 +12,18 @@ export default [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
url: "/api/userinfo",
|
||||||
|
method: "get",
|
||||||
|
timeout: 100,
|
||||||
|
response: {
|
||||||
|
code: 200,
|
||||||
|
message: "获取用户信息成功",
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
userName: 'admin',
|
||||||
|
avatar: "@image('48x48', '#fb0a2a')"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
8
src/api/app.js
Normal file
8
src/api/app.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
// 获取用户信息
|
||||||
|
export const GetUserinfo = () => {
|
||||||
|
return request({
|
||||||
|
url: "/api/userinfo",
|
||||||
|
method: "get"
|
||||||
|
});
|
||||||
|
};
|
||||||
126
src/layout/components/Sidebar/index.vue
Normal file
126
src/layout/components/Sidebar/index.vue
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="left"
|
||||||
|
:class="{collapse:collapse}"
|
||||||
|
>
|
||||||
|
<div class="brand">
|
||||||
|
<img
|
||||||
|
class="logo"
|
||||||
|
src="~@/assets/logo.png"
|
||||||
|
>
|
||||||
|
<div class="title">ERP管理系统</div>
|
||||||
|
</div>
|
||||||
|
<el-menu
|
||||||
|
class="menu"
|
||||||
|
:collapse="collapse"
|
||||||
|
:uniqueOpened="true"
|
||||||
|
default-active="2"
|
||||||
|
background-color="#2d3a4b"
|
||||||
|
text-color="#fff"
|
||||||
|
active-text-color="#fff"
|
||||||
|
>
|
||||||
|
<el-submenu index="1">
|
||||||
|
<template #title>
|
||||||
|
<i class="el-icon-location"></i>
|
||||||
|
<span>导航一</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item-group>
|
||||||
|
<template #title>分组一</template>
|
||||||
|
<el-menu-item index="1-1">选项1</el-menu-item>
|
||||||
|
<el-menu-item index="1-2">选项2</el-menu-item>
|
||||||
|
</el-menu-item-group>
|
||||||
|
<el-menu-item-group title="分组2">
|
||||||
|
<el-menu-item index="1-3">选项3</el-menu-item>
|
||||||
|
</el-menu-item-group>
|
||||||
|
<el-submenu index="1-4">
|
||||||
|
<template #title>选项4</template>
|
||||||
|
<el-menu-item index="1-4-1">选项1</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
|
</el-submenu>
|
||||||
|
<el-menu-item index="2">
|
||||||
|
<i class="el-icon-menu"></i>
|
||||||
|
<template #title>导航二</template>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item
|
||||||
|
index="3"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<i class="el-icon-document"></i>
|
||||||
|
<template #title>导航三</template>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="4">
|
||||||
|
<i class="el-icon-setting"></i>
|
||||||
|
<template #title>导航四</template>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-submenu index="5">
|
||||||
|
<template #title>
|
||||||
|
<i class="el-icon-location"></i>
|
||||||
|
<span>导航一</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item-group>
|
||||||
|
<template #title>分组一</template>
|
||||||
|
<el-menu-item index="5-1">选项1</el-menu-item>
|
||||||
|
<el-menu-item index="5-2">选项2</el-menu-item>
|
||||||
|
</el-menu-item-group>
|
||||||
|
<el-menu-item-group title="分组2">
|
||||||
|
<el-menu-item index="5-3">选项3</el-menu-item>
|
||||||
|
</el-menu-item-group>
|
||||||
|
</el-submenu>
|
||||||
|
</el-menu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, computed } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const store = useStore();
|
||||||
|
const collapse = computed(() => !!store.state.app.sidebar.collapse);
|
||||||
|
|
||||||
|
return {
|
||||||
|
collapse,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.left {
|
||||||
|
width: 210px;
|
||||||
|
background: #2d3a4b;
|
||||||
|
transition: all 0.3s;
|
||||||
|
overflow: hidden;
|
||||||
|
&.collapse {
|
||||||
|
width: 64px;
|
||||||
|
.brand .title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.brand {
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.logo {
|
||||||
|
max-width: 32px;
|
||||||
|
max-height: 32px;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-left: 8px;
|
||||||
|
transition: all 0.5s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.menu {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
::v-deep(.el-menu-item.is-active) {
|
||||||
|
background: #0174df !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
9
src/layout/components/Tabsbar/index.vue
Normal file
9
src/layout/components/Tabsbar/index.vue
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tabs"></div>
|
||||||
|
</template>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.tabs {
|
||||||
|
height: 32px;
|
||||||
|
border-bottom: 1px solid #eaeaea;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
68
src/layout/components/Topbar/Breadcrumbs.vue
Normal file
68
src/layout/components/Topbar/Breadcrumbs.vue
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<el-breadcrumb
|
||||||
|
separator="/"
|
||||||
|
class="breadcrumb"
|
||||||
|
>
|
||||||
|
<el-breadcrumb-item
|
||||||
|
v-for="(item, index) in breadcrumbs"
|
||||||
|
:key="item.path"
|
||||||
|
:class="{no_link: index === breadcrumbs.length - 1}"
|
||||||
|
:to="index < breadcrumbs.length - 1 ? item.path : ''"
|
||||||
|
>
|
||||||
|
{{item.meta.title}}
|
||||||
|
</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { defineComponent, ref, onBeforeMount } from "vue";
|
||||||
|
import { useRoute, useRouter, onBeforeRouteUpdate } from "vue-router";
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const routes = router.getRoutes();
|
||||||
|
const breadcrumbs = ref([]);
|
||||||
|
|
||||||
|
const getBreadcrumbs = (route) => {
|
||||||
|
const res = [{ path: "/", meta: { title: "首页" } }];
|
||||||
|
const { parentBreadcrumb } = route.meta;
|
||||||
|
if (!!parentBreadcrumb) {
|
||||||
|
const parents = routes.filter((item) =>
|
||||||
|
parentBreadcrumb.includes(item.name)
|
||||||
|
);
|
||||||
|
res.push(...parents);
|
||||||
|
}
|
||||||
|
if (route.name !== "home") res.push(route);
|
||||||
|
breadcrumbs.value = res;
|
||||||
|
};
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
getBreadcrumbs(route);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeRouteUpdate((to) => {
|
||||||
|
getBreadcrumbs(to);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
breadcrumbs,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.breadcrumb {
|
||||||
|
margin-left: 10px;
|
||||||
|
::v-deep(a),
|
||||||
|
::v-deep(.is-link) {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no_link {
|
||||||
|
::v-deep(.el-breadcrumb__inner) {
|
||||||
|
color: #97a8be !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
38
src/layout/components/Topbar/Hamburger.vue
Normal file
38
src/layout/components/Topbar/Hamburger.vue
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<i
|
||||||
|
class="fold-btn el-icon-s-fold"
|
||||||
|
:class="{collapse:collapse}"
|
||||||
|
@click="handleToggleMenu"
|
||||||
|
></i>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { defineComponent, computed } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const store = useStore();
|
||||||
|
const collapse = computed(() => !!store.state.app.sidebar.collapse);
|
||||||
|
const handleToggleMenu = () => {
|
||||||
|
store.commit("app/setCollapse", +!collapse.value);
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
collapse,
|
||||||
|
handleToggleMenu,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.fold-btn {
|
||||||
|
line-height: 48px;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
&.collapse {
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
65
src/layout/components/Topbar/Userinfo.vue
Normal file
65
src/layout/components/Topbar/Userinfo.vue
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<el-dropdown>
|
||||||
|
<div class="userinfo">
|
||||||
|
<template v-if="!userinfo">
|
||||||
|
<i class="el-icon-user" /> admin
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<img
|
||||||
|
class="avatar"
|
||||||
|
:src="userinfo.avatar"
|
||||||
|
/> {{userinfo.userName}}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||||
|
<el-dropdown-item>修改密码</el-dropdown-item>
|
||||||
|
<el-dropdown-item>锁定屏幕</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { computed, defineComponent } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const store = useStore();
|
||||||
|
const router = useRouter();
|
||||||
|
const userinfo = computed(() => store.state.app.userinfo);
|
||||||
|
const logout = () => {
|
||||||
|
store.commit("app/clearToken");
|
||||||
|
router.push("/login");
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
userinfo,
|
||||||
|
logout,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.userinfo {
|
||||||
|
padding: 0 16px;
|
||||||
|
line-height: 48px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
&:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
.el-icon-user {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
margin-right: 8px;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
37
src/layout/components/Topbar/index.vue
Normal file
37
src/layout/components/Topbar/index.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div class="header">
|
||||||
|
<div class="navigation">
|
||||||
|
<hamburger />
|
||||||
|
<breadcrumbs />
|
||||||
|
</div>
|
||||||
|
<div class="action">
|
||||||
|
<userinfo />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import Hamburger from "./Hamburger.vue";
|
||||||
|
import Breadcrumbs from "./Breadcrumbs.vue";
|
||||||
|
import Userinfo from "./Userinfo.vue";
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
Hamburger,
|
||||||
|
Breadcrumbs,
|
||||||
|
Userinfo,
|
||||||
|
},
|
||||||
|
setup() {},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.header {
|
||||||
|
height: 48px;
|
||||||
|
border-bottom: 1px solid #eaeaea;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
.navigation {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
63
src/layout/index.vue
Normal file
63
src/layout/index.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wrapper">
|
||||||
|
<sidebar />
|
||||||
|
<div class="right">
|
||||||
|
<div class="top">
|
||||||
|
<topbar />
|
||||||
|
<tabsbar />
|
||||||
|
</div>
|
||||||
|
<div class="main">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { defineComponent, onBeforeMount } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
import Sidebar from "./components/Sidebar/index.vue";
|
||||||
|
import Topbar from "./components/Topbar/index.vue";
|
||||||
|
import Tabsbar from "./components/Tabsbar/index.vue";
|
||||||
|
import { GetUserinfo } from "@/api/app";
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
Sidebar,
|
||||||
|
Topbar,
|
||||||
|
Tabsbar,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const store = useStore();
|
||||||
|
const getUserinfo = async () => {
|
||||||
|
const { code, data } = await GetUserinfo();
|
||||||
|
if (+code === 200) {
|
||||||
|
store.commit("app/setUserinfo", data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onBeforeMount(() => {
|
||||||
|
getUserinfo();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.right {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.top {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
flex: 1;
|
||||||
|
background: #f0f2f5;
|
||||||
|
padding: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,18 +1,24 @@
|
|||||||
// index.js
|
// index.js
|
||||||
import { createRouter, createWebHistory } from "vue-router"
|
import { createRouter, createWebHashHistory } from "vue-router"
|
||||||
import home from './modules/home'
|
import layout from '@/layout/index.vue'
|
||||||
import login from './modules/login'
|
import login from './modules/login'
|
||||||
|
import home from './modules/home'
|
||||||
|
import user from './modules/user'
|
||||||
|
|
||||||
import { TOKEN } from '@/store/modules/user'
|
import { TOKEN } from '@/store/modules/app'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
redirect: '/home'
|
component: layout,
|
||||||
|
redirect: '/home',
|
||||||
|
children: [
|
||||||
|
...home,
|
||||||
|
...user
|
||||||
|
]
|
||||||
},
|
},
|
||||||
...home,
|
|
||||||
...login
|
...login
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,5 +6,8 @@ export default [
|
|||||||
path: "/home",
|
path: "/home",
|
||||||
name: "home",
|
name: "home",
|
||||||
component: Home,
|
component: Home,
|
||||||
|
meta: {
|
||||||
|
title: "首页",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
23
src/router/modules/user.js
Normal file
23
src/router/modules/user.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
const User = () => import("@/views/user/index.vue");
|
||||||
|
const AddUser = () => import("@/views/user/AddUser.vue");
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
path: "/user",
|
||||||
|
name: "user",
|
||||||
|
component: User,
|
||||||
|
meta: {
|
||||||
|
title: "用户管理",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/user/add",
|
||||||
|
name: "addUser",
|
||||||
|
component: AddUser,
|
||||||
|
meta: {
|
||||||
|
title: "添加用户",
|
||||||
|
parentBreadcrumb: ["user"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1,9 +1,9 @@
|
|||||||
//index.js
|
//index.js
|
||||||
import { createStore } from "vuex";
|
import { createStore } from "vuex";
|
||||||
import user from "./modules/user";
|
import app from "./modules/app";
|
||||||
|
|
||||||
export default createStore({
|
export default createStore({
|
||||||
modules: {
|
modules: {
|
||||||
user,
|
app
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -5,6 +5,10 @@ export default {
|
|||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {
|
state: {
|
||||||
authorization: getItem(TOKEN),
|
authorization: getItem(TOKEN),
|
||||||
|
sidebar: {
|
||||||
|
collapse: getItem('collapse')
|
||||||
|
},
|
||||||
|
userinfo: null
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setToken (state, data) {
|
setToken (state, data) {
|
||||||
@ -17,6 +21,21 @@ export default {
|
|||||||
// 保存到localStorage
|
// 保存到localStorage
|
||||||
removeItem(TOKEN);
|
removeItem(TOKEN);
|
||||||
},
|
},
|
||||||
|
setCollapse (state, data) {
|
||||||
|
state.sidebar.collapse = data;
|
||||||
|
// 保存到localStorage
|
||||||
|
setItem('collapse', data);
|
||||||
|
},
|
||||||
|
clearCollapse (state) {
|
||||||
|
state.sidebar.collapse = '';
|
||||||
|
// 保存到localStorage
|
||||||
|
removeItem('collapse');
|
||||||
|
},
|
||||||
|
setUserinfo (state, data) {
|
||||||
|
state.userinfo = data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
|
||||||
},
|
},
|
||||||
actions: {},
|
|
||||||
};
|
};
|
||||||
@ -12,7 +12,7 @@ const service = axios.create({
|
|||||||
// 拦截请求
|
// 拦截请求
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
const authorization = store.state.user;
|
const authorization = store.state.app;
|
||||||
if (authorization) {
|
if (authorization) {
|
||||||
config.headers.Authorization = `Bearer ${authorization.token}`;
|
config.headers.Authorization = `Bearer ${authorization.token}`;
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ service.interceptors.response.use(
|
|||||||
// 响应拦截器中的 error 就是那个响应的错误对象
|
// 响应拦截器中的 error 就是那个响应的错误对象
|
||||||
if (error.response && error.response.status === 401) {
|
if (error.response && error.response.status === 401) {
|
||||||
// 校验是否有 refresh_token
|
// 校验是否有 refresh_token
|
||||||
const { authorization } = store.state;
|
const { authorization } = store.state.app;
|
||||||
if (!authorization || !authorization.refresh_token) {
|
if (!authorization || !authorization.refresh_token) {
|
||||||
router.push("/login");
|
router.push("/login");
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ service.interceptors.response.use(
|
|||||||
});
|
});
|
||||||
// 如果获取成功,则把新的 token 更新到容器中
|
// 如果获取成功,则把新的 token 更新到容器中
|
||||||
// console.log('刷新 token 成功', res)
|
// console.log('刷新 token 成功', res)
|
||||||
store.commit("setToken", {
|
store.commit("app/setToken", {
|
||||||
token: res.data.data.token, // 最新获取的可用 token
|
token: res.data.data.token, // 最新获取的可用 token
|
||||||
refresh_token: authorization.refresh_token, // 还是原来的 refresh_token
|
refresh_token: authorization.refresh_token, // 还是原来的 refresh_token
|
||||||
});
|
});
|
||||||
@ -67,7 +67,7 @@ service.interceptors.response.use(
|
|||||||
// console.log('请求刷线 token 失败', err)
|
// console.log('请求刷线 token 失败', err)
|
||||||
router.push("/login");
|
router.push("/login");
|
||||||
// 清除token
|
// 清除token
|
||||||
store.commit("clearToken")
|
store.commit("app/clearToken")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,7 @@ export default defineComponent({
|
|||||||
router.push(!!targetPath ? targetPath : "/");
|
router.push(!!targetPath ? targetPath : "/");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
store.commit("user/setToken", data);
|
store.commit("app/setToken", data);
|
||||||
} else {
|
} else {
|
||||||
ctx.$message.error(message);
|
ctx.$message.error(message);
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/views/user/AddUser.vue
Normal file
3
src/views/user/AddUser.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
addUser
|
||||||
|
</template>
|
||||||
3
src/views/user/index.vue
Normal file
3
src/views/user/index.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
user
|
||||||
|
</template>
|
||||||
27
项目开发手册.md
27
项目开发手册.md
@ -32,7 +32,6 @@ Vue3 + Element-plus + Vite
|
|||||||
|
|
||||||
> 我们将使用vite和vue3手动进行前端项目架构。为方便大家从vue2过渡到vue3,本项目我们先不使用ts。
|
> 我们将使用vite和vue3手动进行前端项目架构。为方便大家从vue2过渡到vue3,本项目我们先不使用ts。
|
||||||
>
|
>
|
||||||
> ts项目我们后面再讲,并且我们将会使用更加集成化的框架,就不需要手动搭建项目了。
|
|
||||||
|
|
||||||
使用vite的命令创建项目:
|
使用vite的命令创建项目:
|
||||||
|
|
||||||
@ -159,12 +158,12 @@ npm run dev
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// index.js
|
// index.js
|
||||||
import { createRouter, createWebHistory } from "vue-router"
|
import { createRouter, createWebHashHistory } from "vue-router"
|
||||||
import home from './modules/home'
|
import home from './modules/home'
|
||||||
import login from './modules/login'
|
import login from './modules/login'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
@ -242,10 +241,10 @@ npm run dev
|
|||||||
|
|
||||||
> 状态管理也使用模块化的方式
|
> 状态管理也使用模块化的方式
|
||||||
|
|
||||||
在store目录中创建modules目录,modules中创建一个模块,比如叫user.js
|
在store目录中创建modules目录,modules中创建一个模块app.js
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// user.js
|
// app.js
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {},
|
state: {},
|
||||||
@ -259,11 +258,11 @@ npm run dev
|
|||||||
```js
|
```js
|
||||||
//index.js
|
//index.js
|
||||||
import { createStore } from "vuex";
|
import { createStore } from "vuex";
|
||||||
import user from "./modules/user";
|
import app from "./modules/app";
|
||||||
|
|
||||||
export default createStore({
|
export default createStore({
|
||||||
modules: {
|
modules: {
|
||||||
user,
|
app,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -525,12 +524,12 @@ server: {
|
|||||||
password: '123456'
|
password: '123456'
|
||||||
})
|
})
|
||||||
if (+code === 200) {
|
if (+code === 200) {
|
||||||
store.commit("user/setToken", data);
|
store.commit("app/setToken", data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
store/modules/user.js
|
store/modules/app.js
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { getItem, setItem, removeItem } from "@/utils/storage"; //getItem和setItem是封装的操作localStorage的方法
|
import { getItem, setItem, removeItem } from "@/utils/storage"; //getItem和setItem是封装的操作localStorage的方法
|
||||||
@ -604,7 +603,7 @@ server: {
|
|||||||
// 拦截请求
|
// 拦截请求
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
const authorization = store.state.user;
|
const authorization = store.state.app;
|
||||||
if (authorization) {
|
if (authorization) {
|
||||||
config.headers.Authorization = `Bearer ${authorization.token}`;
|
config.headers.Authorization = `Bearer ${authorization.token}`;
|
||||||
}
|
}
|
||||||
@ -628,7 +627,7 @@ server: {
|
|||||||
// 响应拦截器中的 error 就是那个响应的错误对象
|
// 响应拦截器中的 error 就是那个响应的错误对象
|
||||||
if (error.response && error.response.status === 401) {
|
if (error.response && error.response.status === 401) {
|
||||||
// 校验是否有 refresh_token
|
// 校验是否有 refresh_token
|
||||||
const { authorization } = store.state;
|
const { authorization } = store.state.app;
|
||||||
if (!authorization || !authorization.refresh_token) {
|
if (!authorization || !authorization.refresh_token) {
|
||||||
router.push("/login");
|
router.push("/login");
|
||||||
|
|
||||||
@ -646,7 +645,7 @@ server: {
|
|||||||
});
|
});
|
||||||
// 如果获取成功,则把新的 token 更新到容器中
|
// 如果获取成功,则把新的 token 更新到容器中
|
||||||
// console.log('刷新 token 成功', res)
|
// console.log('刷新 token 成功', res)
|
||||||
store.commit("setToken", {
|
store.commit("app/setToken", {
|
||||||
token: res.data.data.token, // 最新获取的可用 token
|
token: res.data.data.token, // 最新获取的可用 token
|
||||||
refresh_token: authorization.refresh_token, // 还是原来的 refresh_token
|
refresh_token: authorization.refresh_token, // 还是原来的 refresh_token
|
||||||
});
|
});
|
||||||
@ -659,7 +658,7 @@ server: {
|
|||||||
// console.log('请求刷线 token 失败', err)
|
// console.log('请求刷线 token 失败', err)
|
||||||
router.push("/login");
|
router.push("/login");
|
||||||
// 清除token
|
// 清除token
|
||||||
store.commit("clearToken")
|
store.commit("app/clearToken")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,7 +699,7 @@ server: {
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// 引入TOKEN变量
|
// 引入TOKEN变量
|
||||||
import { TOKEN } from '@/store/modules/user'
|
import { TOKEN } from '@/store/modules/app'
|
||||||
|
|
||||||
// 全局路由守卫,注意vue-router4的路由守卫不需要next跳转,而是通过return返回false或者一个路由地址
|
// 全局路由守卫,注意vue-router4的路由守卫不需要next跳转,而是通过return返回false或者一个路由地址
|
||||||
router.beforeEach((to, from) => {
|
router.beforeEach((to, from) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user