提交 dacfa8a3 authored 作者: 詹银鑫's avatar 詹银鑫

feat: 拉去develop代码

# 平台本地运行端口号 # 平台本地运行端口号
VITE_PORT = 8848 VITE_PORT = 884899
# 是否隐藏首页 隐藏 true 不隐藏 false (勿删除,VITE_HIDE_HOME只需在.env文件配置) # 是否隐藏首页 隐藏 true 不隐藏 false (勿删除,VITE_HIDE_HOME只需在.env文件配置)
VITE_HIDE_HOME = false VITE_HIDE_HOME = false
# 平台本地运行端口号 # 平台本地运行端口号
VITE_PORT = 8848 VITE_PORT = 884899
# 开发环境读取配置文件路径 # 开发环境读取配置文件路径
VITE_PUBLIC_PATH = / VITE_PUBLIC_PATH = /
......
...@@ -1734,9 +1734,9 @@ export default defineFakeRoute([ ...@@ -1734,9 +1734,9 @@ export default defineFakeRoute([
"Content-Length": 0, "Content-Length": 0,
Cookie: Cookie:
"_ga=GA1.1.231800979.1704562367; _ga_M74ZHEQ1M1=GS1.1.1709299375.7.1.1709299476.0.0.0; Hm_lvt_6a7dac00248d3b6ad8479d7249bb29c5=1709032753,1709359575; Hm_lvt_23a157b7d0d9867f7a51e42628f052f5=1708960489,1709485849,1709879672; authorized-token={%22accessToken%22:%22eyJhbGciOiJIUzUxMiJ9.admin%22%2C%22expires%22:1919520000000}; multiple-tabs=true", "_ga=GA1.1.231800979.1704562367; _ga_M74ZHEQ1M1=GS1.1.1709299375.7.1.1709299476.0.0.0; Hm_lvt_6a7dac00248d3b6ad8479d7249bb29c5=1709032753,1709359575; Hm_lvt_23a157b7d0d9867f7a51e42628f052f5=1708960489,1709485849,1709879672; authorized-token={%22accessToken%22:%22eyJhbGciOiJIUzUxMiJ9.admin%22%2C%22expires%22:1919520000000}; multiple-tabs=true",
Host: "192.168.2.121:8848", Host: "192.168.2.121:884899",
Origin: "http://192.168.2.121:8848", Origin: "http://192.168.2.121:884899",
Referer: "http://192.168.2.121:8848/", Referer: "http://192.168.2.121:884899/",
"User-Agent": "User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"X-Requested-With": "XMLHttpRequest" "X-Requested-With": "XMLHttpRequest"
...@@ -1782,9 +1782,9 @@ export default defineFakeRoute([ ...@@ -1782,9 +1782,9 @@ export default defineFakeRoute([
"Content-Length": 0, "Content-Length": 0,
Cookie: Cookie:
"_ga=GA1.1.231800979.1704562367; _ga_M74ZHEQ1M1=GS1.1.1709299375.7.1.1709299476.0.0.0; Hm_lvt_6a7dac00248d3b6ad8479d7249bb29c5=1709032753,1709359575; Hm_lvt_23a157b7d0d9867f7a51e42628f052f5=1708960489,1709485849,1709879672; authorized-token={%22accessToken%22:%22eyJhbGciOiJIUzUxMiJ9.admin%22%2C%22expires%22:1919520000000}; multiple-tabs=true", "_ga=GA1.1.231800979.1704562367; _ga_M74ZHEQ1M1=GS1.1.1709299375.7.1.1709299476.0.0.0; Hm_lvt_6a7dac00248d3b6ad8479d7249bb29c5=1709032753,1709359575; Hm_lvt_23a157b7d0d9867f7a51e42628f052f5=1708960489,1709485849,1709879672; authorized-token={%22accessToken%22:%22eyJhbGciOiJIUzUxMiJ9.admin%22%2C%22expires%22:1919520000000}; multiple-tabs=true",
Host: "192.168.2.121:8848", Host: "192.168.2.121:884899",
Origin: "http://192.168.2.121:8848", Origin: "http://192.168.2.121:884899",
Referer: "http://192.168.2.121:8848/", Referer: "http://192.168.2.121:884899/",
"User-Agent": "User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"X-Requested-With": "XMLHttpRequest" "X-Requested-With": "XMLHttpRequest"
......
// 示例 API 调用 import { http } from "@/utils/http";
export const get{{properCase name}}Data = async () => {
try { type Result = {
const response = await fetch('/api/{{lowerCase name}}'); success: boolean;
return await response.json(); data?: {
} catch (error) { /** 列表数据 */
console.error('Error fetching data:', error); list?: Array<any>;
} [key: string]: any;
}; };
\ No newline at end of file };
/** 获取{{pascalCase name}}列表 */
export const get{{pascalCase name}}List = (data?: object) => {
return http.request<Result>("post", "/get-{{kebabCase name}}-list", { data });
};
/** 创建{{pascalCase name}} */
export const create{{pascalCase name}} = (data?: object) => {
return http.request<Result>("post", "/create-{{kebabCase name}}", { data });
};
/** 更新{{pascalCase name}} */
export const update{{pascalCase name}} = (data?: object) => {
return http.request<Result>("post", "/update-{{kebabCase name}}", { data });
};
/** 删除{{pascalCase name}} */
export const delete{{pascalCase name}} = (data?: { id: string }) => {
return http.request<Result>("post", "/delete-{{kebabCase name}}", { data });
};
...@@ -31,6 +31,7 @@ module.exports = function (plop) { ...@@ -31,6 +31,7 @@ module.exports = function (plop) {
type: "input", type: "input",
name: "title", name: "title",
message: "请输入路由title名称:", message: "请输入路由title名称:",
when: answers => answers.hasAsyncRoute && answers.hasRoute,
validate: title => { validate: title => {
if (!title) { if (!title) {
return "路由title名称不能为空"; return "路由title名称不能为空";
...@@ -44,6 +45,18 @@ module.exports = function (plop) { ...@@ -44,6 +45,18 @@ module.exports = function (plop) {
message: "是否生成 API 调用?", message: "是否生成 API 调用?",
default: true default: true
} }
// {
// type: "input",
// name: "apiName",
// message: "请输入API名称:",
// when: answers => answers.hasApi,
// validate: apiName => {
// if (!apiName) {
// return "apiTitle不能为空";
// }
// return true;
// }
// }
], ],
actions: data => { actions: data => {
const actions = []; const actions = [];
......
import { http } from "@/utils/http"; import { http } from "@/utils/http";
type ResultTable = {
records?: Array<any>;
total?: number;
/** 每页显示条目个数 */
pageSize?: number;
/** 当前页数 */
currentPage?: number;
};
type Result = { type Result = {
success: boolean; success: boolean;
data: Array<any>; // data: Array<any>;
data: ResultTable;
}; };
export const getAsyncRoutes = () => { export const getAsyncRoutes = (data?: object) => {
return http.request<Result>("get", "/get-async-routes"); // return http.request<Result>("get", "/get-async-routes");
return http.request<Result>("post", "/api/menu/findUserListByPage", { data });
}; };
import { http } from "@/utils/http"; import { http } from "@/utils/http";
export type UserResult = { export type UserResult = {
status: number;
success: boolean; success: boolean;
data: { data: {
jwt: string;
/** 头像 */ /** 头像 */
avatar: string; avatar: string;
/** 用户名 */ /** 用户名 */
...@@ -70,7 +72,7 @@ type ResultTable = { ...@@ -70,7 +72,7 @@ type ResultTable = {
/** 登录 */ /** 登录 */
export const getLogin = (data?: object) => { export const getLogin = (data?: object) => {
return http.request<UserResult>("post", "/login", { data }); return http.request<UserResult>("post", "/api/auth/login", { data });
}; };
/** 刷新`token` */ /** 刷新`token` */
......
...@@ -28,6 +28,7 @@ const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}"); ...@@ -28,6 +28,7 @@ const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
// 动态路由 // 动态路由
import { getAsyncRoutes } from "@/api/routes"; import { getAsyncRoutes } from "@/api/routes";
import { transformRoutes } from "@/utils/createTree";
function handRank(routeInfo: any) { function handRank(routeInfo: any) {
const { name, path, parentId, meta } = routeInfo; const { name, path, parentId, meta } = routeInfo;
...@@ -202,8 +203,12 @@ function initRouter() { ...@@ -202,8 +203,12 @@ function initRouter() {
}); });
} else { } else {
return new Promise(resolve => { return new Promise(resolve => {
getAsyncRoutes().then(({ data }) => { getAsyncRoutes({ pageNum: 1, pageSize: 10 }).then(({ data }) => {
handleAsyncRoutes(cloneDeep(data)); console.log("路由数据:", data);
// const treeData = transformRoutes(data.records);
// console.log("树形数据:", treeData);
// handleAsyncRoutes(cloneDeep(treeData));
storageLocal().setItem(key, data); storageLocal().setItem(key, data);
resolve(router); resolve(router);
}); });
...@@ -211,8 +216,16 @@ function initRouter() { ...@@ -211,8 +216,16 @@ function initRouter() {
} }
} else { } else {
return new Promise(resolve => { return new Promise(resolve => {
getAsyncRoutes().then(({ data }) => { getAsyncRoutes({ pageNum: 1, pageSize: 10 }).then(({ data }) => {
handleAsyncRoutes(cloneDeep(data)); console.log("路由数据---:", data);
const treeData = transformRoutes(data.records);
handleAsyncRoutes(cloneDeep(treeData));
// const treeData = transformRoutes(data.records);
// console.log("树形数据1111:", treeData);
// handleAsyncRoutes(cloneDeep(treeData));
// console.log("路由数据9999:", handleAsyncRoutes(cloneDeep(treeData)));
resolve(router); resolve(router);
}); });
}); });
......
...@@ -26,9 +26,13 @@ export const usePermissionStore = defineStore("pure-permission", { ...@@ -26,9 +26,13 @@ export const usePermissionStore = defineStore("pure-permission", {
actions: { actions: {
/** 组装整体路由生成的菜单 */ /** 组装整体路由生成的菜单 */
handleWholeMenus(routes: any[]) { handleWholeMenus(routes: any[]) {
// filterNoPermissionTree 过滤掉没有权限的路由,本项目目前后端返回的是处理后的,无需处理
this.wholeMenus = filterNoPermissionTree( this.wholeMenus = filterNoPermissionTree(
filterTree(ascending(this.constantMenus.concat(routes))) filterTree(ascending(this.constantMenus))
); ).concat(routes);
console.log("this.wholeMenus", this.wholeMenus);
this.flatteningRoutes = formatFlatteningRoutes( this.flatteningRoutes = formatFlatteningRoutes(
this.constantMenus.concat(routes) as any this.constantMenus.concat(routes) as any
); );
......
...@@ -36,7 +36,9 @@ export const useUserStore = defineStore("pure-user", { ...@@ -36,7 +36,9 @@ export const useUserStore = defineStore("pure-user", {
// 是否勾选了登录页的免登录 // 是否勾选了登录页的免登录
isRemembered: false, isRemembered: false,
// 登录页的免登录存储几天,默认7天 // 登录页的免登录存储几天,默认7天
loginDay: 7 loginDay: 7,
token: localStorage.getItem("jwt") || "",
refreshToken: localStorage.getItem("refreshToken") || ""
}), }),
actions: { actions: {
/** 存储头像 */ /** 存储头像 */
......
...@@ -47,4 +47,6 @@ export type userType = { ...@@ -47,4 +47,6 @@ export type userType = {
currentPage?: number; currentPage?: number;
isRemembered?: boolean; isRemembered?: boolean;
loginDay?: number; loginDay?: number;
token: string;
refreshToken: string;
}; };
...@@ -124,7 +124,8 @@ export function removeToken() { ...@@ -124,7 +124,8 @@ export function removeToken() {
/** 格式化token(jwt格式) */ /** 格式化token(jwt格式) */
export const formatToken = (token: string): string => { export const formatToken = (token: string): string => {
return "Bearer " + token; // return "Bearer " + token;
return token;
}; };
/** 是否有按钮级别的权限(根据登录接口返回的`permissions`字段进行判断)*/ /** 是否有按钮级别的权限(根据登录接口返回的`permissions`字段进行判断)*/
......
// 定义菜单项的接口
interface MenuItem {
id: string; // 菜单项的唯一标识
name: string; // 菜单项的名称
parentId?: string; // 父菜单项的id,如果没有父菜单则为undefined或null
children?: treeType[]; // 子菜单项列表
// remark: null;
type: number;
path: string;
component: string;
// perm: "";
// visible: true;
// sort: 0;
icon: string;
redirect: string;
// deleted: 0;
roles?: string[]; // 角色权限
}
interface treeType {
path: string;
name: string;
redirect?: string;
children?: treeType[];
component?: string;
meta?: {
icon: string;
title: string;
rank?: number;
showLink?: boolean;
roles?: string[];
auths?: string[]; // 按钮级别权限
};
}
/**
* 将菜单项转换为树形结构
* @param item 菜单项
* @returns
*/
const transformData = (item: MenuItem): treeType => {
const { name, path, icon, redirect, children } = item;
return {
path: path,
name: name,
redirect: redirect,
children: children,
meta: {
icon: icon || "ri:admin-line",
title: name
// roles: roles || ["admin"]
}
};
};
/**
* 将后端返回的菜单数据转换为树形结构
* @param menuList 后端返回的菜单数据列表
* @returns 转换后的树形结构菜单数据
*/
export function transformRoutes(menuList: MenuItem[]): treeType[] {
// 创建一个映射表,用于快速查找每个菜单项
const map = new Map<string, MenuItem>();
menuList.forEach(item => {
map.set(item.id, { ...item });
});
// 创建一个数组,用于存储根菜单项
const tree: treeType[] = [];
menuList.forEach((item: MenuItem) => {
// 如果有父菜单项,则将其添加到父菜单项的children中
if (item.type === 1) {
// 如果没有父菜单项,则将其添加到树的根节点
tree.push(transformData(item));
} else if (item.type === 2 && item.parentId) {
const parentItem = map.get(item.parentId);
if (parentItem) {
// if (!parentItem.children) {
// parentItem.children = [];
// }
parentItem.children.push(transformData(item));
}
}
});
return tree;
}
...@@ -26,7 +26,8 @@ const defaultConfig: AxiosRequestConfig = { ...@@ -26,7 +26,8 @@ const defaultConfig: AxiosRequestConfig = {
// 数组格式参数序列化(https://github.com/axios/axios/issues/5142) // 数组格式参数序列化(https://github.com/axios/axios/issues/5142)
paramsSerializer: { paramsSerializer: {
serialize: stringify as unknown as CustomParamsSerializer serialize: stringify as unknown as CustomParamsSerializer
} },
baseURL: "/api"
}; };
class PureHttp { class PureHttp {
...@@ -51,6 +52,7 @@ class PureHttp { ...@@ -51,6 +52,7 @@ class PureHttp {
private static retryOriginalRequest(config: PureHttpRequestConfig) { private static retryOriginalRequest(config: PureHttpRequestConfig) {
return new Promise(resolve => { return new Promise(resolve => {
PureHttp.requests.push((token: string) => { PureHttp.requests.push((token: string) => {
debugger;
config.headers["Authorization"] = formatToken(token); config.headers["Authorization"] = formatToken(token);
resolve(config); resolve(config);
}); });
...@@ -78,6 +80,8 @@ class PureHttp { ...@@ -78,6 +80,8 @@ class PureHttp {
? config ? config
: new Promise(resolve => { : new Promise(resolve => {
const data = getToken(); const data = getToken();
console.log(4444, data);
if (data) { if (data) {
const now = new Date().getTime(); const now = new Date().getTime();
const expired = parseInt(data.expires) - now <= 0; const expired = parseInt(data.expires) - now <= 0;
......
...@@ -2,7 +2,7 @@ import { removeToken, setToken, type DataInfo } from "./auth"; ...@@ -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:884899/#/permission/page/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin
* 划重点: * 划重点:
* 判断是否为单点登录,不为则直接返回不再进行任何逻辑处理,下面是单点登录后的逻辑处理 * 判断是否为单点登录,不为则直接返回不再进行任何逻辑处理,下面是单点登录后的逻辑处理
* 1.清空本地旧信息; * 1.清空本地旧信息;
......
...@@ -33,6 +33,7 @@ import Check from "~icons/ep/check"; ...@@ -33,6 +33,7 @@ import Check from "~icons/ep/check";
import User from "~icons/ri/user-3-fill"; import User from "~icons/ri/user-3-fill";
import Info from "~icons/ri/information-line"; import Info from "~icons/ri/information-line";
import Keyhole from "~icons/ri/shield-keyhole-line"; import Keyhole from "~icons/ri/shield-keyhole-line";
import { setToken, getToken } from "@/utils/auth";
defineOptions({ defineOptions({
name: "Login" name: "Login"
...@@ -57,9 +58,15 @@ dataThemeChange(overallStyle.value); ...@@ -57,9 +58,15 @@ dataThemeChange(overallStyle.value);
const { title, getDropdownItemStyle, getDropdownItemClass } = useNav(); const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
const { locale, translationCh, translationEn } = useTranslationLang(); const { locale, translationCh, translationEn } = useTranslationLang();
// const ruleForm = reactive({
// username: "admin",
// password: "admin123",
// verifyCode: ""
// });
const ruleForm = reactive({ const ruleForm = reactive({
username: "admin", username: "zhangxiaoming",
password: "admin123", password: "string",
verifyCode: "" verifyCode: ""
}); });
...@@ -74,10 +81,17 @@ const onLogin = async (formEl: FormInstance | undefined) => { ...@@ -74,10 +81,17 @@ const onLogin = async (formEl: FormInstance | undefined) => {
password: ruleForm.password password: ruleForm.password
}) })
.then(res => { .then(res => {
if (res.success) { if (res.status === 200 || res.success) {
setToken({
accessToken: res.data.jwt,
refreshToken: "",
expires: new Date(Date.now() + 9999999 * 1000)
});
console.log("getToken", getToken);
// 获取后端路由 // 获取后端路由
return initRouter().then(() => { return initRouter().then(() => {
disabled.value = true; disabled.value = true;
router router
.push(getTopMenu(true).path) .push(getTopMenu(true).path)
.then(() => { .then(() => {
...@@ -208,7 +222,7 @@ watch(loginDay, value => { ...@@ -208,7 +222,7 @@ watch(loginDay, value => {
</Motion> </Motion>
<Motion :delay="150"> <Motion :delay="150">
<el-form-item prop="password"> <el-form-item>
<el-input <el-input
v-model="ruleForm.password" v-model="ruleForm.password"
clearable clearable
...@@ -219,7 +233,7 @@ watch(loginDay, value => { ...@@ -219,7 +233,7 @@ watch(loginDay, value => {
</el-form-item> </el-form-item>
</Motion> </Motion>
<Motion :delay="200"> <!-- <Motion :delay="200">
<el-form-item prop="verifyCode"> <el-form-item prop="verifyCode">
<el-input <el-input
v-model="ruleForm.verifyCode" v-model="ruleForm.verifyCode"
...@@ -232,7 +246,7 @@ watch(loginDay, value => { ...@@ -232,7 +246,7 @@ watch(loginDay, value => {
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
</Motion> </Motion> -->
<Motion :delay="250"> <Motion :delay="250">
<el-form-item> <el-form-item>
......
<template>
<div class="test">
<h2>Test</h2>
<slot />
</div>
</template>
<script setup lang="ts">
// 组件逻辑部分
import { ref } from "vue";
const count = ref(0);
const increment = () => {
count.value++;
};
</script>
<style scoped lang="scss">
.test {
padding: 20px;
border: 1px solid #ccc;
}
</style>
...@@ -10,8 +10,9 @@ import { ...@@ -10,8 +10,9 @@ import {
} from "./build/utils"; } from "./build/utils";
export default ({ mode }: ConfigEnv): UserConfigExport => { export default ({ mode }: ConfigEnv): UserConfigExport => {
const { VITE_CDN, VITE_PORT, VITE_COMPRESSION, VITE_PUBLIC_PATH } = const { VITE_CDN, VITE_COMPRESSION, VITE_PUBLIC_PATH } = wrapperEnv(
wrapperEnv(loadEnv(mode, root)); loadEnv(mode, root)
);
return { return {
base: VITE_PUBLIC_PATH, base: VITE_PUBLIC_PATH,
root, root,
...@@ -21,10 +22,17 @@ export default ({ mode }: ConfigEnv): UserConfigExport => { ...@@ -21,10 +22,17 @@ export default ({ mode }: ConfigEnv): UserConfigExport => {
// 服务端渲染 // 服务端渲染
server: { server: {
// 端口号 // 端口号
port: VITE_PORT, // port: VITE_PORT,
host: "0.0.0.0", host: "0.0.0.0",
// 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy // 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy
proxy: {}, proxy: {
"/api": {
// 这里填写后端地址
target: "http://192.168.1.194:5001",
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, "")
}
},
// 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布 // 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布
warmup: { warmup: {
clientFiles: ["./index.html", "./src/{views,components}/*"] clientFiles: ["./index.html", "./src/{views,components}/*"]
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论