🆕 渲染icon、完善动态路由和资源、封装request
This commit is contained in:
27
.env.prod
27
.env.prod
@@ -1,10 +1,17 @@
|
||||
# 后端服务地址
|
||||
VITE_SERVICE_URL="http://localhost:9095/api/manage"
|
||||
# 项目标题
|
||||
VITE_APP_TITLE="HuLa—校园平台"
|
||||
# 标签后缀
|
||||
VITE_TITLE_SUFFIX=" | HuLa"
|
||||
# 项目备案号
|
||||
VITE_APP_ICP="桂ICP备2021000000号"
|
||||
# 项目名称
|
||||
VITE_APP_NAME="HuLa-vue3"
|
||||
# 生产环境配置
|
||||
|
||||
# 应用标题
|
||||
VITE_APP_TITLE=HuLa Admin
|
||||
|
||||
# Gateway 地址
|
||||
VITE_API_BASE_URL=https://hulaspark.com
|
||||
|
||||
# 是否启用代理(Y/N)
|
||||
VITE_HTTP_PROXY=N
|
||||
|
||||
# Authorization 密钥
|
||||
VITE_SECRET_KEY=luohuo_web_pro:luohuo_web_pro_secret
|
||||
|
||||
# 应用环境
|
||||
VITE_APP_ENV=production
|
||||
|
||||
|
||||
84
package.json
84
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "hula-vue3",
|
||||
"private": true,
|
||||
"version": "v1.1.5-beta",
|
||||
"version": "v1.2.0-beta",
|
||||
"packageManager": "pnpm@8.14.1",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
@@ -41,82 +41,82 @@
|
||||
"dependencies": {
|
||||
"@fingerprintjs/fingerprintjs": "^5.0.1",
|
||||
"@intlify/unplugin-vue-i18n": "^0.11.0",
|
||||
"@types/crypto-js": "^4.2.1",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@vicons/ionicons5": "^0.13.0",
|
||||
"@vicons/tabler": "^0.12.0",
|
||||
"@vueuse/core": "^10.7.0",
|
||||
"@vueuse/core": "^10.11.1",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^1.6.2",
|
||||
"axios": "^1.13.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"dayjs": "^1.11.19",
|
||||
"echarts": "5.4.1",
|
||||
"encryptlong": "^3.1.4",
|
||||
"hotkeys-js": "^3.13.1",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"hotkeys-js": "^3.13.15",
|
||||
"jsencrypt": "^3.5.4",
|
||||
"localforage": "^1.10.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"naive-ui": "^2.37.3",
|
||||
"notiflix": "^3.2.6",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"naive-ui": "^2.43.1",
|
||||
"notiflix": "^3.2.8",
|
||||
"pinia": "^2.3.1",
|
||||
"pinia-plugin-persistedstate": "^3.2.3",
|
||||
"screenfull": "^6.0.2",
|
||||
"vue": "^3.4.15",
|
||||
"vue": "^3.5.24",
|
||||
"vue-drag-resize": "^1.5.4",
|
||||
"vue-i18n": "^9.8.0",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue-i18n": "^9.14.5",
|
||||
"vue-router": "^4.6.3",
|
||||
"vue3-count-to": "^1.1.2",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.23.3",
|
||||
"@babel/eslint-parser": "^7.28.5",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^20.10.4",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.1",
|
||||
"@typescript-eslint/parser": "^6.13.1",
|
||||
"@vitejs/plugin-vue": "^5.0.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@types/node": "^20.19.25",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.1",
|
||||
"@vitest/ui": "^0.32.4",
|
||||
"@vue/test-utils": "^2.4.3",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"commitizen": "^4.3.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"autoprefixer": "^10.4.22",
|
||||
"commitizen": "^4.3.1",
|
||||
"conventional-changelog": "^5.1.0",
|
||||
"conventional-changelog-cli": "^4.1.0",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"cz-customizable": "^7.0.0",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"cz-customizable": "^7.5.1",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^9.1.2",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-vue": "^9.19.2",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-vue": "^9.33.0",
|
||||
"lint-staged": "^14.0.1",
|
||||
"only-allow": "^1.2.1",
|
||||
"oxlint": "^0.2.4",
|
||||
"prettier": "^3.1.1",
|
||||
"rollup-plugin-visualizer": "^5.11.0",
|
||||
"sass": "^1.69.5",
|
||||
"sass-loader": "^14.0.0",
|
||||
"oxlint": "^0.2.18",
|
||||
"prettier": "^3.6.2",
|
||||
"rollup-plugin-visualizer": "^5.14.0",
|
||||
"sass": "^1.94.0",
|
||||
"sass-loader": "^14.2.1",
|
||||
"stylelint": "^15.11.0",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-recess-order": "^4.4.0",
|
||||
"stylelint-config-recess-order": "^4.6.0",
|
||||
"stylelint-config-recommended": "^13.0.0",
|
||||
"stylelint-config-recommended-scss": "^12.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.5.0",
|
||||
"stylelint-config-recommended-vue": "^1.6.1",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-config-standard-scss": "^10.0.0",
|
||||
"stylelint-order": "^6.0.4",
|
||||
"stylelint-prettier": "^3.0.0",
|
||||
"stylelint-scss": "^5.3.1",
|
||||
"terser": "^5.26.0",
|
||||
"typescript": "5.3.3",
|
||||
"unplugin-auto-import": "^0.17.2",
|
||||
"stylelint-scss": "^5.3.2",
|
||||
"terser": "^5.44.1",
|
||||
"typescript": "^5.9.3",
|
||||
"unplugin-auto-import": "^0.17.8",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "5.0.10",
|
||||
"vite": "7.2.2",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-vue-devtools": "^7.0.11",
|
||||
"vite-plugin-vue-devtools": "^7.7.8",
|
||||
"vitest": "^0.32.4",
|
||||
"vue-tsc": "^1.8.27"
|
||||
},
|
||||
|
||||
5535
pnpm-lock.yaml
generated
5535
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -47,7 +47,7 @@ const Content = defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/styles/scss/global-app';
|
||||
@use '@/styles/scss/global-app';
|
||||
#app {
|
||||
background-color: v-bind(LOGIN_BGC);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { request } from '@/utils/http'
|
||||
import { RequestModule } from '@/enums/request'
|
||||
import type { LoginParams, LoginResponse, CaptchaResponse, UserInfo, ApiResponse } from '@/types/api'
|
||||
import type { LoginParams, LoginResponse, CaptchaResponse, UserInfo } from '@/types/api'
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param data 登录参数
|
||||
*/
|
||||
export function loginApi(data: LoginParams): Promise<ApiResponse<LoginResponse>> {
|
||||
return request({
|
||||
export function loginApi(data: LoginParams): Promise<LoginResponse> {
|
||||
return request<LoginResponse>({
|
||||
url: '/anyTenant/login',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -19,8 +19,8 @@ export function loginApi(data: LoginParams): Promise<ApiResponse<LoginResponse>>
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
export function getCaptchaApi(): Promise<ApiResponse<CaptchaResponse>> {
|
||||
return request({
|
||||
export function getCaptchaApi(): Promise<CaptchaResponse> {
|
||||
return request<CaptchaResponse>({
|
||||
url: '/anyTenant/captcha',
|
||||
method: 'get',
|
||||
module: RequestModule.OAUTH,
|
||||
@@ -32,8 +32,8 @@ export function getCaptchaApi(): Promise<ApiResponse<CaptchaResponse>> {
|
||||
* 获取用户信息
|
||||
* 后端从 token 中获取当前登录用户的 userId
|
||||
*/
|
||||
export function getUserInfoApi(): Promise<ApiResponse<UserInfo>> {
|
||||
return request({
|
||||
export function getUserInfoApi(): Promise<UserInfo> {
|
||||
return request<UserInfo>({
|
||||
url: '/anyone/getUserInfo',
|
||||
method: 'get',
|
||||
module: RequestModule.OAUTH
|
||||
@@ -44,8 +44,8 @@ export function getUserInfoApi(): Promise<ApiResponse<UserInfo>> {
|
||||
* 刷新 Token
|
||||
* @param refreshToken 刷新令牌
|
||||
*/
|
||||
export function refreshTokenApi(refreshToken: string): Promise<ApiResponse<LoginResponse>> {
|
||||
return request({
|
||||
export function refreshTokenApi(refreshToken: string): Promise<LoginResponse> {
|
||||
return request<LoginResponse>({
|
||||
url: '/anyTenant/login',
|
||||
method: 'post',
|
||||
data: {
|
||||
@@ -61,8 +61,8 @@ export function refreshTokenApi(refreshToken: string): Promise<ApiResponse<Login
|
||||
* 退出登录
|
||||
* @param data 退出参数
|
||||
*/
|
||||
export function logoutApi(data: { token: string; refreshToken?: string }): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function logoutApi(data: { token: string; refreshToken?: string }): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/anyUser/logout',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -78,8 +78,8 @@ export function updatePasswordApi(data: {
|
||||
oldPassword: string
|
||||
newPassword: string
|
||||
confirmPassword: string
|
||||
}): Promise<ApiResponse> {
|
||||
return request({
|
||||
}): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/anyUser/updatePassword',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -94,8 +94,8 @@ export function updatePasswordApi(data: {
|
||||
export function switchTenantAndOrgApi(data: {
|
||||
orgId?: string
|
||||
clientId: string
|
||||
}): Promise<ApiResponse<LoginResponse>> {
|
||||
return request({
|
||||
}): Promise<LoginResponse> {
|
||||
return request<LoginResponse>({
|
||||
url: '/anyone/switchTenantAndOrg',
|
||||
method: 'put',
|
||||
params: data,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { request } from '@/utils/http'
|
||||
import { RequestModule } from '@/enums/request'
|
||||
import type { RouteItem, UserRoutesResponse, ResourceTreeNode, ApiResponse } from '@/types/api'
|
||||
import type { RouteItem, UserRoutesResponse, ResourceTreeNode } from '@/types/api'
|
||||
|
||||
/**
|
||||
* 获取公共路由
|
||||
* @param applicationId 应用 ID
|
||||
*/
|
||||
export function getConstantRoutesApi(applicationId: number = 1): Promise<ApiResponse<RouteItem[]>> {
|
||||
return request({
|
||||
export function getConstantRoutesApi(applicationId: number = 1): Promise<RouteItem[]> {
|
||||
return request<RouteItem[]>({
|
||||
url: '/anyTenant/menu/initRoute',
|
||||
method: 'get',
|
||||
params: { applicationId },
|
||||
@@ -20,8 +20,8 @@ export function getConstantRoutesApi(applicationId: number = 1): Promise<ApiResp
|
||||
* 获取用户路由(需要登录)
|
||||
* @param applicationId 应用 ID
|
||||
*/
|
||||
export function getUserRoutesApi(applicationId: number = 1): Promise<ApiResponse<UserRoutesResponse>> {
|
||||
return request({
|
||||
export function getUserRoutesApi(applicationId: number = 1): Promise<UserRoutesResponse> {
|
||||
return request<UserRoutesResponse>({
|
||||
url: '/anyone/visible/resource',
|
||||
method: 'get',
|
||||
params: { applicationId },
|
||||
@@ -33,8 +33,8 @@ export function getUserRoutesApi(applicationId: number = 1): Promise<ApiResponse
|
||||
* 检查路由是否存在
|
||||
* @param routeName 路由名称
|
||||
*/
|
||||
export function checkRouteExistApi(routeName: string): Promise<ApiResponse<boolean>> {
|
||||
return request({
|
||||
export function checkRouteExistApi(routeName: string): Promise<boolean> {
|
||||
return request<boolean>({
|
||||
url: '/defResource/isRouteExist',
|
||||
method: 'get',
|
||||
params: { routeName },
|
||||
@@ -46,8 +46,8 @@ export function checkRouteExistApi(routeName: string): Promise<ApiResponse<boole
|
||||
* 获取资源树(用于菜单管理)
|
||||
* @param applicationId 应用 ID
|
||||
*/
|
||||
export function getResourceTreeApi(applicationId: number = 1): Promise<ApiResponse<ResourceTreeNode[]>> {
|
||||
return request({
|
||||
export function getResourceTreeApi(applicationId: number = 1): Promise<ResourceTreeNode[]> {
|
||||
return request<ResourceTreeNode[]>({
|
||||
url: '/defResource/tree',
|
||||
method: 'post',
|
||||
params: { applicationId },
|
||||
@@ -59,8 +59,8 @@ export function getResourceTreeApi(applicationId: number = 1): Promise<ApiRespon
|
||||
* 获取可见资源(菜单 + 视图)
|
||||
* @param applicationId 应用 ID
|
||||
*/
|
||||
export function getVisibleResourceApi(applicationId: number = 1): Promise<ApiResponse<RouteItem[]>> {
|
||||
return request({
|
||||
export function getVisibleResourceApi(applicationId: number = 1): Promise<RouteItem[]> {
|
||||
return request<RouteItem[]>({
|
||||
url: '/defResource/visible',
|
||||
method: 'get',
|
||||
params: { applicationId },
|
||||
@@ -72,8 +72,8 @@ export function getVisibleResourceApi(applicationId: number = 1): Promise<ApiRes
|
||||
* 新增资源
|
||||
* @param data 资源数据
|
||||
*/
|
||||
export function addResourceApi(data: Partial<ResourceTreeNode>): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function addResourceApi(data: Partial<ResourceTreeNode>): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/defResource/add',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -85,8 +85,8 @@ export function addResourceApi(data: Partial<ResourceTreeNode>): Promise<ApiResp
|
||||
* 编辑资源
|
||||
* @param data 资源数据
|
||||
*/
|
||||
export function editResourceApi(data: Partial<ResourceTreeNode>): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function editResourceApi(data: Partial<ResourceTreeNode>): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/defResource/edit',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -98,8 +98,8 @@ export function editResourceApi(data: Partial<ResourceTreeNode>): Promise<ApiRes
|
||||
* 删除资源
|
||||
* @param data 删除参数
|
||||
*/
|
||||
export function deleteResourceApi(data: { id?: string; ids?: string[] }): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function deleteResourceApi(data: { id?: string; ids?: string[] }): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/defResource/del',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -111,8 +111,8 @@ export function deleteResourceApi(data: { id?: string; ids?: string[] }): Promis
|
||||
* 获取资源详情
|
||||
* @param id 资源 ID
|
||||
*/
|
||||
export function getResourceDetailApi(id: string): Promise<ApiResponse<ResourceTreeNode>> {
|
||||
return request({
|
||||
export function getResourceDetailApi(id: string): Promise<ResourceTreeNode> {
|
||||
return request<ResourceTreeNode>({
|
||||
url: '/defResource/detail',
|
||||
method: 'get',
|
||||
params: { id },
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { request } from '@/utils/http'
|
||||
import { RequestModule } from '@/enums/request'
|
||||
import type { TenantInfo, ApiResponse } from '@/types/api'
|
||||
import type { TenantInfo } from '@/types/api'
|
||||
|
||||
/**
|
||||
* 获取所有正常状态的租户列表
|
||||
* 需要登录后调用
|
||||
*/
|
||||
export function getTenantListApi(): Promise<ApiResponse<TenantInfo[]>> {
|
||||
return request({
|
||||
export function getTenantListApi(): Promise<TenantInfo[]> {
|
||||
return request<TenantInfo[]>({
|
||||
url: '/defTenant/all',
|
||||
method: 'get',
|
||||
module: RequestModule.BASE
|
||||
@@ -19,8 +19,8 @@ export function getTenantListApi(): Promise<ApiResponse<TenantInfo[]>> {
|
||||
* 需要登录后调用
|
||||
* 后端会从 token 中解析 userId,无需前端传递
|
||||
*/
|
||||
export function getUserTenantListApi(): Promise<ApiResponse<TenantInfo[]>> {
|
||||
return request({
|
||||
export function getUserTenantListApi(): Promise<TenantInfo[]> {
|
||||
return request<TenantInfo[]>({
|
||||
url: `/defTenant/listTenantByUserId`,
|
||||
method: 'get',
|
||||
module: RequestModule.BASE
|
||||
@@ -31,8 +31,8 @@ export function getUserTenantListApi(): Promise<ApiResponse<TenantInfo[]>> {
|
||||
* 检测租户编码是否存在
|
||||
* @param code 租户编码
|
||||
*/
|
||||
export function checkTenantCodeApi(code: string): Promise<ApiResponse<boolean>> {
|
||||
return request({
|
||||
export function checkTenantCodeApi(code: string): Promise<boolean> {
|
||||
return request<boolean>({
|
||||
url: `/defTenant/check/${code}`,
|
||||
method: 'get',
|
||||
module: RequestModule.BASE
|
||||
@@ -43,8 +43,8 @@ export function checkTenantCodeApi(code: string): Promise<ApiResponse<boolean>>
|
||||
* 获取租户详情
|
||||
* @param id 租户 ID
|
||||
*/
|
||||
export function getTenantDetailApi(id: string): Promise<ApiResponse<TenantInfo>> {
|
||||
return request({
|
||||
export function getTenantDetailApi(id: string): Promise<TenantInfo> {
|
||||
return request<TenantInfo>({
|
||||
url: `/defTenant/detail`,
|
||||
method: 'get',
|
||||
params: { id },
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { request } from '@/utils/http'
|
||||
import { RequestModule } from '@/enums/request'
|
||||
import type { UserInfo, PageParams, PageResponse, ApiResponse } from '@/types/api'
|
||||
import type { UserInfo, PageParams, PageResponse } from '@/types/api'
|
||||
|
||||
/**
|
||||
* 获取用户列表(分页)
|
||||
* @param params 分页参数
|
||||
*/
|
||||
export function getUserListApi(params: PageParams & Record<string, any>): Promise<ApiResponse<PageResponse<UserInfo>>> {
|
||||
return request({
|
||||
export function getUserListApi(
|
||||
params: PageParams & Record<string, any>
|
||||
): Promise<PageResponse<UserInfo>> {
|
||||
return request<PageResponse<UserInfo>>({
|
||||
url: '/user/page',
|
||||
method: 'get',
|
||||
params,
|
||||
@@ -19,8 +21,8 @@ export function getUserListApi(params: PageParams & Record<string, any>): Promis
|
||||
* 获取用户详情
|
||||
* @param id 用户 ID
|
||||
*/
|
||||
export function getUserDetailApi(id: number | string): Promise<ApiResponse<UserInfo>> {
|
||||
return request({
|
||||
export function getUserDetailApi(id: number | string): Promise<UserInfo> {
|
||||
return request<UserInfo>({
|
||||
url: '/user/detail',
|
||||
method: 'get',
|
||||
params: { id },
|
||||
@@ -32,8 +34,8 @@ export function getUserDetailApi(id: number | string): Promise<ApiResponse<UserI
|
||||
* 新增用户
|
||||
* @param data 用户数据
|
||||
*/
|
||||
export function addUserApi(data: Partial<UserInfo>): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function addUserApi(data: Partial<UserInfo>): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/user/add',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -45,8 +47,8 @@ export function addUserApi(data: Partial<UserInfo>): Promise<ApiResponse> {
|
||||
* 编辑用户
|
||||
* @param data 用户数据
|
||||
*/
|
||||
export function editUserApi(data: Partial<UserInfo>): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function editUserApi(data: Partial<UserInfo>): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/user/edit',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -58,8 +60,8 @@ export function editUserApi(data: Partial<UserInfo>): Promise<ApiResponse> {
|
||||
* 删除用户
|
||||
* @param data 删除参数
|
||||
*/
|
||||
export function deleteUserApi(data: { id?: number; ids?: number[] }): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function deleteUserApi(data: { id?: number; ids?: number[] }): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/user/del',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -71,8 +73,8 @@ export function deleteUserApi(data: { id?: number; ids?: number[] }): Promise<Ap
|
||||
* 重置用户密码
|
||||
* @param data 重置密码参数
|
||||
*/
|
||||
export function resetPasswordApi(data: { id: number; password: string }): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function resetPasswordApi(data: { id: number; password: string }): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/user/resetPassword',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -84,8 +86,8 @@ export function resetPasswordApi(data: { id: number; password: string }): Promis
|
||||
* 修改用户状态
|
||||
* @param data 状态参数
|
||||
*/
|
||||
export function updateUserStateApi(data: { id: number; state: boolean }): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function updateUserStateApi(data: { id: number; state: boolean }): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/user/updateState',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -96,8 +98,8 @@ export function updateUserStateApi(data: { id: number; state: boolean }): Promis
|
||||
/**
|
||||
* 获取当前用户信息(扩展)
|
||||
*/
|
||||
export function getCurrentUserInfoApi(): Promise<ApiResponse<UserInfo>> {
|
||||
return request({
|
||||
export function getCurrentUserInfoApi(): Promise<UserInfo> {
|
||||
return request<UserInfo>({
|
||||
url: '/user/current',
|
||||
method: 'get',
|
||||
module: RequestModule.BASE
|
||||
@@ -108,8 +110,8 @@ export function getCurrentUserInfoApi(): Promise<ApiResponse<UserInfo>> {
|
||||
* 更新当前用户信息
|
||||
* @param data 用户数据
|
||||
*/
|
||||
export function updateCurrentUserApi(data: Partial<UserInfo>): Promise<ApiResponse> {
|
||||
return request({
|
||||
export function updateCurrentUserApi(data: Partial<UserInfo>): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/user/updateCurrent',
|
||||
method: 'post',
|
||||
data,
|
||||
@@ -117,15 +119,16 @@ export function updateCurrentUserApi(data: Partial<UserInfo>): Promise<ApiRespon
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 上传用户头像
|
||||
* @param file 头像文件
|
||||
*/
|
||||
export function uploadAvatarApi(file: File): Promise<ApiResponse<{ url: string }>> {
|
||||
export function uploadAvatarApi(file: File): Promise<{ url: string }> {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
return request({
|
||||
return request<{ url: string }>({
|
||||
url: '/user/uploadAvatar',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
@@ -136,3 +139,4 @@ export function uploadAvatarApi(file: File): Promise<ApiResponse<{ url: string }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ const englishSwitch = () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/layout-header.scss';
|
||||
@use '@/styles/scss/layout-header.scss';
|
||||
.n-button-hover {
|
||||
font-weight: bold;
|
||||
.n-button:hover {
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
@contextmenu="handleContextMenu($event, item.path)"
|
||||
@click.stop="router.push(item.path)">
|
||||
<div class="tabs-left" />
|
||||
<n-icon class="tab-icon" size="18" :component="(vicons as any)[item.icon]" />
|
||||
<n-icon
|
||||
class="tab-icon"
|
||||
size="18"
|
||||
:component="(vicons as any)[item.icon] || (vicons as any)[DEFAULT_TAB_ICON]"
|
||||
/>
|
||||
{{ item.title }}
|
||||
<n-icon class="del" size="14" :component="X" @click.stop="removeTabs(item.path)" />
|
||||
</div>
|
||||
@@ -52,6 +56,8 @@
|
||||
<script setup lang="ts">
|
||||
import * as vicons from '@vicons/tabler'
|
||||
import { BrowserX, DotsVertical, LetterA, LetterO, SmartHome, X } from '@vicons/tabler'
|
||||
|
||||
const DEFAULT_TAB_ICON = 'LayoutGrid'
|
||||
import { mainStore } from '@/stores/main'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { tabs } from '@/stores/tabs'
|
||||
|
||||
@@ -87,5 +87,5 @@ const handleDel = async (item: any) => {
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/global-search';
|
||||
@use '@/styles/scss/global-search';
|
||||
</style>
|
||||
|
||||
@@ -75,5 +75,5 @@ const handleTo = () => {
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/global-search';
|
||||
@use '@/styles/scss/global-search';
|
||||
</style>
|
||||
|
||||
@@ -104,7 +104,7 @@ document.addEventListener('keydown', (event) => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/layout-header.scss';
|
||||
@use '@/styles/scss/layout-header.scss';
|
||||
.search-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -162,6 +162,6 @@ const save = (val: globalSetting, event: MouseEvent) => {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/styles/scss/layout-header.scss';
|
||||
@import '@/styles/scss/toggle-theme.scss';
|
||||
@use '@/styles/scss/layout-header';
|
||||
@use '@/styles/scss/toggle-theme';
|
||||
</style>
|
||||
|
||||
@@ -3,8 +3,6 @@ import useState from '@/hooks/useState.ts'
|
||||
import { i18n } from '@/i18n'
|
||||
import { NForm } from 'naive-ui'
|
||||
import { remember } from '@/stores/remember'
|
||||
import apis from '@/services/apis'
|
||||
import { RCodeEnum } from '@/enums'
|
||||
import { Loading } from 'notiflix'
|
||||
import { tabs } from '@/stores/tabs.ts'
|
||||
import { getEnhancedFingerprint } from '@/utils/fingerprint'
|
||||
@@ -135,25 +133,26 @@ export const useLogin = () => {
|
||||
* @param notifi 是否显示提示
|
||||
*/
|
||||
const exit = async (notifi = true) => {
|
||||
await apis.logout().then((res) => {
|
||||
if (res.code !== RCodeEnum.OK) {
|
||||
window.$notification.error({
|
||||
title: res.msg ? res.msg : t('logout_error'),
|
||||
duration: 1500,
|
||||
keepAliveOnHover: true
|
||||
})
|
||||
return false
|
||||
}
|
||||
userInfoStore.logout()
|
||||
try {
|
||||
await userInfoStore.logout()
|
||||
tabsStore.resetState()
|
||||
if (notifi) {
|
||||
window.$notification.success({
|
||||
title: res.msg,
|
||||
title: t('logout'),
|
||||
duration: 1500,
|
||||
keepAliveOnHover: true
|
||||
})
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('退出登录失败:', error)
|
||||
if (notifi) {
|
||||
window.$notification.error({
|
||||
title: t('logout_error'),
|
||||
duration: 1500,
|
||||
keepAliveOnHover: true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
/*弹出验证码输入框*/
|
||||
const handleCodeInput = async (formInstance: any) => {
|
||||
|
||||
@@ -101,10 +101,12 @@ const handleCollapsed = () => {
|
||||
emit('collapsed', collapsed.value)
|
||||
}
|
||||
|
||||
/* 渲染菜单图标(兼容后端返回的任意 icon 字符串,不存在的直接不渲染) */
|
||||
const DEFAULT_MENU_ICON = 'LayoutGrid'
|
||||
|
||||
/* 渲染菜单图标(兼容后端返回的任意 icon 字符串,找不到则使用默认图标) */
|
||||
const renderIcon = (icon?: string) => {
|
||||
if (!icon) return undefined
|
||||
const Comp = (vicons as any)[icon]
|
||||
const iconName = icon && (vicons as any)[icon] ? icon : DEFAULT_MENU_ICON
|
||||
const Comp = (vicons as any)[iconName]
|
||||
if (!Comp) return undefined
|
||||
return () => <NIcon component={Comp} />
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="footer">Copyright © 2023-2024 HuLa.All Rights Reserved.</div>
|
||||
<div class="footer">Copyright © 2023-2026 HuLa.All Rights Reserved.</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -271,7 +271,7 @@ const userExit = () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/layout-header.scss';
|
||||
@use '@/styles/scss/layout-header.scss';
|
||||
:deep(.n-tabs-capsule) {
|
||||
width: 122px !important;
|
||||
height: 33px !important;
|
||||
|
||||
@@ -32,9 +32,19 @@ const routes: Array<RouteRecordRaw> = [
|
||||
path: '/',
|
||||
name: 'page',
|
||||
component: () => import('@/layout/index.vue'),
|
||||
//斜杠重定向路由到/home
|
||||
// 斜杠重定向路由到 /home
|
||||
redirect: '/home',
|
||||
children: []
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
name: 'Home',
|
||||
meta: {
|
||||
title: '主页',
|
||||
requiresAuth: true
|
||||
},
|
||||
component: () => import('@/views/page/Home.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -53,37 +53,35 @@ export const userStore = defineStore('localUserInfo', {
|
||||
*/
|
||||
async login(loginParams: LoginParams) {
|
||||
try {
|
||||
const res = await loginApi(loginParams)
|
||||
if (res.success && res.data) {
|
||||
this.loginInfo.token = res.data.token
|
||||
this.loginInfo.refreshToken = res.data.refreshToken
|
||||
const uid = res.data.uid
|
||||
const data = await loginApi(loginParams)
|
||||
|
||||
if (!this.loginInfo.sysUser) {
|
||||
this.loginInfo.sysUser = {
|
||||
id: '',
|
||||
uid: uid,
|
||||
tenantId: '',
|
||||
role: ''
|
||||
} as any
|
||||
} else {
|
||||
this.loginInfo.sysUser.uid = uid
|
||||
}
|
||||
this.loginInfo.token = data.token
|
||||
this.loginInfo.refreshToken = data.refreshToken
|
||||
const uid = data.uid
|
||||
|
||||
console.log('✅ 登录成功,token 和 uid 已保存:', {
|
||||
token: res.data.token?.substring(0, 20) + '...',
|
||||
if (!this.loginInfo.sysUser) {
|
||||
this.loginInfo.sysUser = {
|
||||
id: '',
|
||||
uid,
|
||||
userId: this.loginInfo.sysUser?.id
|
||||
})
|
||||
|
||||
// 等待租户选择后再获取用户信息和初始化路由
|
||||
return { success: true, needSelectTenant: true, uid: res.data.uid }
|
||||
tenantId: '',
|
||||
role: ''
|
||||
} as any
|
||||
} else {
|
||||
return { success: false, message: res.msg || '登录失败' }
|
||||
this.loginInfo.sysUser.uid = uid
|
||||
}
|
||||
|
||||
console.log('✅ 登录成功,token 和 uid 已保存:', {
|
||||
token: data.token?.substring(0, 20) + '...',
|
||||
uid,
|
||||
userId: this.loginInfo.sysUser?.id
|
||||
})
|
||||
|
||||
// 等待租户选择后再获取用户信息和初始化路由
|
||||
return { success: true, needSelectTenant: true, uid: data.uid }
|
||||
} catch (error: any) {
|
||||
console.error('❌ 登录失败:', error)
|
||||
return { success: false, message: error.message || '登录失败' }
|
||||
const message = (error && (error.msg || error.message)) || '登录失败'
|
||||
return { success: false, message }
|
||||
}
|
||||
},
|
||||
|
||||
@@ -92,13 +90,8 @@ export const userStore = defineStore('localUserInfo', {
|
||||
*/
|
||||
async getUserTenantList(): Promise<TenantInfo[]> {
|
||||
try {
|
||||
const res = await getUserTenantListApi()
|
||||
if (res.success && res.data) {
|
||||
return res.data
|
||||
} else {
|
||||
console.error('获取租户列表失败:', res.msg)
|
||||
return []
|
||||
}
|
||||
const list = await getUserTenantListApi()
|
||||
return list || []
|
||||
} catch (error) {
|
||||
console.error('获取租户列表失败:', error)
|
||||
return []
|
||||
@@ -126,43 +119,33 @@ export const userStore = defineStore('localUserInfo', {
|
||||
}
|
||||
|
||||
// 调用切换租户 API
|
||||
const switchRes = await switchTenantAndOrgApi({
|
||||
const switchData = await switchTenantAndOrgApi({
|
||||
clientId: 'luohuo_web_pro' // 使用配置的客户端 ID
|
||||
})
|
||||
|
||||
if (switchRes.success && switchRes.data) {
|
||||
// 更新 token
|
||||
if (switchRes.data.token) {
|
||||
this.loginInfo.token = switchRes.data.token
|
||||
console.log('✅ 切换租户成功,token 已更新')
|
||||
}
|
||||
} else {
|
||||
console.error('❌ 切换租户失败:', switchRes.msg)
|
||||
return { success: false, message: switchRes.msg || '切换租户失败' }
|
||||
// 更新 token
|
||||
if (switchData.token) {
|
||||
this.loginInfo.token = switchData.token
|
||||
console.log('✅ 切换租户成功,token 已更新')
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
const userRes = await getUserInfoApi()
|
||||
if (userRes.success && userRes.data) {
|
||||
this.loginInfo.sysUser = {
|
||||
...userRes.data,
|
||||
id: userRes.data.id || '',
|
||||
uid: userRes.data.uid || '',
|
||||
tenantId,
|
||||
role:
|
||||
Array.isArray(userRes.data.roles) && userRes.data.roles.length > 0
|
||||
? userRes.data.roles[0].code || ''
|
||||
: ''
|
||||
}
|
||||
console.log('✅ 用户信息获取成功:', {
|
||||
id: userRes.data.id,
|
||||
userName: userRes.data.userName,
|
||||
tenantId: String(tenantId)
|
||||
})
|
||||
} else {
|
||||
console.error('❌ 获取用户信息失败:', userRes.msg)
|
||||
return { success: false, message: userRes.msg || '获取用户信息失败' }
|
||||
const userData = await getUserInfoApi()
|
||||
this.loginInfo.sysUser = {
|
||||
...userData,
|
||||
id: userData.id || '',
|
||||
uid: userData.uid || '',
|
||||
tenantId,
|
||||
role:
|
||||
Array.isArray(userData.roles) && userData.roles.length > 0
|
||||
? userData.roles[0].code || ''
|
||||
: ''
|
||||
}
|
||||
console.log('✅ 用户信息获取成功:', {
|
||||
id: userData.id,
|
||||
userName: userData.userName,
|
||||
tenantId: String(tenantId)
|
||||
})
|
||||
|
||||
// 初始化动态路由
|
||||
await this.initUserInfo()
|
||||
@@ -170,7 +153,8 @@ export const userStore = defineStore('localUserInfo', {
|
||||
return { success: true }
|
||||
} catch (error: any) {
|
||||
console.error('❌ 设置租户失败:', error)
|
||||
return { success: false, message: error.message || '设置租户失败' }
|
||||
const message = (error && (error.msg || error.message)) || '设置租户失败'
|
||||
return { success: false, message }
|
||||
}
|
||||
},
|
||||
|
||||
@@ -179,134 +163,132 @@ export const userStore = defineStore('localUserInfo', {
|
||||
*/
|
||||
async initUserInfo() {
|
||||
try {
|
||||
const routeRes = await getUserRoutesApi(1) // applicationId = 1
|
||||
if (routeRes.success && routeRes.data) {
|
||||
const rawRoutes = routeRes.data.routerList || []
|
||||
const routeData = await getUserRoutesApi(1) // applicationId = 1
|
||||
const rawRoutes = routeData.routerList || []
|
||||
|
||||
// 将后端返回的 VueRouter 数据转换为前端菜单结构
|
||||
const transformRoutesToMenus = (routes: any[]): any[] => {
|
||||
/**
|
||||
* 从路由记录解析出前端使用的 page 名称
|
||||
* - 优先使用 route.page
|
||||
* - 其次使用 route.component / route.meta.component
|
||||
* - 对于后端返回的 "/basic/.../index"、"/basic/.../Edit" 等,取最后一段作为 page
|
||||
* - 对于 "LAYOUT",不生成实际页面,仅作为分组存在
|
||||
*/
|
||||
const getPageFromRoute = (route: any): string | undefined => {
|
||||
if (route.page) return route.page
|
||||
// 将后端返回的 VueRouter 数据转换为前端菜单结构
|
||||
const transformRoutesToMenus = (routes: any[]): any[] => {
|
||||
/**
|
||||
* 从路由记录解析出前端使用的 page 名称
|
||||
* - 优先使用 route.page
|
||||
* - 其次使用 route.component / route.meta.component
|
||||
* - 对于后端返回的 "/basic/.../index"、"/basic/.../Edit" 等,取最后一段作为 page
|
||||
* - 对于 "LAYOUT",不生成实际页面,仅作为分组存在
|
||||
*/
|
||||
const getPageFromRoute = (route: any): string | undefined => {
|
||||
if (route.page) return route.page
|
||||
|
||||
const rawComponent: string | undefined = route.component || route.meta?.component
|
||||
if (!rawComponent || rawComponent === 'LAYOUT') return undefined
|
||||
const rawComponent: string | undefined = route.component || route.meta?.component
|
||||
if (!rawComponent || rawComponent === 'LAYOUT') return undefined
|
||||
|
||||
if (rawComponent.includes('/basic/user/')) {
|
||||
return 'User'
|
||||
}
|
||||
if (rawComponent.includes('/basic/system/baseRole/')) {
|
||||
return 'Role'
|
||||
}
|
||||
if (rawComponent.includes('/basic/system/') || rawComponent.includes('/basic/msg/')) {
|
||||
return 'Home'
|
||||
}
|
||||
|
||||
let comp = rawComponent
|
||||
// 去掉可能的前缀,例如 src/views/、views/、page/、/basic/
|
||||
comp = comp.replace(/^\/?src\/views\//, '')
|
||||
comp = comp.replace(/^views\//, '')
|
||||
comp = comp.replace(/^page\//, '')
|
||||
comp = comp.replace(/^\/?basic\//, '')
|
||||
|
||||
const segments = comp.split('/')
|
||||
let last = segments[segments.length - 1] || ''
|
||||
|
||||
// 去掉 .vue 后缀
|
||||
if (last.endsWith('.vue')) {
|
||||
last = last.slice(0, -4)
|
||||
}
|
||||
return last || undefined
|
||||
if (rawComponent.includes('/basic/user/')) {
|
||||
return 'User'
|
||||
}
|
||||
if (rawComponent.includes('/basic/system/baseRole/')) {
|
||||
return 'Role'
|
||||
}
|
||||
if (rawComponent.includes('/basic/system/') || rawComponent.includes('/basic/msg/')) {
|
||||
return 'Home'
|
||||
}
|
||||
|
||||
const normalizePath = (path: string | undefined, page: string): string => {
|
||||
// Home 页特殊处理:保持为 "home"
|
||||
if (page === 'Home') {
|
||||
if (!path || path === '/' || path === '/home' || path === 'home') {
|
||||
return 'home'
|
||||
}
|
||||
}
|
||||
let comp = rawComponent
|
||||
// 去掉可能的前缀,例如 src/views/、views/、page/、/basic/
|
||||
comp = comp.replace(/^\/?src\/views\//, '')
|
||||
comp = comp.replace(/^views\//, '')
|
||||
comp = comp.replace(/^page\//, '')
|
||||
comp = comp.replace(/^\/?basic\//, '')
|
||||
|
||||
const p = path || `/${page}`
|
||||
return p.startsWith('/') ? p : `/${p}`
|
||||
const segments = comp.split('/')
|
||||
let last = segments[segments.length - 1] || ''
|
||||
|
||||
// 去掉 .vue 后缀
|
||||
if (last.endsWith('.vue')) {
|
||||
last = last.slice(0, -4)
|
||||
}
|
||||
|
||||
const loop = (list: any[]): any[] => {
|
||||
const result: any[] = []
|
||||
|
||||
list.forEach((route) => {
|
||||
const meta = route.meta || {}
|
||||
const hideMenu = meta.hideMenu === true
|
||||
const hideChildrenInMenu = meta.hideChildrenInMenu === true
|
||||
|
||||
const page = getPageFromRoute(route)
|
||||
const path = page ? normalizePath(route.path, page) : route.path
|
||||
|
||||
let children: any[] | undefined
|
||||
if (Array.isArray(route.children) && route.children.length && !hideChildrenInMenu) {
|
||||
children = loop(route.children)
|
||||
if (!children.length) children = undefined
|
||||
}
|
||||
|
||||
// 当前节点隐藏菜单,仅提升子节点
|
||||
if (hideMenu) {
|
||||
if (children) {
|
||||
result.push(...children)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LAYOUT 等仅作为分组存在且没有子节点时,直接忽略
|
||||
if (!page && (!children || !children.length)) {
|
||||
return
|
||||
}
|
||||
|
||||
const menu: any = {
|
||||
id: route.id ? String(route.id) : undefined,
|
||||
path: page ? path : undefined, // 分组节点不需要 path
|
||||
// 菜单显示名称优先使用 meta.title
|
||||
name: meta.title || route.name || page,
|
||||
page,
|
||||
icon: route.icon || meta.icon,
|
||||
hideMenu,
|
||||
hideChildrenInMenu
|
||||
}
|
||||
|
||||
if (children) {
|
||||
menu.children = children
|
||||
}
|
||||
|
||||
result.push(menu)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return loop(routes)
|
||||
return last || undefined
|
||||
}
|
||||
|
||||
this.loginInfo.menus = transformRoutesToMenus(rawRoutes)
|
||||
// 同步权限资源与角色信息
|
||||
this.loginInfo.auths = (routeRes.data.resourceList || []).map((code: string) => ({ auth: code }))
|
||||
this.loginInfo.roles = (routeRes.data.roleList || []).map((code: string) => ({ name: code, code }))
|
||||
const normalizePath = (path: string | undefined, page: string): string => {
|
||||
// Home 页特殊处理:保持为 "home"
|
||||
if (page === 'Home') {
|
||||
if (!path || path === '/' || path === '/home' || path === 'home') {
|
||||
return 'home'
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 动态路由初始化完成:', {
|
||||
rawRoutes,
|
||||
menus: this.loginInfo.menus,
|
||||
resourceList: routeRes.data.resourceList,
|
||||
roleList: routeRes.data.roleList
|
||||
})
|
||||
const p = path || `/${page}`
|
||||
return p.startsWith('/') ? p : `/${p}`
|
||||
}
|
||||
|
||||
// 设置动态路由
|
||||
setRoutes(this.loginInfo.menus)
|
||||
const loop = (list: any[]): any[] => {
|
||||
const result: any[] = []
|
||||
|
||||
list.forEach((route) => {
|
||||
const meta = route.meta || {}
|
||||
const hideMenu = meta.hideMenu === true
|
||||
const hideChildrenInMenu = meta.hideChildrenInMenu === true
|
||||
|
||||
const page = getPageFromRoute(route)
|
||||
const path = page ? normalizePath(route.path, page) : route.path
|
||||
|
||||
let children: any[] | undefined
|
||||
if (Array.isArray(route.children) && route.children.length && !hideChildrenInMenu) {
|
||||
children = loop(route.children)
|
||||
if (!children.length) children = undefined
|
||||
}
|
||||
|
||||
// 当前节点隐藏菜单,仅提升子节点
|
||||
if (hideMenu) {
|
||||
if (children) {
|
||||
result.push(...children)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LAYOUT 等仅作为分组存在且没有子节点时,直接忽略
|
||||
if (!page && (!children || !children.length)) {
|
||||
return
|
||||
}
|
||||
|
||||
const menu: any = {
|
||||
id: route.id ? String(route.id) : undefined,
|
||||
path: page ? path : undefined, // 分组节点不需要 path
|
||||
// 菜单显示名称优先使用 meta.title
|
||||
name: meta.title || route.name || page,
|
||||
page,
|
||||
icon: route.icon || meta.icon,
|
||||
hideMenu,
|
||||
hideChildrenInMenu
|
||||
}
|
||||
|
||||
if (children) {
|
||||
menu.children = children
|
||||
}
|
||||
|
||||
result.push(menu)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return loop(routes)
|
||||
}
|
||||
|
||||
this.loginInfo.menus = transformRoutesToMenus(rawRoutes)
|
||||
// 同步权限资源与角色信息
|
||||
this.loginInfo.auths = (routeData.resourceList || []).map((code: string) => ({ auth: code }))
|
||||
this.loginInfo.roles = (routeData.roleList || []).map((code: string) => ({ name: code, code }))
|
||||
|
||||
console.log('✅ 动态路由初始化完成:', {
|
||||
rawRoutes,
|
||||
menus: this.loginInfo.menus,
|
||||
resourceList: routeData.resourceList,
|
||||
roleList: routeData.roleList
|
||||
})
|
||||
|
||||
// 设置动态路由
|
||||
setRoutes(this.loginInfo.menus)
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('初始化用户信息失败:', error)
|
||||
|
||||
@@ -115,9 +115,7 @@ const createAxiosInstance = (): AxiosInstance => {
|
||||
console.log('✅ Response:', config.url, data)
|
||||
|
||||
// 判断响应是否成功
|
||||
const isSuccess = data.code === 200 || data.code === '200' || data.code === '00000' || data.success === true
|
||||
|
||||
if (isSuccess) {
|
||||
if (data.code === 200 || data.success) {
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -283,8 +281,9 @@ const http = createAxiosInstance()
|
||||
* @param url 请求地址
|
||||
* @param config 请求配置
|
||||
*/
|
||||
export function get<T = any>(url: string, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
return http.get(url, config).then((res) => res.data)
|
||||
export async function get<T = any>(url: string, config?: RequestConfig): Promise<T> {
|
||||
const res = await http.get<ApiResponse<T>>(url, config)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,8 +292,9 @@ export function get<T = any>(url: string, config?: RequestConfig): Promise<ApiRe
|
||||
* @param data 请求数据
|
||||
* @param config 请求配置
|
||||
*/
|
||||
export function post<T = any>(url: string, data?: any, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
return http.post(url, data, config).then((res) => res.data)
|
||||
export async function post<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T> {
|
||||
const res = await http.post<ApiResponse<T>>(url, data, config)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,8 +303,9 @@ export function post<T = any>(url: string, data?: any, config?: RequestConfig):
|
||||
* @param data 请求数据
|
||||
* @param config 请求配置
|
||||
*/
|
||||
export function put<T = any>(url: string, data?: any, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
return http.put(url, data, config).then((res) => res.data)
|
||||
export async function put<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T> {
|
||||
const res = await http.put<ApiResponse<T>>(url, data, config)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,16 +313,18 @@ export function put<T = any>(url: string, data?: any, config?: RequestConfig): P
|
||||
* @param url 请求地址
|
||||
* @param config 请求配置
|
||||
*/
|
||||
export function del<T = any>(url: string, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
return http.delete(url, config).then((res) => res.data)
|
||||
export async function del<T = any>(url: string, config?: RequestConfig): Promise<T> {
|
||||
const res = await http.delete<ApiResponse<T>>(url, config)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用请求方法
|
||||
* @param config 请求配置
|
||||
*/
|
||||
export function request<T = any>(config: RequestConfig): Promise<ApiResponse<T>> {
|
||||
return http.request(config).then((res) => res.data)
|
||||
export async function request<T = any>(config: RequestConfig): Promise<T> {
|
||||
const res = await http.request<ApiResponse<T>>(config)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
export default http
|
||||
|
||||
@@ -249,7 +249,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/login';
|
||||
@use '@/styles/scss/login';
|
||||
|
||||
.login h1 {
|
||||
color: v-bind(TEXT_COLOR);
|
||||
|
||||
@@ -108,6 +108,6 @@ const linkOpen = (val: any) => {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/styles/scss/login';
|
||||
@import '@/styles/scss/toggle-theme';
|
||||
@use '@/styles/scss/login';
|
||||
@use '@/styles/scss/toggle-theme';
|
||||
</style>
|
||||
|
||||
@@ -219,5 +219,5 @@ provide('onLoginSuccess', async () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/login';
|
||||
@use '@/styles/scss/login';
|
||||
</style>
|
||||
|
||||
@@ -128,5 +128,5 @@ const handleUpdateFilter = (filters: DataTableFilterState, sourceColumn: DataTab
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/user';
|
||||
@use '@/styles/scss/user';
|
||||
</style>
|
||||
|
||||
@@ -116,7 +116,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/barchar.scss';
|
||||
@use '@/styles/scss/barchar.scss';
|
||||
.barchart {
|
||||
background: v-bind(BGC);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/barchar.scss';
|
||||
@use '@/styles/scss/barchar.scss';
|
||||
.barchart {
|
||||
background: v-bind(BGC);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/barchar.scss';
|
||||
@use '@/styles/scss/barchar.scss';
|
||||
|
||||
.barchart {
|
||||
background: v-bind(BGC);
|
||||
|
||||
@@ -155,7 +155,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/cardchart.scss';
|
||||
@use '@/styles/scss/cardchart.scss';
|
||||
.weekData {
|
||||
background: v-bind(BGC);
|
||||
span {
|
||||
|
||||
@@ -145,7 +145,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/cardchart.scss';
|
||||
@use '@/styles/scss/cardchart.scss';
|
||||
.weekData {
|
||||
background: v-bind(BGC);
|
||||
span {
|
||||
|
||||
@@ -94,7 +94,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/scss/cardchart.scss';
|
||||
@use '@/styles/scss/cardchart.scss';
|
||||
.weekData {
|
||||
background: v-bind(BGC);
|
||||
span {
|
||||
|
||||
@@ -75,6 +75,11 @@ export default defineConfig(({ mode }: ConfigEnv) => {
|
||||
}
|
||||
})
|
||||
],
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {}
|
||||
}
|
||||
},
|
||||
build: {
|
||||
cssCodeSplit: true, // 启用 CSS 代码拆分
|
||||
minify: 'terser', // 指定使用哪种混淆器
|
||||
|
||||
Reference in New Issue
Block a user