Introducing new features. 对接后端路由

This commit is contained in:
lbw 2023-02-05 12:03:23 +08:00
parent 8045001e8b
commit aaf98bf771
29 changed files with 310 additions and 457 deletions

View File

@ -1,31 +1,19 @@
import request from '/@/utils/request';
/**
* gitee
*
* request.post(xxx) post params data
*
* json https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
* isRequestRoutes true
* @method getAdminMenu (admin)
* @method getTestMenu (test)
*/
export function useMenuApi() {
return {
getAdminMenu: (params?: object) => {
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json',
url: '/admin/menu',
method: 'get',
params,
});
},
getTestMenu: (params?: object) => {
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json',
method: 'get',
params,
});
},
}
};
}

View File

@ -65,7 +65,6 @@ const handleNodeClick = (item: any) => {
}
const getDeptTree = () => {
console.log('3333')
if(query instanceof Function){
state.localLoading = true
const result = query(unref(searchName))

View File

@ -1,5 +1,5 @@
import { ElMessage } from "element-plus";
import { useMessage } from "./message";
import {ElMessage} from "element-plus";
import {useMessage} from "./message";
import request from "/@/utils/request";
export interface BasicTableProps {
@ -87,7 +87,7 @@ export function useTable(options?: BasicTableProps) {
state.dataList = state.isPage ? res.data.records : res.data
state.pagination!.total = state.isPage ? res.data.total : 0
}).catch(err => {
ElMessage.error(err.msg)
ElMessage.error(err.response.data.msg)
}).finally(() => {
state.loading = false;
})
@ -114,7 +114,7 @@ export function useTable(options?: BasicTableProps) {
// 排序
const sortChangeHandle = (data: any) => {
const { prop, order } = data
const {prop, order} = data
if (prop && order) {
state.pagination!.order = prop
@ -138,7 +138,7 @@ export function useTable(options?: BasicTableProps) {
method: 'get',
responseType: 'blob',
params: query
}).then(response => {
}).then((response: any) => {
// 处理返回的文件流
if (response && response.size === 0) {
useMessage().error('内容为空,无法下载')

14
src/i18n/pages/form/en.ts Normal file
View File

@ -0,0 +1,14 @@
// 定义通用内容
export default {
common: {
queryBtn: 'query',
addBtn: 'add',
editBtn: 'edit',
delBtn: 'delete',
exportBtn: 'export',
importBtn: 'import',
importUserTip: 'import user',
queryDeptTip: 'input deptName',
resetBtn: 'reset'
}
};

View File

@ -0,0 +1,13 @@
// 定义通用内容
export default {
common: {
queryBtn: '查询',
addBtn: '新 增',
editBtn: '修 改',
delBtn: '删除',
exportBtn: '导出',
importBtn: '导入',
queryDeptTip: '请输入部门名称',
resetBtn: '重置'
}
};

View File

@ -537,7 +537,7 @@ onBeforeMount(() => {
// TagsView
mittBus.on('openShareTagsView', () => {
if (getThemeConfig.value.isShareTagsView) {
router.push('/home');
router.push('/home/index');
state.tagsViewList = [];
state.tagsViewRoutesList.map((v: RouteItem) => {
if (v.meta?.isAffix && !v.meta.isHide) {

View File

@ -19,7 +19,7 @@
<el-menu-item :index="val.path" :key="val.path">
<SvgIcon :name="val.meta.icon" />
<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
<span>{{ $t(val.meta.title) }}</span>
<span>{{ $t(val.meta.title) }} </span>
</template>
<template #title v-else>
<a class="w100" @click.prevent="onALinkClick(val)">{{ $t(val.meta.title) }}</a>

View File

@ -1,15 +1,15 @@
import { RouteRecordRaw } from 'vue-router';
import { storeToRefs } from 'pinia';
import {RouteRecordRaw} from 'vue-router';
import {storeToRefs} from 'pinia';
import pinia from '/@/stores/index';
import { useUserInfo } from '/@/stores/userInfo';
import { useRequestOldRoutes } from '/@/stores/requestOldRoutes';
import { Session } from '/@/utils/storage';
import { NextLoading } from '/@/utils/loading';
import { dynamicRoutes, notFoundAndNoPower } from '/@/router/route';
import { formatTwoStageRoutes, formatFlatteningRoutes, router } from '/@/router/index';
import { useRoutesList } from '/@/stores/routesList';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useMenuApi } from '/@/api/menu/index';
import {useUserInfo} from '/@/stores/userInfo';
import {useRequestOldRoutes} from '/@/stores/requestOldRoutes';
import {Session} from '/@/utils/storage';
import {NextLoading} from '/@/utils/loading';
import {dynamicRoutes, notFoundAndNoPower} from '/@/router/route';
import {formatTwoStageRoutes, formatFlatteningRoutes, router} from '/@/router/index';
import {useRoutesList} from '/@/stores/routesList';
import {useTagsViewRoutes} from '/@/stores/tagsViewRoutes';
import {useMenuApi} from '/@/api/menu/index';
// 后端控制路由
@ -23,7 +23,7 @@ const menuApi = useMenuApi();
*/
const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}');
const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layouModules }, { ...viewsModules });
const dynamicViewsModules: Record<string, Function> = Object.assign({}, {...layouModules}, {...viewsModules});
/**
*
@ -34,26 +34,26 @@ const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...lay
* @method setFilterMenuAndCacheTagsViewRoutes pinia routesList
*/
export async function initBackEndControlRoutes() {
// 界面 loading 动画开始执行
if (window.nextLoading === undefined) NextLoading.start();
// 无 token 停止执行下一步
if (!Session.get('token')) return false;
// 触发初始化用户信息 pinia
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
await useUserInfo().setUserInfos();
// 获取路由菜单数据
const res = await getBackEndControlRoutes();
// 无登录权限时,添加判断
// https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO
if (res.data.length <= 0) return Promise.resolve(true);
// 存储接口原始路由未处理component根据需求选择使用
useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(res.data)));
// 处理路由component替换 dynamicRoutes/@/router/route第一个顶级 children 的路由
dynamicRoutes[0].children = await backEndComponent(res.data);
// 添加动态路由
await setAddRoute();
// 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
await setFilterMenuAndCacheTagsViewRoutes();
// 界面 loading 动画开始执行
if (window.nextLoading === undefined) NextLoading.start();
// 无 token 停止执行下一步
if (!Session.get('token')) return false;
// 触发初始化用户信息 pinia
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
await useUserInfo().setUserInfos();
// 获取路由菜单数据
const res = await getBackEndControlRoutes();
// 无登录权限时,添加判断
// https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO
if (res.data.length <= 0) return Promise.resolve(true);
// 存储接口原始路由未处理component根据需求选择使用
useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(res.data)));
// 处理路由component替换 dynamicRoutes/@/router/route第一个顶级 children 的路由
dynamicRoutes[0].children = await backEndComponent(res.data);
// 添加动态路由
await setAddRoute();
// 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
await setFilterMenuAndCacheTagsViewRoutes();
}
/**
@ -62,9 +62,9 @@ export async function initBackEndControlRoutes() {
* @description tagsView(isHide)
*/
export async function setFilterMenuAndCacheTagsViewRoutes() {
const storesRoutesList = useRoutesList(pinia);
storesRoutesList.setRoutesList(dynamicRoutes[0].children as any);
setCacheTagsViewRoutes();
const storesRoutesList = useRoutesList(pinia);
storesRoutesList.setRoutesList(dynamicRoutes[0].children as any);
setCacheTagsViewRoutes();
}
/**
@ -72,8 +72,8 @@ export async function setFilterMenuAndCacheTagsViewRoutes() {
* @description tagsView(isHide)
*/
export function setCacheTagsViewRoutes() {
const storesTagsView = useTagsViewRoutes(pinia);
storesTagsView.setTagsViewRoutes(formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))[0].children);
const storesTagsView = useTagsViewRoutes(pinia);
storesTagsView.setTagsViewRoutes(formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))[0].children);
}
/**
@ -82,11 +82,11 @@ export function setCacheTagsViewRoutes() {
* @returns
*/
export function setFilterRouteEnd() {
let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
// notFoundAndNoPower 防止 404、401 不在 layout 布局中不设置的话404、401 界面将全屏显示
// 关联问题 No match found for location with path 'xxx'
filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower];
return filterRouteEnd;
let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
// notFoundAndNoPower 防止 404、401 不在 layout 布局中不设置的话404、401 界面将全屏显示
// 关联问题 No match found for location with path 'xxx'
filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower];
return filterRouteEnd;
}
/**
@ -96,9 +96,9 @@ export function setFilterRouteEnd() {
* @link https://next.router.vuejs.org/zh/api/#addroute
*/
export async function setAddRoute() {
await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
router.addRoute(route);
});
await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
router.addRoute(route);
});
}
/**
@ -107,23 +107,16 @@ export async function setAddRoute() {
* @returns
*/
export function getBackEndControlRoutes() {
// 模拟 admin 与 test
const stores = useUserInfo(pinia);
const { userInfos } = storeToRefs(stores);
const auth = userInfos.value.roles[0];
// 管理员 admin
if (auth === 'admin') return menuApi.getAdminMenu();
// 其它用户 test
else return menuApi.getTestMenu();
return menuApi.getAdminMenu();
}
/**
*
* @description
* @description /src/views/system/menu/component/addMenu.vue
* @description /src/views/admin/menu/component/addMenu.vue
*/
export async function setBackEndControlRefreshRoutes() {
await getBackEndControlRoutes();
await getBackEndControlRoutes();
}
/**
@ -132,12 +125,12 @@ export async function setBackEndControlRefreshRoutes() {
* @returns component
*/
export function backEndComponent(routes: any) {
if (!routes) return;
return routes.map((item: any) => {
if (item.component) item.component = dynamicImport(dynamicViewsModules, item.component as string);
item.children && backEndComponent(item.children);
return item;
});
if (!routes) return;
return routes.map((item: any) => {
if (item.path) item.component = dynamicImport(dynamicViewsModules, item.path as string);
item.children && backEndComponent(item.children);
return item;
});
}
/**
@ -147,16 +140,18 @@ export function backEndComponent(routes: any) {
* @returns component
*/
export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
const keys = Object.keys(dynamicViewsModules);
const matchKeys = keys.filter((key) => {
const k = key.replace(/..\/views|../, '');
return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
});
if (matchKeys?.length === 1) {
const matchKey = matchKeys[0];
return dynamicViewsModules[matchKey];
}
if (matchKeys?.length > 1) {
return false;
}
const keys = Object.keys(dynamicViewsModules);
const matchKeys = keys.filter((key) => {
const k = key.replace(/..\/views|../, '');
return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
});
if (matchKeys?.length === 1) {
const matchKey = matchKeys[0];
return dynamicViewsModules[matchKey];
}
if (matchKeys?.length > 1) {
return false;
}
}

