mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 20:55:14 +00:00
fix(monitor): 修复定时任务权限标识错误
refactor(base_crud): 优化数据权限过滤逻辑 fix(user): 修正用户详情部门查询错误 feat(menu): 添加菜单标题字段并优化路由更新 style(auth): 优化登录页面背景样式 fix(profile): 修复表单样式问题 perf(redis): 优化缓存清除逻辑 fix(role): 移除默认角色操作限制 chore: 清理无用导入和注释
This commit is contained in:
@@ -25,7 +25,7 @@ router = APIRouter(route_class=OperationLogRoute)
|
|||||||
@router.get("/detail/{id}", summary="获取定时任务详情", description="获取定时任务详情")
|
@router.get("/detail/{id}", summary="获取定时任务详情", description="获取定时任务详情")
|
||||||
async def get_obj_detail_controller(
|
async def get_obj_detail_controller(
|
||||||
id: int = Path(..., description="定时任务ID"),
|
id: int = Path(..., description="定时任务ID"),
|
||||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:job:query"]))
|
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"]))
|
||||||
) -> JSONResponse:
|
) -> JSONResponse:
|
||||||
result_dict = await JobService.get_job_detail_service(id=id, auth=auth)
|
result_dict = await JobService.get_job_detail_service(id=id, auth=auth)
|
||||||
logger.info(f"获取定时任务详情成功 {id}")
|
logger.info(f"获取定时任务详情成功 {id}")
|
||||||
@@ -35,7 +35,7 @@ async def get_obj_detail_controller(
|
|||||||
async def get_obj_list_controller(
|
async def get_obj_list_controller(
|
||||||
page: PaginationQueryParams = Depends(),
|
page: PaginationQueryParams = Depends(),
|
||||||
search: JobQueryParams = Depends(),
|
search: JobQueryParams = Depends(),
|
||||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:job:query"]))
|
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"]))
|
||||||
) -> JSONResponse:
|
) -> JSONResponse:
|
||||||
result_dict_list = await JobService.get_job_list_service(auth=auth, search=search, order_by=page.order_by)
|
result_dict_list = await JobService.get_job_list_service(auth=auth, search=search, order_by=page.order_by)
|
||||||
result_dict = await PaginationService.get_page_obj(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
result_dict = await PaginationService.get_page_obj(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||||
@@ -45,7 +45,7 @@ async def get_obj_list_controller(
|
|||||||
@router.post("/create", summary="创建定时任务", description="创建定时任务")
|
@router.post("/create", summary="创建定时任务", description="创建定时任务")
|
||||||
async def create_obj_controller(
|
async def create_obj_controller(
|
||||||
data: JobCreateSchema,
|
data: JobCreateSchema,
|
||||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:job:create"]))
|
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:create"]))
|
||||||
) -> JSONResponse:
|
) -> JSONResponse:
|
||||||
result_dict = await JobService.create_job_service(auth=auth, data=data)
|
result_dict = await JobService.create_job_service(auth=auth, data=data)
|
||||||
logger.info(f"创建定时任务成功: {result_dict}")
|
logger.info(f"创建定时任务成功: {result_dict}")
|
||||||
@@ -54,7 +54,7 @@ async def create_obj_controller(
|
|||||||
@router.put("/update", summary="修改定时任务", description="修改定时任务")
|
@router.put("/update", summary="修改定时任务", description="修改定时任务")
|
||||||
async def update_obj_controller(
|
async def update_obj_controller(
|
||||||
data: JobUpdateSchema,
|
data: JobUpdateSchema,
|
||||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:job:update"]))
|
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:update"]))
|
||||||
) -> JSONResponse:
|
) -> JSONResponse:
|
||||||
result_dict = await JobService.update_job_service(auth=auth, data=data)
|
result_dict = await JobService.update_job_service(auth=auth, data=data)
|
||||||
logger.info(f"修改定时任务成功: {result_dict}")
|
logger.info(f"修改定时任务成功: {result_dict}")
|
||||||
@@ -63,7 +63,7 @@ async def update_obj_controller(
|
|||||||
@router.delete("/delete", summary="删除定时任务", description="删除定时任务")
|
@router.delete("/delete", summary="删除定时任务", description="删除定时任务")
|
||||||
async def delete_obj_controller(
|
async def delete_obj_controller(
|
||||||
ids: list[int] = Body(..., description="ID列表"),
|
ids: list[int] = Body(..., description="ID列表"),
|
||||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:job:delete"]))
|
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"]))
|
||||||
) -> JSONResponse:
|
) -> JSONResponse:
|
||||||
await JobService.delete_job_service(auth=auth, ids=ids)
|
await JobService.delete_job_service(auth=auth, ids=ids)
|
||||||
logger.info(f"删除定时任务成功: {id}")
|
logger.info(f"删除定时任务成功: {id}")
|
||||||
@@ -72,7 +72,7 @@ async def delete_obj_controller(
|
|||||||
@router.post('/export', summary="导出定时任务", description="导出定时任务")
|
@router.post('/export', summary="导出定时任务", description="导出定时任务")
|
||||||
async def export_obj_list_controller(
|
async def export_obj_list_controller(
|
||||||
search: JobQueryParams = Depends(),
|
search: JobQueryParams = Depends(),
|
||||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:job:export"]))
|
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:export"]))
|
||||||
) -> StreamingResponse:
|
) -> StreamingResponse:
|
||||||
# 获取全量数据
|
# 获取全量数据
|
||||||
result_dict_list = await JobService.get_job_list_service(search=search, auth=auth)
|
result_dict_list = await JobService.get_job_list_service(search=search, auth=auth)
|
||||||
@@ -89,7 +89,7 @@ async def export_obj_list_controller(
|
|||||||
|
|
||||||
@router.delete("/clear", summary="清空定时任务日志", description="清空定时任务日志")
|
@router.delete("/clear", summary="清空定时任务日志", description="清空定时任务日志")
|
||||||
async def clear_obj_log_controller(
|
async def clear_obj_log_controller(
|
||||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:job:delete"]))
|
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"]))
|
||||||
) -> JSONResponse:
|
) -> JSONResponse:
|
||||||
await JobService.clear_job_service(auth=auth)
|
await JobService.clear_job_service(auth=auth)
|
||||||
logger.info(f"清空定时任务成功")
|
logger.info(f"清空定时任务成功")
|
||||||
@@ -99,13 +99,13 @@ async def clear_obj_log_controller(
|
|||||||
async def option_obj_controller(
|
async def option_obj_controller(
|
||||||
id: int = Body(..., description="定时任务ID"),
|
id: int = Body(..., description="定时任务ID"),
|
||||||
option: int = Body(..., description="操作类型 1: 暂停 2: 恢复 3: 重启"),
|
option: int = Body(..., description="操作类型 1: 暂停 2: 恢复 3: 重启"),
|
||||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:job:update"]))
|
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:update"]))
|
||||||
) -> JSONResponse:
|
) -> JSONResponse:
|
||||||
await JobService.option_job_service(auth=auth, id=id, option=option)
|
await JobService.option_job_service(auth=auth, id=id, option=option)
|
||||||
logger.info(f"操作定时任务成功: {id}")
|
logger.info(f"操作定时任务成功: {id}")
|
||||||
return SuccessResponse(msg="操作定时任务成功")
|
return SuccessResponse(msg="操作定时任务成功")
|
||||||
|
|
||||||
@router.get("/log", summary="获取定时任务日志", description="获取定时任务日志", dependencies=[Depends(AuthPermission(permissions=["system:job:query"]))])
|
@router.get("/log", summary="获取定时任务日志", description="获取定时任务日志", dependencies=[Depends(AuthPermission(permissions=["monitor:job:query"]))])
|
||||||
async def get_job_log_controller():
|
async def get_job_log_controller():
|
||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ConfigModel(ModelBase):
|
|||||||
creator = relationship(
|
creator = relationship(
|
||||||
"UserModel",
|
"UserModel",
|
||||||
foreign_keys=creator_id,
|
foreign_keys=creator_id,
|
||||||
lazy="joined",
|
lazy="selectin",
|
||||||
post_update=True,
|
post_update=True,
|
||||||
uselist=False
|
uselist=False
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from sqlalchemy import Boolean, Column, String, Integer, DateTime, ForeignKey, Text
|
from sqlalchemy import Boolean, Column, String, Integer, DateTime, ForeignKey, Text
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from typing import Optional, List
|
|
||||||
|
|
||||||
from app.core.base_model import ModelBase
|
from app.core.base_model import ModelBase
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from typing import Optional
|
|||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.core.base_schema import BaseSchema
|
from app.core.base_schema import BaseSchema
|
||||||
|
from app.core.validator import DateTimeStr
|
||||||
|
|
||||||
class PositionCreateSchema(BaseModel):
|
class PositionCreateSchema(BaseModel):
|
||||||
"""岗位创建模型"""
|
"""岗位创建模型"""
|
||||||
@@ -22,3 +22,11 @@ class PositionUpdateSchema(PositionCreateSchema):
|
|||||||
class PositionOutSchema(PositionCreateSchema, BaseSchema):
|
class PositionOutSchema(PositionCreateSchema, BaseSchema):
|
||||||
"""岗位信息响应模型"""
|
"""岗位信息响应模型"""
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
|
||||||
|
class PositionOptionsOut(PositionCreateSchema):
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
id: int = Field(description="主键ID")
|
||||||
|
created_at: DateTimeStr = Field(description="创建时间")
|
||||||
|
updated_at: DateTimeStr = Field(description="更新时间")
|
||||||
@@ -7,7 +7,7 @@ from app.api.v1.schemas.system.dept_schema import DeptOutSchema
|
|||||||
from app.api.v1.schemas.system.menu_schema import MenuOutSchema
|
from app.api.v1.schemas.system.menu_schema import MenuOutSchema
|
||||||
from app.core.base_schema import BaseSchema
|
from app.core.base_schema import BaseSchema
|
||||||
from app.core.validator import role_permission_request_validator
|
from app.core.validator import role_permission_request_validator
|
||||||
|
from app.core.validator import DateTimeStr
|
||||||
|
|
||||||
class RoleCreateSchema(BaseModel):
|
class RoleCreateSchema(BaseModel):
|
||||||
"""角色创建模型"""
|
"""角色创建模型"""
|
||||||
@@ -43,3 +43,13 @@ class RoleOutSchema(RoleCreateSchema, BaseSchema):
|
|||||||
|
|
||||||
menus: List[MenuOutSchema] = Field(default_factory=list, description='角色菜单列表')
|
menus: List[MenuOutSchema] = Field(default_factory=list, description='角色菜单列表')
|
||||||
depts: List[DeptOutSchema] = Field(default_factory=list, description='角色部门列表')
|
depts: List[DeptOutSchema] = Field(default_factory=list, description='角色部门列表')
|
||||||
|
|
||||||
|
|
||||||
|
class RoleOptionsOut(RoleCreateSchema):
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
id: int = Field(description="主键ID")
|
||||||
|
created_at: DateTimeStr = Field(description="创建时间")
|
||||||
|
updated_at: DateTimeStr = Field(description="更新时间")
|
||||||
|
menus: List[MenuOutSchema] = Field(default_factory=list, description='角色菜单列表')
|
||||||
|
depts: List[DeptOutSchema] = Field(default_factory=list, description='角色部门列表')
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from pydantic import BaseModel, ConfigDict, Field, EmailStr, field_validator
|
from pydantic import BaseModel, ConfigDict, Field, EmailStr, field_validator
|
||||||
|
|
||||||
from app.api.v1.schemas.system.position_schema import PositionOutSchema
|
from app.api.v1.schemas.system.position_schema import PositionOptionsOut
|
||||||
from app.api.v1.schemas.system.role_schema import RoleOutSchema
|
from app.api.v1.schemas.system.role_schema import RoleOptionsOut
|
||||||
from app.api.v1.schemas.system.dept_schema import DeptOutSchema
|
from app.api.v1.schemas.system.dept_schema import DeptOutSchema
|
||||||
from app.core.validator import DateTimeStr, mobile_validator
|
from app.core.validator import DateTimeStr, mobile_validator
|
||||||
from app.core.base_schema import BaseSchema
|
from app.core.base_schema import BaseSchema
|
||||||
@@ -76,6 +76,7 @@ class UserCreateSchema(CurrentUserUpdateSchema):
|
|||||||
|
|
||||||
class UserUpdateSchema(UserCreateSchema):
|
class UserUpdateSchema(UserCreateSchema):
|
||||||
"""更新"""
|
"""更新"""
|
||||||
|
model_config = ConfigDict(from_attributes=True, exclude={"password"})
|
||||||
id: int = Field(..., description="主键ID")
|
id: int = Field(..., description="主键ID")
|
||||||
|
|
||||||
|
|
||||||
@@ -86,5 +87,5 @@ class UserOutSchema(UserCreateSchema, BaseSchema):
|
|||||||
last_login: Optional[DateTimeStr] = Field(default=None, description="最后登录时间")
|
last_login: Optional[DateTimeStr] = Field(default=None, description="最后登录时间")
|
||||||
dept_name: Optional[str] = Field(default=None, description='部门名称')
|
dept_name: Optional[str] = Field(default=None, description='部门名称')
|
||||||
dept: Optional[DeptOutSchema] = Field(default=None, description='部门')
|
dept: Optional[DeptOutSchema] = Field(default=None, description='部门')
|
||||||
roles: List[RoleOutSchema] = Field(default=[], description='角色')
|
roles: Optional[List[RoleOptionsOut]] = Field(default=[], description='角色')
|
||||||
positions: List[PositionOutSchema] = Field(default=[], description='岗位')
|
positions: Optional[List[PositionOptionsOut]] = Field(default=[], description='岗位')
|
||||||
|
|||||||
@@ -116,8 +116,11 @@ class CacheService:
|
|||||||
:param redis: Redis对象
|
:param redis: Redis对象
|
||||||
:return: 操作缓存响应信息
|
:return: 操作缓存响应信息
|
||||||
"""
|
"""
|
||||||
cache_keys = await RedisCURD(redis).get_keys
|
cache_keys = await RedisCURD(redis).get_keys()
|
||||||
if cache_keys:
|
if cache_keys:
|
||||||
await RedisCURD(redis).delete(*cache_keys)
|
await RedisCURD(redis).delete(*cache_keys)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# 避免清除所有的缓存,而采用上面的方式,只清除本系统内指定的所有缓存
|
||||||
|
# return await RedisCURD(redis).clear()
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ class OnlineService:
|
|||||||
async def clear_online_service(cls, redis: Redis) -> bool:
|
async def clear_online_service(cls, redis: Redis) -> bool:
|
||||||
"""强制下线在线用户"""
|
"""强制下线在线用户"""
|
||||||
# 删除 token
|
# 删除 token
|
||||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:*")
|
await RedisCURD(redis).clear(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:*")
|
||||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:*")
|
await RedisCURD(redis).clear(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:*")
|
||||||
|
|
||||||
logger.info(f"清除所有在线用户会话成功")
|
logger.info(f"清除所有在线用户会话成功")
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ class UserService:
|
|||||||
raise CustomException(msg="用户不存在")
|
raise CustomException(msg="用户不存在")
|
||||||
# 获取部门名称
|
# 获取部门名称
|
||||||
if user.dept_id:
|
if user.dept_id:
|
||||||
dept = await DeptCRUD(auth).get_by_id_crud(id=user.dept_id)
|
dept = await DeptCRUD(auth).get_by_id_crud(id=auth.user.dept_id)
|
||||||
user.dept_name = dept.name if dept else None
|
user.dept_name = dept.name if dept else None
|
||||||
user_dict = UserOutSchema.model_validate(user).model_dump()
|
user_dict = UserOutSchema.model_validate(user).model_dump()
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ class UserService:
|
|||||||
MenuOutSchema.model_validate(menu).model_dump()
|
MenuOutSchema.model_validate(menu).model_dump()
|
||||||
for role in auth.user.roles
|
for role in auth.user.roles
|
||||||
for menu in role.menus
|
for menu in role.menus
|
||||||
if menu.status and menu.type in [1, 2]
|
if menu.status and menu.type in [1, 2, 4]
|
||||||
]
|
]
|
||||||
user_dict["menus"] = menus
|
user_dict["menus"] = menus
|
||||||
return user_dict
|
return user_dict
|
||||||
@@ -259,7 +259,7 @@ class UserService:
|
|||||||
|
|
||||||
# 更新密码
|
# 更新密码
|
||||||
new_password_hash = PwdUtil.set_password_hash(password=data.password)
|
new_password_hash = PwdUtil.set_password_hash(password=data.password)
|
||||||
new_user = await UserCRUD(auth).change_password_crud(id=user.id, password_hash=new_password_hash)
|
new_user = await UserCRUD(auth).change_password_crud(id=data.id, password_hash=new_password_hash)
|
||||||
return UserOutSchema.model_validate(new_user).model_dump()
|
return UserOutSchema.model_validate(new_user).model_dump()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ class Settings(BaseSettings):
|
|||||||
UPLOAD_MACHINE: str = 'A' # 上传机器标识
|
UPLOAD_MACHINE: str = 'A' # 上传机器标识
|
||||||
ALLOWED_EXTENSIONS: list[str] = [ # 允许的文件类型
|
ALLOWED_EXTENSIONS: list[str] = [ # 允许的文件类型
|
||||||
# 图片
|
# 图片
|
||||||
'.bmp', '.gif', '.jpg', '.jpeg', '.png', '.ico',
|
'.bmp', '.gif', '.jpg', '.jpeg', '.png', '.ico', '.svg',
|
||||||
# 文档
|
# 文档
|
||||||
'.csv', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.html', '.htm', '.txt', '.pdf',
|
'.csv', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.html', '.htm', '.txt', '.pdf',
|
||||||
# 压缩包
|
# 压缩包
|
||||||
|
|||||||
@@ -55,8 +55,13 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
|||||||
if hasattr(self.model, "creator"):
|
if hasattr(self.model, "creator"):
|
||||||
sql = sql.options(selectinload(self.model.creator))
|
sql = sql.options(selectinload(self.model.creator))
|
||||||
|
|
||||||
|
# sql = await self.__filter_permissions(sql)
|
||||||
|
|
||||||
result: Result = await self.db.execute(sql)
|
result: Result = await self.db.execute(sql)
|
||||||
obj = result.scalars().unique().first()
|
obj = result.scalars().unique().first()
|
||||||
|
# if not obj:
|
||||||
|
# raise CustomException(msg="该信息不存在")
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise CustomException(msg=f"获取查询失败: {str(e)}")
|
raise CustomException(msg=f"获取查询失败: {str(e)}")
|
||||||
@@ -82,6 +87,9 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
|||||||
.where(*conditions)
|
.where(*conditions)
|
||||||
.order_by(*self.__order_by(order))
|
.order_by(*self.__order_by(order))
|
||||||
.distinct())
|
.distinct())
|
||||||
|
# 预加载creator关系
|
||||||
|
if hasattr(self.model, "creator"):
|
||||||
|
sql = sql.options(selectinload(self.model.creator))
|
||||||
sql = await self.__filter_permissions(sql)
|
sql = await self.__filter_permissions(sql)
|
||||||
result: Result = await self.db.execute(sql)
|
result: Result = await self.db.execute(sql)
|
||||||
return result.scalars().unique().all()
|
return result.scalars().unique().all()
|
||||||
@@ -219,14 +227,18 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
|||||||
|
|
||||||
async def __filter_permissions(self, sql: Select[Any]) -> Select[Any]:
|
async def __filter_permissions(self, sql: Select[Any]) -> Select[Any]:
|
||||||
"""过滤数据权限"""
|
"""过滤数据权限"""
|
||||||
# 1. 如果模型没有creator字段,则不需要过滤
|
# 如果不需要检查数据权限,则直接返回
|
||||||
|
if not self.current_user or not self.auth.check_data_scope:
|
||||||
|
return sql
|
||||||
|
|
||||||
|
# 1. 如果模型没有创建人creator字段,则不需要权限判断
|
||||||
if not hasattr(self.model, "creator"):
|
if not hasattr(self.model, "creator"):
|
||||||
return sql
|
return sql
|
||||||
|
|
||||||
sql = sql.options(selectinload(self.model.creator))
|
sql = sql.options(selectinload(self.model.creator))
|
||||||
|
|
||||||
# 2. 超级管理员可以查看所有数据
|
# 2. 超级管理员可以查看所有数据
|
||||||
if not self.current_user or self.current_user.is_superuser:
|
if self.current_user.is_superuser:
|
||||||
return sql
|
return sql
|
||||||
|
|
||||||
# 3. 如果用户没有部门或角色,则只能查看自己的数据
|
# 3. 如果用户没有部门或角色,则只能查看自己的数据
|
||||||
@@ -243,24 +255,22 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
|||||||
# 3: 本部门及以下数据权限
|
# 3: 本部门及以下数据权限
|
||||||
# 4: 全部数据权限
|
# 4: 全部数据权限
|
||||||
# 5: 自定义数据权限
|
# 5: 自定义数据权限
|
||||||
# 5. 处理各种数据权限范围
|
|
||||||
|
# 获取当前用户所绑定角色的数据权限范围
|
||||||
for role in self.current_user.roles:
|
for role in self.current_user.roles:
|
||||||
# 如果有全部数据权限,直接返回所有数据
|
for dept in role.depts:
|
||||||
if role.data_scope == 4:
|
dept_ids.add(dept.id)
|
||||||
return sql
|
|
||||||
|
|
||||||
data_scopes.add(role.data_scope)
|
data_scopes.add(role.data_scope)
|
||||||
# 如果是自定义权限,添加自定义部门
|
|
||||||
if role.data_scope == 5:
|
|
||||||
dept_ids.update({dept.id for dept in role.depts})
|
|
||||||
|
|
||||||
conditions = []
|
if 4 in data_scopes:
|
||||||
|
# 4、全部数据权限
|
||||||
# 5. 处理各种数据权限范围
|
return sql
|
||||||
|
|
||||||
if 1 in data_scopes:
|
if 1 in data_scopes:
|
||||||
# 1、仅本人数据
|
# 1、仅本人数据
|
||||||
conditions.append(self.model.creator_id == self.current_user.id)
|
return sql.where(self.model.creator_id == self.current_user.id)
|
||||||
|
|
||||||
if 2 in data_scopes:
|
if 2 in data_scopes:
|
||||||
# 2、本部门数据
|
# 2、本部门数据
|
||||||
dept_ids.add(self.current_user.dept_id)
|
dept_ids.add(self.current_user.dept_id)
|
||||||
@@ -270,16 +280,11 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
|||||||
dept_objs = await CRUDBase(DeptModel, self.auth).list()
|
dept_objs = await CRUDBase(DeptModel, self.auth).list()
|
||||||
id_map = get_child_id_map(dept_objs)
|
id_map = get_child_id_map(dept_objs)
|
||||||
dept_child_ids = get_child_recursion(id=self.current_user.dept_id, id_map=id_map)
|
dept_child_ids = get_child_recursion(id=self.current_user.dept_id, id_map=id_map)
|
||||||
dept_ids.update(dept_child_ids)
|
for child_id in dept_child_ids:
|
||||||
|
dept_ids.add(child_id)
|
||||||
if dept_ids:
|
|
||||||
conditions.append(self.model.creator.has(UserModel.dept_id.in_(list(dept_ids))))
|
# 5、自定义权限
|
||||||
|
return sql.where(self.model.creator.has(UserModel.dept_id.in_(list(dept_ids))))
|
||||||
# 6. 组合所有条件
|
|
||||||
if conditions:
|
|
||||||
return sql.where(and_(*conditions))
|
|
||||||
|
|
||||||
return sql.where(self.model.creator_id == self.current_user.id)
|
|
||||||
|
|
||||||
def __order_by(self, order_by: List[Dict[str, str]]) -> List[ColumnElement]:
|
def __order_by(self, order_by: List[Dict[str, str]]) -> List[ColumnElement]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -103,8 +103,13 @@ export interface MenuForm {
|
|||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
always_show?: boolean;
|
always_show?: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
params?: { key: string; value: string; }[];
|
params?: KeyValue[];
|
||||||
affix?: boolean;
|
affix?: boolean;
|
||||||
status?: boolean;
|
status?: boolean;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface KeyValue {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@ export const UserAPI = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getUserDetail(query: number) {
|
getUserDetail(query: number) {
|
||||||
return request<ApiResponse>({
|
return request<ApiResponse<UserInfo>>({
|
||||||
url: `/system/user/detail/${query}`,
|
url: `/system/user/detail/${query}`,
|
||||||
method: "get",
|
method: "get",
|
||||||
});
|
});
|
||||||
@@ -246,9 +246,9 @@ export interface UserForm {
|
|||||||
dept_id?: number;
|
dept_id?: number;
|
||||||
dept_name?: string;
|
dept_name?: string;
|
||||||
role_ids?: number[];
|
role_ids?: number[];
|
||||||
roleNames?: string;
|
roleNames?: string[];
|
||||||
position_ids?: number[];
|
position_ids?: number[];
|
||||||
positionNames?: string;
|
positionNames?: string[];
|
||||||
password?: string;
|
password?: string;
|
||||||
gender?: number;
|
gender?: number;
|
||||||
email?: string;
|
email?: string;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const useUserStore = defineStore("user", {
|
|||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
async getUserInfo() {
|
async getUserInfo() {
|
||||||
const response = await UserAPI.getCurrentUserInfo();
|
const response = await UserAPI.getCurrentUserInfo();
|
||||||
const routers = response.data.data.menus;
|
const routers: MenuTable[] = response.data.data.menus || [];
|
||||||
delete response.data.data.menus;
|
delete response.data.data.menus;
|
||||||
this.setRoute(routers);
|
this.setRoute(routers);
|
||||||
this.basicInfo = { ...this.basicInfo, ...response.data.data };
|
this.basicInfo = { ...this.basicInfo, ...response.data.data };
|
||||||
@@ -37,7 +37,8 @@ export const useUserStore = defineStore("user", {
|
|||||||
this.basicInfo = info;
|
this.basicInfo = info;
|
||||||
},
|
},
|
||||||
|
|
||||||
setRoute(routers: any) {
|
// 设置路由
|
||||||
|
setRoute(routers: MenuTable[]) {
|
||||||
this.routeList = routers;
|
this.routeList = routers;
|
||||||
this.hasGetRoute = true;
|
this.hasGetRoute = true;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -122,18 +122,18 @@
|
|||||||
<span>基本设置</span>
|
<span>基本设置</span>
|
||||||
</template>
|
</template>
|
||||||
<div>
|
<div>
|
||||||
<el-form ref="ruleFormRef" :model="infoFormState" :rules="rules" :inline="true" label-suffix=":">
|
<el-form ref="ruleFormRef" :model="infoFormState" :rules="rules" :inline="true" label-width="80px" label-suffix=":">
|
||||||
|
|
||||||
<el-form-item label="姓名" name="name">
|
<el-form-item label="姓名" name="name">
|
||||||
<el-input v-model="infoFormState.name" placeholder="请输入姓名" prefix-icon="User" clearable />
|
<el-input v-model="infoFormState.name" placeholder="请输入姓名" prefix-icon="User" clearable style="width: 240px;" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="手机号" name="mobile">
|
<el-form-item label="手机号" name="mobile">
|
||||||
<el-input v-model="infoFormState.mobile" placeholder="请输入手机号码" prefix-icon="Phone" clearable />
|
<el-input v-model="infoFormState.mobile" placeholder="请输入手机号码" prefix-icon="Phone" clearable style="width: 240px;" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="邮箱" name="email">
|
<el-form-item label="邮箱" name="email">
|
||||||
<el-input v-model="infoFormState.email" placeholder="请输入邮箱" prefix-icon="Message" clearable />
|
<el-input v-model="infoFormState.email" placeholder="请输入邮箱" prefix-icon="Message" clearable style="width: 240px;" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="性别" name="gender">
|
<el-form-item label="性别" name="gender">
|
||||||
@@ -159,9 +159,9 @@
|
|||||||
<span>安全设置</span>
|
<span>安全设置</span>
|
||||||
</template>
|
</template>
|
||||||
<div>
|
<div>
|
||||||
<el-form ref="ruleFormRef" :model="passwordFormState" :rules="resetPasswordRules" label-suffix=":">
|
<el-form ref="ruleFormRef" :model="passwordFormState" :rules="resetPasswordRules" label-width="80px" label-suffix=":">
|
||||||
<el-form-item label="当前密码" name="old_password">
|
<el-form-item label="当前密码" name="old_password">
|
||||||
<el-input v-model.trim="passwordFormState.old_password" :placeholder="t('login.password')" type="password" show-password clearable>
|
<el-input v-model.trim="passwordFormState.old_password" :placeholder="t('login.password')" type="password" show-password clearable style="width: 240px;">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<Lock />
|
<Lock />
|
||||||
</template>
|
</template>
|
||||||
@@ -169,7 +169,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="新密码" name="new_password">
|
<el-form-item label="新密码" name="new_password">
|
||||||
<el-input v-model.trim="passwordFormState.new_password" type="password" :placeholder="t('login.newPassword')" show-password clearable>
|
<el-input v-model.trim="passwordFormState.new_password" type="password" :placeholder="t('login.newPassword')" show-password clearable style="width: 240px;">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<Key />
|
<Key />
|
||||||
</template>
|
</template>
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="确认新密码" name="confirm_password">
|
<el-form-item label="确认新密码" name="confirm_password">
|
||||||
<el-input v-model.trim="passwordFormState.confirm_password" type="password" :placeholder="t('login.message.password.confirm')" show-password clearable>
|
<el-input v-model.trim="passwordFormState.confirm_password" type="password" :placeholder="t('login.message.password.confirm')" show-password clearable style="width: 240px;">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<Check />
|
<Check />
|
||||||
</template>
|
</template>
|
||||||
@@ -507,4 +507,13 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 修复表单输入框清除按钮导致的宽度变化问题 */
|
||||||
|
.el-input {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -879,7 +879,6 @@ const handleClear = () => {
|
|||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await JobAPI.clearJob();
|
await JobAPI.clearJob();
|
||||||
ElMessage.success("清空成功");
|
|
||||||
handleResetQuery();
|
handleResetQuery();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -101,10 +101,10 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormInstance } from "element-plus";
|
import type { FormInstance } from "element-plus";
|
||||||
import { LocationQuery, RouteLocationRaw, useRoute } from "vue-router";
|
import { LocationQuery, RouteLocationRaw, useRoute, useRouter } from "vue-router";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { onActivated, onMounted, watch } from "vue";
|
||||||
import AuthAPI, {type LoginFormData, type CaptchaInfo } from "@/api/system/auth";
|
import AuthAPI, {type LoginFormData, type CaptchaInfo } from "@/api/system/auth";
|
||||||
import router from "@/router";
|
|
||||||
import { useAppStore, useUserStore } from "@/store";
|
import { useAppStore, useUserStore } from "@/store";
|
||||||
import CommonWrapper from "@/components/CommonWrapper/index.vue";
|
import CommonWrapper from "@/components/CommonWrapper/index.vue";
|
||||||
|
|
||||||
@@ -113,9 +113,27 @@ const userStore = useUserStore();
|
|||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// 组件挂载时获取验证码
|
||||||
onMounted(() => getCaptcha());
|
onMounted(() => getCaptcha());
|
||||||
|
|
||||||
|
// 组件激活时获取验证码(适用于KeepAlive缓存的情况)
|
||||||
|
onActivated(() => {
|
||||||
|
getCaptcha();
|
||||||
|
// 重置登录表单
|
||||||
|
loginForm.captcha = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听路由变化,确保每次进入登录页面都有最新验证码
|
||||||
|
watch(
|
||||||
|
() => route.fullPath,
|
||||||
|
() => {
|
||||||
|
getCaptcha();
|
||||||
|
loginForm.captcha = '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const loginFormRef = ref<FormInstance>();
|
const loginFormRef = ref<FormInstance>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
// 是否大写锁定
|
// 是否大写锁定
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login-container">
|
<div class="login-container" :style="{ 'background-image': configStore.configData?.sys_login_background?.config_value ? `url(${configStore.configData.sys_login_background.config_value})` : '/background.svg' }">
|
||||||
<!-- 右侧切换主题、语言按钮 -->
|
<!-- 右侧切换主题、语言按钮 -->
|
||||||
<div class="action-bar">
|
<div class="action-bar">
|
||||||
<el-tooltip :content="t('login.themeToggle')" placement="bottom">
|
<el-tooltip :content="t('login.themeToggle')" placement="bottom">
|
||||||
@@ -93,17 +93,10 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 添加伪元素作为背景层
|
// 添加伪元素作为背景层
|
||||||
.login-container::before {
|
.login-container {
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: -1;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
content: "";
|
|
||||||
background: url("/background.svg");
|
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-bar {
|
.action-bar {
|
||||||
|
|||||||
@@ -242,6 +242,10 @@
|
|||||||
<el-input v-model="formData.name" placeholder="请输入菜单名称" />
|
<el-input v-model="formData.name" placeholder="请输入菜单名称" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="菜单标题" prop="title">
|
||||||
|
<el-input v-model="formData.title" placeholder="请输入菜单标题" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="菜单类型" prop="type">
|
<el-form-item label="菜单类型" prop="type">
|
||||||
<el-radio-group v-model="formData.type" @change="handleMenuTypeChange">
|
<el-radio-group v-model="formData.type" @change="handleMenuTypeChange">
|
||||||
<el-radio :value="MenuTypeEnum.CATALOG">目录</el-radio>
|
<el-radio :value="MenuTypeEnum.CATALOG">目录</el-radio>
|
||||||
@@ -255,7 +259,7 @@
|
|||||||
<el-input v-model="formData.route_path" placeholder="请输入外链完整路径" />
|
<el-input v-model="formData.route_path" placeholder="请输入外链完整路径" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" prop="routeName">
|
<el-form-item prop="routeName">
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="flex-y-center">
|
<div class="flex-y-center">
|
||||||
路由名称
|
路由名称
|
||||||
@@ -464,6 +468,7 @@ defineOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
import { useAppStore } from "@/store/modules/app.store";
|
import { useAppStore } from "@/store/modules/app.store";
|
||||||
|
import { useUserStore } from "@/store/modules/user.store";
|
||||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||||
|
|
||||||
import MenuAPI, { MenuPageQuery, MenuForm, MenuTable } from "@/api/system/menu";
|
import MenuAPI, { MenuPageQuery, MenuForm, MenuTable } from "@/api/system/menu";
|
||||||
@@ -471,6 +476,7 @@ import { MenuTypeEnum } from "@/enums/system/menu.enum";
|
|||||||
import { formatTree, listToTree } from "@/utils/common";
|
import { formatTree, listToTree } from "@/utils/common";
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const queryFormRef = ref();
|
const queryFormRef = ref();
|
||||||
const dataFormRef = ref();
|
const dataFormRef = ref();
|
||||||
@@ -514,7 +520,7 @@ const formData = reactive<MenuForm>({
|
|||||||
hidden: false,
|
hidden: false,
|
||||||
always_show: false,
|
always_show: false,
|
||||||
title: '',
|
title: '',
|
||||||
params: [] as { key: string; value: string }[],
|
params: undefined,
|
||||||
affix: false,
|
affix: false,
|
||||||
status: true,
|
status: true,
|
||||||
description: undefined,
|
description: undefined,
|
||||||
@@ -716,6 +722,7 @@ async function handleSubmit() {
|
|||||||
if (id) {
|
if (id) {
|
||||||
try {
|
try {
|
||||||
await MenuAPI.updateMenu(formData)
|
await MenuAPI.updateMenu(formData)
|
||||||
|
await userStore.getUserInfo();
|
||||||
dialogVisible.visible = false;
|
dialogVisible.visible = false;
|
||||||
resetForm();
|
resetForm();
|
||||||
handleResetQuery();
|
handleResetQuery();
|
||||||
@@ -727,6 +734,7 @@ async function handleSubmit() {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
await MenuAPI.createMenu(formData)
|
await MenuAPI.createMenu(formData)
|
||||||
|
await userStore.getUserInfo();
|
||||||
dialogVisible.visible = false;
|
dialogVisible.visible = false;
|
||||||
resetForm();
|
resetForm();
|
||||||
handleResetQuery();
|
handleResetQuery();
|
||||||
@@ -750,6 +758,7 @@ async function handleDelete(ids: number[]) {
|
|||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await MenuAPI.deleteMenu(ids);
|
await MenuAPI.deleteMenu(ids);
|
||||||
|
await userStore.getUserInfo();
|
||||||
handleResetQuery();
|
handleResetQuery();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -146,8 +146,7 @@
|
|||||||
size="small"
|
size="small"
|
||||||
link
|
link
|
||||||
icon="document"
|
icon="document"
|
||||||
@click="scope.row.id === 1 ? ElMessage.warning('系统默认角色,不可操作') : handleOpenDialog('detail', scope.row.id)"
|
@click="handleOpenDialog('detail', scope.row.id)"
|
||||||
:disabled="scope.row.id === 1"
|
|
||||||
>详情</el-button>
|
>详情</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|||||||
@@ -257,7 +257,7 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="手机号码" prop="mobile">
|
<el-form-item label="手机号" prop="mobile">
|
||||||
<el-input v-model="formData.mobile" placeholder="请输入手机号码" maxlength="11" />
|
<el-input v-model="formData.mobile" placeholder="请输入手机号码" maxlength="11" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="密码" prop="password">
|
<el-form-item label="密码" prop="password" v-if="dialogVisible.type === 'create'">
|
||||||
<el-input v-model="formData.password" placeholder="请输入密码" type="password" show-password clearable />
|
<el-input v-model="formData.password" placeholder="请输入密码" type="password" show-password clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
@@ -548,6 +548,8 @@ async function handleCloseDialog() {
|
|||||||
// 打开弹窗
|
// 打开弹窗
|
||||||
async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: number) {
|
async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: number) {
|
||||||
dialogVisible.type = type;
|
dialogVisible.type = type;
|
||||||
|
// 动态设置密码验证规则
|
||||||
|
rules.password[0].required = type === 'create';
|
||||||
if (id) {
|
if (id) {
|
||||||
const response = await UserAPI.getUserDetail(id);
|
const response = await UserAPI.getUserDetail(id);
|
||||||
if (type === 'detail') {
|
if (type === 'detail') {
|
||||||
@@ -556,6 +558,9 @@ async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: numbe
|
|||||||
} else if (type === 'update') {
|
} else if (type === 'update') {
|
||||||
dialogVisible.title = "修改用户";
|
dialogVisible.title = "修改用户";
|
||||||
Object.assign(formData, response.data.data);
|
Object.assign(formData, response.data.data);
|
||||||
|
// 确保角色和岗位ID正确设置
|
||||||
|
formData.role_ids = (response.data.data.roles || []).map(item => item.id as number);
|
||||||
|
formData.position_ids = (response.data.data.positions || []).map(item => item.id as number);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dialogVisible.title = "新增用户";
|
dialogVisible.title = "新增用户";
|
||||||
@@ -596,8 +601,11 @@ async function handleSubmit() {
|
|||||||
// 根据弹窗传入的参数(deatil\create\update)判断走什么逻辑
|
// 根据弹窗传入的参数(deatil\create\update)判断走什么逻辑
|
||||||
const id = formData.id;
|
const id = formData.id;
|
||||||
if (id) {
|
if (id) {
|
||||||
try {
|
try {
|
||||||
await UserAPI.updateUser({ id, ...formData })
|
// 编辑用户时,不传递密码字段
|
||||||
|
const updateData = { id, ...formData };
|
||||||
|
delete updateData.password;
|
||||||
|
await UserAPI.updateUser(updateData)
|
||||||
dialogVisible.visible = false;
|
dialogVisible.visible = false;
|
||||||
resetForm();
|
resetForm();
|
||||||
handleCloseDialog();
|
handleCloseDialog();
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default defineConfig(({ mode }: ConfigEnv) => {
|
|||||||
target: env.VITE_API_BASE_URL, // 代理目标地址:https://后端地址
|
target: env.VITE_API_BASE_URL, // 代理目标地址:https://后端地址
|
||||||
secure: false, // 请求是否https
|
secure: false, // 请求是否https
|
||||||
changeOrigin: true, // 是否跨域
|
changeOrigin: true, // 是否跨域
|
||||||
// rewrite: (path) => path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
|
rewrite: (path) => path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user