新增 与后端对接

This commit is contained in:
小肥羊 2025-08-05 16:30:55 +08:00
parent 910a3a5837
commit ac2a6caa1e
21 changed files with 107 additions and 95 deletions

View File

@ -6,3 +6,5 @@ VITE_PUBLIC_PATH = /
# 开发环境路由历史模式Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数" # 开发环境路由历史模式Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数"
VITE_ROUTER_HISTORY = "hash" VITE_ROUTER_HISTORY = "hash"
VITE_API_BASEURL = "http://localhost:5199/api"

View File

@ -11,7 +11,7 @@ import removeNoMatch from "vite-plugin-router-warn";
import { visualizer } from "rollup-plugin-visualizer"; import { visualizer } from "rollup-plugin-visualizer";
import removeConsole from "vite-plugin-remove-console"; import removeConsole from "vite-plugin-remove-console";
import { codeInspectorPlugin } from "code-inspector-plugin"; import { codeInspectorPlugin } from "code-inspector-plugin";
import { vitePluginFakeServer } from "vite-plugin-fake-server"; // import { vitePluginFakeServer } from "vite-plugin-fake-server";
export function getPluginsList( export function getPluginsList(
VITE_CDN: boolean, VITE_CDN: boolean,
@ -41,12 +41,12 @@ export function getPluginsList(
*/ */
removeNoMatch(), removeNoMatch(),
// mock支持 // mock支持
vitePluginFakeServer({ // vitePluginFakeServer({
logger: false, // logger: false,
include: "mock", // include: "mock",
infixName: false, // infixName: false,
enableProd: true // enableProd: true
}), // }),
// svg组件化支持 // svg组件化支持
svgLoader(), svgLoader(),
// 自动按需加载图标 // 自动按需加载图标

View File

@ -6,13 +6,13 @@ export default defineFakeRoute([
url: "/login", url: "/login",
method: "post", method: "post",
response: ({ body }) => { response: ({ body }) => {
if (body.username === "admin") { if (body.userName === "admin") {
return { return {
success: true, success: true,
data: { data: {
avatar: "https://avatars.githubusercontent.com/u/44761321", avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin", userName: "admin",
nickname: "小铭", nickName: "小铭",
// 一个用户可能有多个角色 // 一个用户可能有多个角色
roles: ["admin"], roles: ["admin"],
// 按钮级别权限 // 按钮级别权限
@ -27,8 +27,8 @@ export default defineFakeRoute([
success: true, success: true,
data: { data: {
avatar: "https://avatars.githubusercontent.com/u/52823142", avatar: "https://avatars.githubusercontent.com/u/52823142",
username: "common", userName: "common",
nickname: "小林", nickName: "小林",
roles: ["common"], roles: ["common"],
permissions: ["permission:btn:add", "permission:btn:edit"], permissions: ["permission:btn:add", "permission:btn:edit"],
accessToken: "eyJhbGciOiJIUzUxMiJ9.common", accessToken: "eyJhbGciOiJIUzUxMiJ9.common",

View File

@ -6,5 +6,5 @@ type Result = {
}; };
export const getAsyncRoutes = () => { export const getAsyncRoutes = () => {
return http.request<Result>("get", "/get-async-routes"); return http.request<Result>("get", "/Menu/AdminMenu");
}; };

View File

@ -1,14 +1,13 @@
import { http } from "@/utils/http"; import { http } from "@/utils/http";
import type { Res } from "@/utils/http/types";
export type UserResult = { export type UserResult = {
success: boolean;
data: {
/** 头像 */ /** 头像 */
avatar: string; avatar: string;
/** 用户名 */ /** 用户名 */
username: string; userName: string;
/** 昵称 */ /** 昵称 */
nickname: string; nickName: string;
/** 当前登录用户的角色 */ /** 当前登录用户的角色 */
roles: Array<string>; roles: Array<string>;
/** 按钮级别权限 */ /** 按钮级别权限 */
@ -20,7 +19,6 @@ export type UserResult = {
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx' */ /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx' */
expires: Date; expires: Date;
}; };
};
export type RefreshTokenResult = { export type RefreshTokenResult = {
success: boolean; success: boolean;
@ -36,7 +34,7 @@ export type RefreshTokenResult = {
/** 登录 */ /** 登录 */
export const getLogin = (data?: object) => { export const getLogin = (data?: object) => {
return http.request<UserResult>("post", "/login", { data }); return http.request<Res<UserResult>>("post", "/Admin/Login", { data });
}; };
/** 刷新`token` */ /** 刷新`token` */

View File

@ -16,7 +16,7 @@ const {
logout, logout,
onPanel, onPanel,
pureApp, pureApp,
username, userName,
userAvatar, userAvatar,
avatarsStyle, avatarsStyle,
toggleSideBar toggleSideBar
@ -50,7 +50,7 @@ const {
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none"> <span class="el-dropdown-link navbar-bg-hover select-none">
<img :src="userAvatar" :style="avatarsStyle" /> <img :src="userAvatar" :style="avatarsStyle" />
<p v-if="username" class="dark:text-white">{{ username }}</p> <p v-if="userName" class="dark:text-white">{{ userName }}</p>
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <el-dropdown-menu class="logout">

View File

@ -26,7 +26,7 @@ const {
logout, logout,
onPanel, onPanel,
getLogo, getLogo,
username, userName,
userAvatar, userAvatar,
backTopMenu, backTopMenu,
avatarsStyle avatarsStyle
@ -81,7 +81,7 @@ onMounted(() => {
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover"> <span class="el-dropdown-link navbar-bg-hover">
<img :src="userAvatar" :style="avatarsStyle" /> <img :src="userAvatar" :style="avatarsStyle" />
<p v-if="username" class="dark:text-white">{{ username }}</p> <p v-if="userName" class="dark:text-white">{{ userName }}</p>
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <el-dropdown-menu class="logout">

View File

@ -22,7 +22,7 @@ const {
logout, logout,
onPanel, onPanel,
resolvePath, resolvePath,
username, userName,
userAvatar, userAvatar,
getDivStyle, getDivStyle,
avatarsStyle avatarsStyle
@ -101,7 +101,7 @@ watch(
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none"> <span class="el-dropdown-link navbar-bg-hover select-none">
<img :src="userAvatar" :style="avatarsStyle" /> <img :src="userAvatar" :style="avatarsStyle" />
<p v-if="username" class="dark:text-white">{{ username }}</p> <p v-if="userName" class="dark:text-white">{{ userName }}</p>
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <el-dropdown-menu class="logout">

View File

@ -88,7 +88,10 @@ const expandCloseIcon = computed(() => {
const onlyOneChild: menuType = ref(null); const onlyOneChild: menuType = ref(null);
function hasOneShowingChild(children: menuType[] = [], parent: menuType) { function hasOneShowingChild(children: menuType[] = [], parent: menuType) {
const showingChildren = children.filter((item: any) => { const showingChildren =
children == null
? []
: children.filter((item: any) => {
onlyOneChild.value = item; onlyOneChild.value = item;
return true; return true;
}); });

View File

@ -45,14 +45,14 @@ export function useNav() {
}); });
/** 昵称(如果昵称为空则显示用户名) */ /** 昵称(如果昵称为空则显示用户名) */
const username = computed(() => { const userName = computed(() => {
return isAllEmpty(useUserStoreHook()?.nickname) return isAllEmpty(useUserStoreHook()?.nickName)
? useUserStoreHook()?.username ? useUserStoreHook()?.userName
: useUserStoreHook()?.nickname; : useUserStoreHook()?.nickName;
}); });
const avatarsStyle = computed(() => { const avatarsStyle = computed(() => {
return username.value ? { marginRight: "10px" } : ""; return userName.value ? { marginRight: "10px" } : "";
}); });
const isCollapse = computed(() => { const isCollapse = computed(() => {
@ -149,7 +149,7 @@ export function useNav() {
getLogo, getLogo,
isCollapse, isCollapse,
pureApp, pureApp,
username, userName,
userAvatar, userAvatar,
avatarsStyle, avatarsStyle,
tooltipEffect tooltipEffect

View File

@ -28,7 +28,6 @@ const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
// 动态路由 // 动态路由
import { getAsyncRoutes } from "@/api/routes"; import { getAsyncRoutes } from "@/api/routes";
function handRank(routeInfo: any) { function handRank(routeInfo: any) {
const { name, path, parentId, meta } = routeInfo; const { name, path, parentId, meta } = routeInfo;
return isAllEmpty(parentId) return isAllEmpty(parentId)

View File

@ -26,6 +26,7 @@ export const usePermissionStore = defineStore("pure-permission", {
actions: { actions: {
/** 组装整体路由生成的菜单 */ /** 组装整体路由生成的菜单 */
handleWholeMenus(routes: any[]) { handleWholeMenus(routes: any[]) {
debugger;
this.wholeMenus = filterNoPermissionTree( this.wholeMenus = filterNoPermissionTree(
filterTree(ascending(this.constantMenus.concat(routes))) filterTree(ascending(this.constantMenus.concat(routes)))
); );

View File

@ -15,15 +15,16 @@ import {
} from "@/api/user"; } from "@/api/user";
import { useMultiTagsStoreHook } from "./multiTags"; import { useMultiTagsStoreHook } from "./multiTags";
import { type DataInfo, setToken, removeToken, userKey } from "@/utils/auth"; import { type DataInfo, setToken, removeToken, userKey } from "@/utils/auth";
import type { Res } from "@/utils/http/types";
export const useUserStore = defineStore("pure-user", { export const useUserStore = defineStore("pure-user", {
state: (): userType => ({ state: (): userType => ({
// 头像 // 头像
avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "", avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "",
// 用户名 // 用户名
username: storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "", userName: storageLocal().getItem<DataInfo<number>>(userKey)?.userName ?? "",
// 昵称 // 昵称
nickname: storageLocal().getItem<DataInfo<number>>(userKey)?.nickname ?? "", nickName: storageLocal().getItem<DataInfo<number>>(userKey)?.nickName ?? "",
// 页面级别权限 // 页面级别权限
roles: storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [], roles: storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [],
// 按钮级别权限 // 按钮级别权限
@ -40,12 +41,12 @@ export const useUserStore = defineStore("pure-user", {
this.avatar = avatar; this.avatar = avatar;
}, },
/** 存储用户名 */ /** 存储用户名 */
SET_USERNAME(username: string) { SET_USERNAME(userName: string) {
this.username = username; this.userName = userName;
}, },
/** 存储昵称 */ /** 存储昵称 */
SET_NICKNAME(nickname: string) { SET_NICKNAME(nickName: string) {
this.nickname = nickname; this.nickName = nickName;
}, },
/** 存储角色 */ /** 存储角色 */
SET_ROLES(roles: Array<string>) { SET_ROLES(roles: Array<string>) {
@ -65,10 +66,10 @@ export const useUserStore = defineStore("pure-user", {
}, },
/** 登入 */ /** 登入 */
async loginByUsername(data) { async loginByUsername(data) {
return new Promise<UserResult>((resolve, reject) => { return new Promise<Res<UserResult>>((resolve, reject) => {
getLogin(data) getLogin(data)
.then(data => { .then(data => {
if (data?.success) setToken(data.data); if (data?.code == 200) setToken(data.data);
resolve(data); resolve(data);
}) })
.catch(error => { .catch(error => {
@ -78,7 +79,7 @@ export const useUserStore = defineStore("pure-user", {
}, },
/** 前端登出(不调用接口) */ /** 前端登出(不调用接口) */
logOut() { logOut() {
this.username = ""; this.userName = "";
this.roles = []; this.roles = [];
this.permissions = []; this.permissions = [];
removeToken(); removeToken();

View File

@ -38,8 +38,8 @@ export type setType = {
export type userType = { export type userType = {
avatar?: string; avatar?: string;
username?: string; userName?: string;
nickname?: string; nickName?: string;
roles?: Array<string>; roles?: Array<string>;
permissions?: Array<string>; permissions?: Array<string>;
isRemembered?: boolean; isRemembered?: boolean;

View File

@ -12,9 +12,9 @@ export interface DataInfo<T> {
/** 头像 */ /** 头像 */
avatar?: string; avatar?: string;
/** 用户名 */ /** 用户名 */
username?: string; userName?: string;
/** 昵称 */ /** 昵称 */
nickname?: string; nickName?: string;
/** 当前登录用户的角色 */ /** 当前登录用户的角色 */
roles?: Array<string>; roles?: Array<string>;
/** 当前登录用户的按钮级别权限 */ /** 当前登录用户的按钮级别权限 */
@ -43,7 +43,7 @@ export function getToken(): DataInfo<number> {
* @description `token``token` * @description `token``token`
* `accessToken`访使`token``refreshToken``accessToken``token``refreshToken`30`accessToken`2`expires``accessToken` * `accessToken`访使`token``refreshToken``accessToken``token``refreshToken`30`accessToken`2`expires``accessToken`
* `accessToken``expires``refreshToken`key值为authorized-token的cookie里 * `accessToken``expires``refreshToken`key值为authorized-token的cookie里
* `avatar``username``nickname``roles``permissions``refreshToken``expires`key值为`user-info`localStorage里`multipleTabsKey` * `avatar``userName``nickName``roles``permissions``refreshToken``expires`key值为`user-info`localStorage里`multipleTabsKey`
*/ */
export function setToken(data: DataInfo<Date>) { export function setToken(data: DataInfo<Date>) {
let expires = 0; let expires = 0;
@ -68,47 +68,47 @@ export function setToken(data: DataInfo<Date>) {
: {} : {}
); );
function setUserKey({ avatar, username, nickname, roles, permissions }) { function setUserKey({ avatar, userName, nickName, roles, permissions }) {
useUserStoreHook().SET_AVATAR(avatar); useUserStoreHook().SET_AVATAR(avatar);
useUserStoreHook().SET_USERNAME(username); useUserStoreHook().SET_USERNAME(userName);
useUserStoreHook().SET_NICKNAME(nickname); useUserStoreHook().SET_NICKNAME(nickName);
useUserStoreHook().SET_ROLES(roles); useUserStoreHook().SET_ROLES(roles);
useUserStoreHook().SET_PERMS(permissions); useUserStoreHook().SET_PERMS(permissions);
storageLocal().setItem(userKey, { storageLocal().setItem(userKey, {
refreshToken, refreshToken,
expires, expires,
avatar, avatar,
username, userName,
nickname, nickName,
roles, roles,
permissions permissions
}); });
} }
if (data.username && data.roles) { if (data.userName && data.roles) {
const { username, roles } = data; const { userName, roles } = data;
setUserKey({ setUserKey({
avatar: data?.avatar ?? "", avatar: data?.avatar ?? "",
username, userName,
nickname: data?.nickname ?? "", nickName: data?.nickName ?? "",
roles, roles,
permissions: data?.permissions ?? [] permissions: data?.permissions ?? []
}); });
} else { } else {
const avatar = const avatar =
storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? ""; storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "";
const username = const userName =
storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? ""; storageLocal().getItem<DataInfo<number>>(userKey)?.userName ?? "";
const nickname = const nickName =
storageLocal().getItem<DataInfo<number>>(userKey)?.nickname ?? ""; storageLocal().getItem<DataInfo<number>>(userKey)?.nickName ?? "";
const roles = const roles =
storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? []; storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [];
const permissions = const permissions =
storageLocal().getItem<DataInfo<number>>(userKey)?.permissions ?? []; storageLocal().getItem<DataInfo<number>>(userKey)?.permissions ?? [];
setUserKey({ setUserKey({
avatar, avatar,
username, userName,
nickname, nickName,
roles, roles,
permissions permissions
}); });

View File

@ -16,6 +16,7 @@ import { useUserStoreHook } from "@/store/modules/user";
// 相关配置请参考www.axios-js.com/zh-cn/docs/#axios-request-config-1 // 相关配置请参考www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = { const defaultConfig: AxiosRequestConfig = {
baseURL: import.meta.env.VITE_API_BASEURL,
// 请求超时时间 // 请求超时时间
timeout: 10000, timeout: 10000,
headers: { headers: {

View File

@ -18,6 +18,11 @@ export interface PureHttpError extends AxiosError {
isCancelRequest?: boolean; isCancelRequest?: boolean;
} }
export type Res<T> = {
code: number;
data: T;
message: string;
};
export interface PureHttpResponse extends AxiosResponse { export interface PureHttpResponse extends AxiosResponse {
config: PureHttpRequestConfig; config: PureHttpRequestConfig;
} }

View File

@ -2,7 +2,7 @@ import { removeToken, setToken, type DataInfo } from "./auth";
import { subBefore, getQueryMap } from "@pureadmin/utils"; import { subBefore, getQueryMap } from "@pureadmin/utils";
/** /**
* http://localhost:8848/#/permission/page/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin * http://localhost:8848/#/permission/page/index?userName=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin
* *
* *
* 1. * 1.
@ -13,7 +13,7 @@ import { subBefore, getQueryMap } from "@pureadmin/utils";
(function () { (function () {
// 获取 url 中的参数 // 获取 url 中的参数
const params = getQueryMap(location.href) as DataInfo<Date>; const params = getQueryMap(location.href) as DataInfo<Date>;
const must = ["username", "roles", "accessToken"]; const must = ["userName", "roles", "accessToken"];
const mustLength = must.length; const mustLength = must.length;
if (Object.keys(params).length !== mustLength) return; if (Object.keys(params).length !== mustLength) return;

View File

@ -37,8 +37,8 @@ dataThemeChange(overallStyle.value);
const { title } = useNav(); const { title } = useNav();
const ruleForm = reactive({ const ruleForm = reactive({
username: "admin", account: "admin",
password: "admin123" password: "123456"
}); });
const onLogin = async (formEl: FormInstance | undefined) => { const onLogin = async (formEl: FormInstance | undefined) => {
@ -48,11 +48,11 @@ const onLogin = async (formEl: FormInstance | undefined) => {
loading.value = true; loading.value = true;
useUserStoreHook() useUserStoreHook()
.loginByUsername({ .loginByUsername({
username: ruleForm.username, account: ruleForm.account,
password: ruleForm.password password: ruleForm.password
}) })
.then(res => { .then(res => {
if (res.success) { if ((res.code = 200)) {
// //
return initRouter().then(() => { return initRouter().then(() => {
disabled.value = true; disabled.value = true;
@ -112,12 +112,14 @@ useEventListener(document, "keydown", ({ code }) => {
<h2 class="outline-hidden">{{ title }}</h2> <h2 class="outline-hidden">{{ title }}</h2>
</Motion> </Motion>
<el-form <!-- <el-form
ref="ruleFormRef" ref="ruleFormRef"
:model="ruleForm" :model="ruleForm"
:rules="loginRules" :rules="loginRules"
size="large" size="large"
> > -->
<el-form ref="ruleFormRef" :model="ruleForm" size="large">
<Motion :delay="100"> <Motion :delay="100">
<el-form-item <el-form-item
:rules="[ :rules="[
@ -127,10 +129,10 @@ useEventListener(document, "keydown", ({ code }) => {
trigger: 'blur' trigger: 'blur'
} }
]" ]"
prop="username" prop="account"
> >
<el-input <el-input
v-model="ruleForm.username" v-model="ruleForm.account"
clearable clearable
placeholder="账号" placeholder="账号"
:prefix-icon="useRenderIcon(User)" :prefix-icon="useRenderIcon(User)"

View File

@ -16,7 +16,7 @@ const elStyle = computed((): CSSProperties => {
}; };
}); });
const username = ref(useUserStoreHook()?.username); const userName = ref(useUserStoreHook()?.userName);
const options = [ const options = [
{ {
@ -31,9 +31,9 @@ const options = [
function onChange() { function onChange() {
useUserStoreHook() useUserStoreHook()
.loginByUsername({ username: username.value, password: "admin123" }) .loginByUsername({ userName: userName.value, password: "admin123" })
.then(res => { .then(res => {
if (res.success) { if (res.code === 200) {
storageLocal().removeItem("async-routes"); storageLocal().removeItem("async-routes");
usePermissionStoreHook().clearAllCachePage(); usePermissionStoreHook().clearAllCachePage();
initRouter(); initRouter();
@ -50,10 +50,10 @@ function onChange() {
<el-card shadow="never" :style="elStyle"> <el-card shadow="never" :style="elStyle">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>当前角色{{ username }}</span> <span>当前角色{{ userName }}</span>
</div> </div>
</template> </template>
<el-select v-model="username" class="w-[160px]!" @change="onChange"> <el-select v-model="userName" class="w-[160px]!" @change="onChange">
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.value" :key="item.value"

2
types/global.d.ts vendored
View File

@ -136,7 +136,7 @@ declare global {
showLogo?: boolean; showLogo?: boolean;
showModel?: string; showModel?: string;
menuSearchHistory?: number; menuSearchHistory?: number;
username?: string; userName?: string;
} }
/** /**