View File

@ -5,10 +5,8 @@ import pinia from '/@/stores/index';
import { storeToRefs } from 'pinia';
import { useKeepALiveNames } from '/@/stores/keepAliveNames';
import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig';
import { Session } from '/@/utils/storage';
import { staticRoutes, notFoundAndNoPower } from '/@/router/route';
import { initFrontEndControlRoutes } from '/@/router/frontEnd';
import { initBackEndControlRoutes } from '/@/router/backEnd';
/**
@ -20,11 +18,6 @@ import { initBackEndControlRoutes } from '/@/router/backEnd';
* 2
*/
// 读取 `/src/stores/themeConfig.ts` 是否开启后端控制路由配置
const storesThemeConfig = useThemeConfig(pinia);
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isRequestRoutes } = themeConfig.value;
/**
* Vue 使
* @method createRouter(options: RouterOptions): Router
@ -110,17 +103,9 @@ router.beforeEach(async (to, from, next) => {
const storesRoutesList = useRoutesList(pinia);
const { routesList } = storeToRefs(storesRoutesList);
if (routesList.value.length === 0) {
if (isRequestRoutes) {
// 后端控制路由:路由数据初始化,防止刷新时丢失
await initBackEndControlRoutes();
// 解决刷新时,一直跳 404 页面问题,关联问题 No match found for location with path 'xxx'
// to.query 防止页面刷新时普通路由带参数时参数丢失。动态路由xxx/:id/:name"isDynamic 无需处理
next({ path: to.path, query: to.query });
} else {
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
await initFrontEndControlRoutes();
next({ path: to.path, query: to.query });
}
// 后端控制路由:路由数据初始化,防止刷新时丢失
await initBackEndControlRoutes();
next({ path: to.path, query: to.query });
} else {
next();
}

View File

@ -1,4 +1,4 @@
import { RouteRecordRaw } from 'vue-router';
import {RouteRecordRaw} from 'vue-router';
/**
* path 便
@ -18,16 +18,16 @@ import { RouteRecordRaw } from 'vue-router';
// 扩展 RouteMeta 接口
declare module 'vue-router' {
interface RouteMeta {
title?: string;
isLink?: string;
isHide?: boolean;
isKeepAlive?: boolean;
isAffix?: boolean;
isIframe?: boolean;
roles?: string[];
icon?: string;
}
interface RouteMeta {
title?: string;
isLink?: string;
isHide?: boolean;
isKeepAlive?: boolean;
isAffix?: boolean;
isIframe?: boolean;
roles?: string[];
icon?: string;
}
}
/**
@ -38,155 +38,16 @@ declare module 'vue-router' {
* @returns
*/
export const dynamicRoutes: Array<RouteRecordRaw> = [
{
path: '/',
name: '/',
component: () => import('/@/layout/index.vue'),
redirect: '/home',
meta: {
isKeepAlive: true,
},
children: [
{
path: '/home',
name: 'home',
component: () => import('/@/views/home/index.vue'),
meta: {
title: 'router.home',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: true,
isIframe: false,
roles: ['admin', 'common'],
icon: 'iconfont icon-shouye',
},
},
{
path: '/system',
name: 'system',
component: () => import('/@/layout/routerView/parent.vue'),
redirect: '/system/menu',
meta: {
title: 'router.system',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'iconfont icon-xitongshezhi',
},
children: [
{
path: '/system/menu',
name: 'systemMenu',
component: () => import('/@/views/system/menu/index.vue'),
meta: {
title: 'router.systemMenu',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'iconfont icon-caidan',
},
},
{
path: '/system/role',
name: 'systemRole',
component: () => import('/@/views/system/role/index.vue'),
meta: {
title: 'router.systemRole',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'ele-ColdDrink',
},
},
{
path: '/system/user',
name: 'systemUser',
component: () => import('/@/views/system/user/index.vue'),
meta: {
title: 'router.systemUser',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'iconfont icon-icon-',
},
},
{
path: '/system/dept',
name: 'systemDept',
component: () => import('/@/views/system/dept/index.vue'),
meta: {
title: 'router.systemDept',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'ele-OfficeBuilding',
},
},
{
path: '/system/dic',
name: 'systemDic',
component: () => import('/@/views/system/dic/index.vue'),
meta: {
title: 'router.systemDic',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'ele-SetUp',
},
},
],
},
{
path: '/personal',
name: 'personal',
component: () => import('/@/views/personal/index.vue'),
meta: {
title: 'router.personal',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'iconfont icon-gerenzhongxin',
},
},
{
path: '/tools',
name: 'tools',
component: () => import('/@/views/tools/index.vue'),
meta: {
title: 'router.tools',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'iconfont icon-gongju',
},
},
],
},
{
path: '/',
name: '/',
component: () => import('/@/layout/index.vue'),
redirect: '/home/index',
meta: {
isKeepAlive: true,
},
children: [],
},
];
/**
@ -194,24 +55,24 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
* @link https://next.router.vuejs.org/zh/guide/essentials/history-mode.html#netlify
*/
export const notFoundAndNoPower = [
{
path: '/:path(.*)*',
name: 'notFound',
component: () => import('/@/views/error/404.vue'),
meta: {
title: 'staticRoutes.notFound',
isHide: true,
},
},
{
path: '/401',
name: 'noPower',
component: () => import('/@/views/error/401.vue'),
meta: {
title: 'staticRoutes.noPower',
isHide: true,
},
},
{
path: '/:path(.*)*',
name: 'notFound',
component: () => import('/@/views/error/404.vue'),
meta: {
title: 'staticRoutes.notFound',
isHide: true,
},
},
{
path: '/401',
name: 'noPower',
component: () => import('/@/views/error/401.vue'),
meta: {
title: 'staticRoutes.noPower',
isHide: true,
},
}
];
/**
@ -221,12 +82,12 @@ export const notFoundAndNoPower = [
* @returns
*/
export const staticRoutes: Array<RouteRecordRaw> = [
{
path: '/login',
name: 'login',
component: () => import('/@/views/login/index.vue'),
meta: {
title: '登录',
},
}
{
path: '/login',
name: 'login',
component: () => import('/@/views/login/index.vue'),
meta: {
title: '登录',
},
}
];

View File

@ -131,7 +131,7 @@ export const useThemeConfig = defineStore('themeConfig', {
*
*/
// 是否开启后端控制路由
isRequestRoutes: false,
isRequestRoutes: true,
/**
* /

View File

@ -1,51 +1,50 @@
import { defineStore } from 'pinia';
import {defineStore} from 'pinia';
import Cookies from 'js-cookie';
import { Session } from '/@/utils/storage';
import { login } from '/@//api/login/index'
import other from '/@/utils/other'
import {Session} from '/@/utils/storage';
import {login} from '/@//api/login/index'
import other from '/@/utils/other'
/**
*
* @methods setUserInfos
*/
export const useUserInfo = defineStore('userInfo', {
state: (): UserInfosState => ({
userInfos: {
userName: '',
photo: '',
time: 0,
roles: [],
authBtnList: [],
},
}),
actions: {
async login(data: any){
data.grant_type = 'password'
data.scope = 'server'
// 密码加密
const user = other.encryption({
data: data,
key: 'pigxpigxpigxpigx',
param: ['password']
})
return new Promise((resolve, reject) => {
login(user).then(res =>{
// 存储token 信息
Session.set('token', res.access_token);
// 模拟数据,对接接口时,记得删除多余代码及对应依赖的引入。用于 `/src/stores/userInfo.ts` 中不同用户登录判断(模拟数据)
Cookies.set('userName', res.username);
resolve(res)
}).catch((err) => {
console.log(err)
reject(err)
})
})
state: (): UserInfosState => ({
userInfos: {
userName: '',
photo: '',
time: 0,
roles: [],
authBtnList: [],
},
}),
actions: {
async login(data: any) {
data.grant_type = 'password'
data.scope = 'server'
// 密码加密
const user = other.encryption({
data: data,
key: 'pigxpigxpigxpigx',
param: ['password']
})
return new Promise((resolve, reject) => {
login(user).then(res => {
// 存储token 信息
Session.set('token', res.access_token);
// 模拟数据,对接接口时,记得删除多余代码及对应依赖的引入。用于 `/src/stores/userInfo.ts` 中不同用户登录判断(模拟数据)
Cookies.set('userName', res.username);
resolve(res)
}).catch((err) => {
reject(err)
})
})
},
async setUserInfos() {
// 存储用户信息到浏览器缓存
if (Session.get('userInfo')) {
this.userInfos = Session.get('userInfo');
},
async setUserInfos() {
// 存储用户信息到浏览器缓存
if (Session.get('userInfo')) {
this.userInfos = Session.get('userInfo');
} else {
const userInfos: any = await this.getApiUserInfo();
this.userInfos = userInfos;
@ -76,7 +75,7 @@ export const useUserInfo = defineStore('userInfo', {
} else {
defaultRoles = testRoles;
defaultAuthBtnList = testAuthBtnList;
}
}
// 用户信息模拟数据
const userInfos = {
userName: userName,
@ -91,6 +90,7 @@ export const useUserInfo = defineStore('userInfo', {
resolve(userInfos);
}, 0);
});
},
},
},
},
});

13
src/utils/errorCode.ts Normal file
View File

@ -0,0 +1,13 @@
export default {
'000': '操作太频繁,请勿重复请求',
'401': '当前操作没有权限',
'403': '当前操作没有权限',
'404': '资源不存在',
'417': '未绑定登录账号,请使用密码登录后绑定',
'423': '演示环境不能操作,如需了解联系我们',
'426': '用户名不存在或密码错误',
'428': '验证码错误,请重新输入',
'429': '请求过频繁',
'479': '演示环境,没有权限操作',
'default': '系统未知错误,请反馈给管理员'
}

View File

@ -1,66 +1,63 @@
import axios, {AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig} from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Session } from '/@/utils/storage';
import errorCode from './errorCode'
import {ElMessage, ElMessageBox} from 'element-plus';
import {Session} from '/@/utils/storage';
import qs from 'qs';
// 配置新建一个 axios 实例
const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 50000,
headers: { 'Content-Type': 'application/json' },
paramsSerializer: {
serialize(params) {
return qs.stringify(params, { allowDots: true });
},
},
baseURL: import.meta.env.VITE_API_URL,
timeout: 50000,
headers: {'Content-Type': 'application/json'},
paramsSerializer: {
serialize(params) {
return qs.stringify(params, {allowDots: true});
},
},
});
// 添加请求拦截器
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {
// 在发送请求之前做些什么 token
if (Session.get('token')) {
config.headers!['Authorization'] = `Bearer ${Session.get('token')}`;
}
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
// 在发送请求之前做些什么 token
if (Session.get('token')) {
config.headers!['Authorization'] = `Bearer ${Session.get('token')}`;
}
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// 添加响应拦截器
service.interceptors.response.use(
(response) => {
// 对响应数据做点什么
const res = response.data;
if (res.code && res.code !== 0) {
// `token` 过期或者账号已在别处登录
if (res.code === 401 || res.code === 4001 || res.code === 402) {
Session.clear(); // 清除浏览器全部临时缓存
window.location.href = '/'; // 去登录页
ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
.then(() => {})
.catch(() => {});
}
return Promise.reject(service.interceptors.response);
} else {
return response.data;
}
},
(error) => {
// 对响应错误做点什么
if (error.message.indexOf('timeout') != -1) {
ElMessage.error('网络超时');
} else if (error.message == 'Network Error') {
ElMessage.error('网络连接错误');
} else {
if (error.response.data) ElMessage.error(error.response.statusText);
else ElMessage.error('接口路径找不到');
}
return Promise.reject(error);
}
);
service.interceptors.response.use((res: any) => {
return res.data;
}, error => {
const status = Number(error.response.status) || 200
const message = error.response.data.msg || errorCode[status] || errorCode['default']
if (status === 424) {
ElMessageBox.confirm('令牌状态已过期,请点击重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
Session.clear(); // 清除浏览器全部临时缓存
window.location.href = '/'; // 去登录页
return
})
.catch(() => {
})
}
if (status !== 200 || error.response.data.code === 1) {
ElMessage.error(message)
return Promise.reject(new Error(message))
}
return Promise.reject(error);
})
// 导出 axios 实例
export default service;

View File

@ -56,7 +56,7 @@ import { defineAsyncComponent, ref, reactive, onMounted } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
//
const DeptDialog = defineAsyncComponent(() => import('/@/views/system/dept/dialog.vue'));
const DeptDialog = defineAsyncComponent(() => import('/@/views/admin/dept/dialog.vue'));
//
const deptDialogRef = ref();

View File

@ -58,7 +58,7 @@ import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
//
const DicDialog = defineAsyncComponent(() => import('/@/views/system/dic/dialog.vue'));
const DicDialog = defineAsyncComponent(() => import('/@/views/admin/dict/dialog.vue'));
//
const dicDialogRef = ref();

View File

@ -12,7 +12,7 @@
<el-row style="margin-top: 20px">
<div class="mb15" style="width: 100%">
<el-button size="default" icon="folder-add" type="success" class="ml10" @click="roleDialogRef.openDialog('add')">
新增用户
新增
</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" style="float: right;margin-right: 20px" @queryTable="getDataList"></right-toolbar>
</div>

View File

@ -1,63 +1,54 @@
<template>
<el-form size="large" class="login-content-form">
<el-form-item class="login-animation1">
<el-input text :placeholder="$t('account.accountPlaceholder1')" v-model="state.ruleForm.username" clearable autocomplete="off">
<el-input text :placeholder="$t('account.accountPlaceholder1')" v-model="state.ruleForm.username" clearable
autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><ele-User /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item class="login-animation2">
<el-input
:type="state.isShowPassword ? 'text' : 'password'"
:placeholder="$t('account.accountPlaceholder2')"
v-model="state.ruleForm.password"
autocomplete="off"
>
<el-input :type="state.isShowPassword ? 'text' : 'password'"
:placeholder="$t('account.accountPlaceholder2')" v-model="state.ruleForm.password" autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><ele-Unlock /></el-icon>
</template>
<template #suffix>
<i
class="iconfont el-input__icon login-content-password"
<i class="iconfont el-input__icon login-content-password"
:class="state.isShowPassword ? 'icon-yincangmima' : 'icon-xianshimima'"
@click="state.isShowPassword = !state.isShowPassword"
>
@click="state.isShowPassword = !state.isShowPassword">
</i>
</template>
</el-input>
</el-form-item>
<el-form-item>
<Verify
@success="verifySuccess"
:mode="'pop'"
:captchaType="'blockPuzzle'"
:imgSize="{ width: '330px', height: '155px' }"
ref="verifyref"
/>
</el-form-item>
<!-- <el-form-item class="login-animation3">-->
<!-- <el-col :span="15">-->
<!-- <el-input-->
<!-- text-->
<!-- maxlength="4"-->
<!-- :placeholder="$t('account.accountPlaceholder3')"-->
<!-- v-model="state.ruleForm.code"-->
<!-- clearable-->
<!-- autocomplete="off"-->
<!-- >-->
<!-- <template #prefix>-->
<!-- <el-icon class="el-input__icon"><ele-Position /></el-icon>-->
<!-- </template>-->
<!-- </el-input>-->
<!-- </el-col>-->
<!-- <el-col :span="1"></el-col>-->
<!-- <el-col :span="8">-->
<!-- <el-button class="login-content-code" v-waves>1234</el-button>-->
<!-- </el-col>-->
<!-- </el-form-item>-->
<el-form-item>
<Verify @success="verifySuccess" :mode="'pop'" :captchaType="'blockPuzzle'"
:imgSize="{ width: '330px', height: '155px' }" ref="verifyref" />
</el-form-item>
<!-- <el-form-item class="login-animation3">-->
<!-- <el-col :span="15">-->
<!-- <el-input-->
<!-- text-->
<!-- maxlength="4"-->
<!-- :placeholder="$t('account.accountPlaceholder3')"-->
<!-- v-model="state.ruleForm.code"-->
<!-- clearable-->
<!-- autocomplete="off"-->
<!-- >-->
<!-- <template #prefix>-->
<!-- <el-icon class="el-input__icon"><ele-Position /></el-icon>-->
<!-- </template>-->
<!-- </el-input>-->
<!-- </el-col>-->
<!-- <el-col :span="1"></el-col>-->
<!-- <el-col :span="8">-->
<!-- <el-button class="login-content-code" v-waves>1234</el-button>-->
<!-- </el-col>-->
<!-- </el-form-item>-->
<el-form-item class="login-animation4">
<el-button type="primary" class="login-content-submit" round v-waves @click="handleLogin" :loading="state.loading.signIn">
<el-button type="primary" class="login-content-submit" round v-waves @click="handleLogin"
:loading="state.loading.signIn">
<span>{{ $t('account.accountBtnText') }}</span>
</el-button>
</el-form-item>
@ -65,7 +56,7 @@
</template>
<script setup lang="ts" name="loginAccount">
import {reactive, computed, defineAsyncComponent, ref} from 'vue';
import { reactive, computed, defineAsyncComponent, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
@ -89,10 +80,10 @@ const router = useRouter();
const state = reactive({
isShowPassword: false,
ruleForm: {
username: 'admin',
username: 'admin',
password: '123456',
code: '',
randomStr: 'blockPuzzle'
randomStr: 'blockPuzzle'
},
loading: {
signIn: false,
@ -107,26 +98,18 @@ const currentTime = computed(() => {
});
const handleLogin = () => {
verifyref.value.show();
verifyref.value.show();
}
//
const onSignIn = async () => {
state.loading.signIn = true;
// token
await useUserInfo().login(state.ruleForm)
//
await useUserInfo().login(state.ruleForm)
//
if (!themeConfig.value.isRequestRoutes) {
// 2
const isNoPower = await initFrontEndControlRoutes();
signInSuccess(isNoPower);
} else {
// isRequestRoutes true
// router No match found for location with path "/"
const isNoPower = await initBackEndControlRoutes();
// initBackEndControlRoutes signInSuccess
signInSuccess(isNoPower);
}
const isNoPower = await initBackEndControlRoutes();
// initBackEndControlRoutes signInSuccess
signInSuccess(isNoPower);
};
//
const signInSuccess = (isNoPower: boolean | undefined) => {
@ -156,14 +139,15 @@ const signInSuccess = (isNoPower: boolean | undefined) => {
};
const verifySuccess = (params: any) => {
state.ruleForm.code = params.captchaVerification;
onSignIn()
state.ruleForm.code = params.captchaVerification;
onSignIn()
}
</script>
<style scoped lang="scss">
.login-content-form {
margin-top: 20px;
@for $i from 1 through 4 {
.login-animation#{$i} {
opacity: 0;
@ -173,20 +157,24 @@ const verifySuccess = (params: any) => {
animation-delay: calc($i/10) + s;
}
}
.login-content-password {
display: inline-block;
width: 20px;
cursor: pointer;
&:hover {
color: #909399;
}
}
.login-content-code {
width: 100%;
padding: 0;
font-weight: bold;
letter-spacing: 5px;
}
.login-content-submit {
width: 100%;
letter-spacing: 2px;