diff --git a/management/server/app.py b/management/server/app.py index 3525c18..9d319e0 100644 --- a/management/server/app.py +++ b/management/server/app.py @@ -7,6 +7,7 @@ from flask import Flask, request from flask_cors import CORS from routes import register_routes +from services.users.service import authenticate_user # 加载环境变量 load_dotenv(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "docker", ".env")) @@ -41,12 +42,19 @@ # 生成token -def generate_token(username): +def generate_token(user_info): # 设置令牌过期时间(例如1小时后过期) expire_time = datetime.utcnow() + timedelta(hours=1) - # 生成令牌 - token = jwt.encode({"username": username, "exp": expire_time}, JWT_SECRET, algorithm="HS256") + # 生成令牌,包含用户ID、用户名、角色和租户ID + payload = { + "user_id": user_info['id'], + "username": user_info['username'], + "role": user_info['role'], + "tenant_id": user_info.get('tenant_id'), + "exp": expire_time + } + token = jwt.encode(payload, JWT_SECRET, algorithm="HS256") return token @@ -58,19 +66,17 @@ def login(): username = data.get("username") password = data.get("password") - # 创建用户名和密码的映射 - valid_users = {ADMIN_USERNAME: ADMIN_PASSWORD} + if not username or not password: + return {"code": 1, "message": "用户名和密码不能为空"}, 400 - # 验证用户名是否存在 - if not username or username not in valid_users: - return {"code": 1, "message": "用户名不存在"}, 400 + # 从数据库验证用户,只允许超级管理员和团队负责人登录 + success, user_info, error_message = authenticate_user(username, password) - # 验证密码是否正确 - if not password or password != valid_users[username]: - return {"code": 1, "message": "密码错误"}, 400 + if not success: + return {"code": 1, "message": error_message}, 400 # 生成token - token = generate_token(username) + token = generate_token(user_info) return {"code": 0, "data": {"token": token}, "message": "登录成功"} diff --git a/management/server/routes/files/routes.py b/management/server/routes/files/routes.py index 1eff273..feac0bb 100644 --- a/management/server/routes/files/routes.py +++ b/management/server/routes/files/routes.py @@ -3,6 +3,7 @@ from flask import current_app, jsonify, request, send_file from services.files.service import batch_delete_files, delete_file, download_file_from_minio, get_file_info, get_files_list, handle_chunk_upload, merge_chunks, upload_files_to_server from services.files.utils import FileType +from services.auth import get_current_user_from_token, is_admin from .. import files_bp @@ -19,8 +20,12 @@ def upload_file(): if "files" not in request.files: return jsonify({"code": 400, "message": "未选择文件", "data": None}), 400 + # 获取当前用户信息 + current_user = get_current_user_from_token() + user_id = current_user.get('user_id') if current_user else None + files = request.files.getlist("files") - upload_result = upload_files_to_server(files) + upload_result = upload_files_to_server(files, user_id=user_id) # 返回标准格式 return jsonify({"code": 0, "message": "上传成功", "data": upload_result["data"]}) @@ -33,13 +38,22 @@ def get_files(): return "", 200 try: + # 获取当前用户信息 + current_user = get_current_user_from_token() + current_page = int(request.args.get("currentPage", 1)) page_size = int(request.args.get("size", 10)) name_filter = request.args.get("name", "") sort_by = request.args.get("sort_by", "create_time") sort_order = request.args.get("sort_order", "desc") - result, total = get_files_list(current_page, page_size, name_filter, sort_by, sort_order) + # 根据用户角色过滤文件 + # 超级管理员可以看到所有文件,团队负责人只能看到自己上传的文件 + user_id = None + if current_user and not is_admin(current_user): + user_id = current_user.get('user_id') + + result, total = get_files_list(current_page, page_size, name_filter, sort_by, sort_order, user_id) return jsonify({"code": 0, "data": {"list": result, "total": total}, "message": "获取文件列表成功"}) diff --git a/management/server/routes/knowledgebases/routes.py b/management/server/routes/knowledgebases/routes.py index 54a00d2..1f46e7a 100644 --- a/management/server/routes/knowledgebases/routes.py +++ b/management/server/routes/knowledgebases/routes.py @@ -2,6 +2,7 @@ from flask import request from services.knowledgebases.service import KnowledgebaseService +from services.auth import get_current_user_from_token, is_admin from utils import error_response, success_response from .. import knowledgebase_bp @@ -11,6 +12,9 @@ def get_knowledgebase_list(): """获取知识库列表""" try: + # 获取当前用户信息 + current_user = get_current_user_from_token() + params = { "page": int(request.args.get("currentPage", 1)), "size": int(request.args.get("size", 10)), @@ -18,6 +22,11 @@ def get_knowledgebase_list(): "sort_by": request.args.get("sort_by", "create_time"), "sort_order": request.args.get("sort_order", "desc"), } + + # 如果是团队负责人,只返回其租户的知识库 + if current_user and not is_admin(current_user): + params["tenant_id"] = current_user.get("tenant_id") + result = KnowledgebaseService.get_knowledgebase_list(**params) return success_response(result) except ValueError: diff --git a/management/server/routes/teams/routes.py b/management/server/routes/teams/routes.py index e81ac0b..aa51ef1 100644 --- a/management/server/routes/teams/routes.py +++ b/management/server/routes/teams/routes.py @@ -1,11 +1,15 @@ from flask import jsonify, request from services.teams.service import get_teams_with_pagination, get_team_by_id, delete_team, get_team_members, add_team_member, remove_team_member +from services.auth import get_current_user_from_token, is_admin from .. import teams_bp @teams_bp.route('', methods=['GET']) def get_teams(): """获取团队列表的API端点,支持分页和条件查询""" try: + # 获取当前用户信息 + current_user = get_current_user_from_token() + # 获取查询参数 current_page = int(request.args.get('currentPage', 1)) page_size = int(request.args.get('size', 10)) @@ -13,8 +17,13 @@ def get_teams(): sort_by = request.args.get("sort_by", "create_time") sort_order = request.args.get("sort_order", "desc") + # 如果是团队负责人,只返回其自己的团队 + tenant_id = None + if current_user and not is_admin(current_user): + tenant_id = current_user.get("tenant_id") + # 调用服务函数获取分页和筛选后的团队数据 - teams, total = get_teams_with_pagination(current_page, page_size, team_name, sort_by, sort_order) + teams, total = get_teams_with_pagination(current_page, page_size, team_name, sort_by, sort_order, tenant_id) # 返回符合前端期望格式的数据 return jsonify({ diff --git a/management/server/routes/users/routes.py b/management/server/routes/users/routes.py index c81eeee..a1b322b 100644 --- a/management/server/routes/users/routes.py +++ b/management/server/routes/users/routes.py @@ -1,7 +1,11 @@ +import os +import jwt from flask import jsonify, request -from services.users.service import get_users_with_pagination, delete_user, create_user, update_user, reset_user_password +from services.users.service import get_users_with_pagination, delete_user, create_user, update_user, reset_user_password, get_user_info_by_id from .. import users_bp +JWT_SECRET = os.getenv("MANAGEMENT_JWT_SECRET", "your-secret-key") + @users_bp.route('', methods=['GET']) def get_users(): """获取用户的API端点,支持分页和条件查询""" @@ -78,14 +82,46 @@ def update_user_route(user_id): @users_bp.route('/me', methods=['GET']) def get_current_user(): - return jsonify({ - "code": 0, - "data": { - "username": "admin", - "roles": ["admin"] - }, - "message": "获取用户信息成功" - }) + """获取当前登录用户信息""" + try: + # 从请求头获取token + auth_header = request.headers.get('Authorization') + if not auth_header or not auth_header.startswith('Bearer '): + return jsonify({"code": 401, "message": "未提供有效的认证令牌"}), 401 + + token = auth_header.split(' ')[1] + + # 解析token + try: + payload = jwt.decode(token, JWT_SECRET, algorithms=["HS256"]) + except jwt.ExpiredSignatureError: + return jsonify({"code": 401, "message": "令牌已过期"}), 401 + except jwt.InvalidTokenError: + return jsonify({"code": 401, "message": "无效的令牌"}), 401 + + user_id = payload.get('user_id') + + # 从数据库获取用户信息 + user_info = get_user_info_by_id(user_id) + + if not user_info: + return jsonify({"code": 401, "message": "用户不存在或无权限"}), 401 + + return jsonify({ + "code": 0, + "data": { + "username": user_info['username'], + "roles": user_info['roles'], + "role": user_info['role'], + "tenant_id": user_info.get('tenant_id') + }, + "message": "获取用户信息成功" + }) + except Exception as e: + return jsonify({ + "code": 500, + "message": f"获取用户信息失败: {str(e)}" + }), 500 @users_bp.route('//reset-password', methods=['PUT']) def reset_password_route(user_id): diff --git a/management/server/services/auth/__init__.py b/management/server/services/auth/__init__.py new file mode 100644 index 0000000..16810b2 --- /dev/null +++ b/management/server/services/auth/__init__.py @@ -0,0 +1 @@ +from .auth_utils import get_current_user_from_token, is_admin, is_team_owner diff --git a/management/server/services/auth/auth_utils.py b/management/server/services/auth/auth_utils.py new file mode 100644 index 0000000..6baca47 --- /dev/null +++ b/management/server/services/auth/auth_utils.py @@ -0,0 +1,47 @@ +import os +import jwt +from flask import request + +JWT_SECRET = os.getenv("MANAGEMENT_JWT_SECRET", "your-secret-key") + + +def get_current_user_from_token(): + """ + 从请求头的JWT token中获取当前用户信息 + 返回: dict 包含 user_id, username, role, tenant_id 或 None + """ + try: + auth_header = request.headers.get('Authorization') + if not auth_header or not auth_header.startswith('Bearer '): + return None + + token = auth_header.split(' ')[1] + + try: + payload = jwt.decode(token, JWT_SECRET, algorithms=["HS256"]) + return { + 'user_id': payload.get('user_id'), + 'username': payload.get('username'), + 'role': payload.get('role'), + 'tenant_id': payload.get('tenant_id') + } + except jwt.ExpiredSignatureError: + return None + except jwt.InvalidTokenError: + return None + except Exception: + return None + + +def is_admin(user_info): + """检查用户是否是超级管理员""" + if not user_info: + return False + return user_info.get('role') == 'admin' + + +def is_team_owner(user_info): + """检查用户是否是团队负责人""" + if not user_info: + return False + return user_info.get('role') == 'team_owner' diff --git a/management/server/services/files/service.py b/management/server/services/files/service.py index 471dccc..7a1a48c 100644 --- a/management/server/services/files/service.py +++ b/management/server/services/files/service.py @@ -49,15 +49,17 @@ def filename_type(filename): return FileType.OTHER.value -def get_files_list(current_page, page_size, name_filter="", sort_by="create_time", sort_order="desc"): +def get_files_list(current_page, page_size, name_filter="", sort_by="create_time", sort_order="desc", user_id=None): """ 获取文件列表 Args: current_page: 当前页码 page_size: 每页大小 - parent_id: 父文件夹ID name_filter: 文件名过滤条件 + sort_by: 排序字段 + sort_order: 排序顺序 + user_id: 用户ID(如果提供,则只返回该用户上传的文件) Returns: tuple: (文件列表, 总数) @@ -77,6 +79,11 @@ def get_files_list(current_page, page_size, name_filter="", sort_by="create_time if name_filter: where_clause += " AND f.name LIKE %s" params.append(f"%{name_filter}%") + + # 如果提供了user_id,则只返回该用户上传的文件 + if user_id: + where_clause += " AND f.created_by = %s" + params.append(user_id) # 验证排序字段 valid_sort_fields = ["name", "size", "type", "create_time", "create_date"] diff --git a/management/server/services/knowledgebases/service.py b/management/server/services/knowledgebases/service.py index ef4e832..ee29c10 100644 --- a/management/server/services/knowledgebases/service.py +++ b/management/server/services/knowledgebases/service.py @@ -24,8 +24,11 @@ def _get_db_connection(cls): return mysql.connector.connect(**DB_CONFIG) @classmethod - def get_knowledgebase_list(cls, page=1, size=10, name="", sort_by="create_time", sort_order="desc"): - """获取知识库列表""" + def get_knowledgebase_list(cls, page=1, size=10, name="", sort_by="create_time", sort_order="desc", tenant_id=None): + """ + 获取知识库列表 + tenant_id: 如果提供,则只返回该租户相关的知识库 + """ conn = cls._get_db_connection() cursor = conn.cursor(dictionary=True) @@ -49,16 +52,26 @@ def get_knowledgebase_list(cls, page=1, size=10, name="", sort_by="create_time", k.language, k.permission, k.created_by, + k.tenant_id, u.nickname FROM knowledgebase k - LEFT JOIN user u ON k.created_by = u.id -- 获取创建者的昵称 + LEFT JOIN user u ON k.created_by = u.id """ params = [] + where_clauses = [] if name: - query += " WHERE k.name LIKE %s" + where_clauses.append("k.name LIKE %s") params.append(f"%{name}%") + # 如果提供了tenant_id,则只返回该租户的知识库 + if tenant_id: + where_clauses.append("k.tenant_id = %s") + params.append(tenant_id) + + if where_clauses: + query += " WHERE " + " AND ".join(where_clauses) + # 添加查询排序条件 query += f" {sort_clause}" @@ -87,10 +100,18 @@ def get_knowledgebase_list(cls, page=1, size=10, name="", sort_by="create_time", result['nickname'] = "未知用户" # 获取总数 - count_query = "SELECT COUNT(*) as total FROM knowledgebase" + count_query = "SELECT COUNT(*) as total FROM knowledgebase k" + count_params = [] + count_where_clauses = [] if name: - count_query += " WHERE name LIKE %s" - cursor.execute(count_query, params[:1] if name else []) + count_where_clauses.append("k.name LIKE %s") + count_params.append(f"%{name}%") + if tenant_id: + count_where_clauses.append("k.tenant_id = %s") + count_params.append(tenant_id) + if count_where_clauses: + count_query += " WHERE " + " AND ".join(count_where_clauses) + cursor.execute(count_query, count_params) total = cursor.fetchone()["total"] cursor.close() diff --git a/management/server/services/teams/service.py b/management/server/services/teams/service.py index 1dcf0c7..087a459 100644 --- a/management/server/services/teams/service.py +++ b/management/server/services/teams/service.py @@ -3,8 +3,11 @@ from utils import generate_uuid from database import DB_CONFIG -def get_teams_with_pagination(current_page, page_size, name='', sort_by="create_time",sort_order="desc"): - """查询团队信息,支持分页和条件筛选""" +def get_teams_with_pagination(current_page, page_size, name='', sort_by="create_time", sort_order="desc", tenant_id=None): + """ + 查询团队信息,支持分页和条件筛选 + tenant_id: 如果提供,则只返回该租户的团队 + """ try: conn = mysql.connector.connect(**DB_CONFIG) cursor = conn.cursor(dictionary=True) @@ -17,6 +20,11 @@ def get_teams_with_pagination(current_page, page_size, name='', sort_by="create_ where_clauses.append("t.name LIKE %s") params.append(f"%{name}%") + # 如果提供了tenant_id,则只返回该租户的团队 + if tenant_id: + where_clauses.append("t.id = %s") + params.append(tenant_id) + # 组合WHERE子句 where_sql = "WHERE " + (" AND ".join(where_clauses) if where_clauses else "1=1") diff --git a/management/server/services/users/service.py b/management/server/services/users/service.py index 9b50bee..594afb0 100644 --- a/management/server/services/users/service.py +++ b/management/server/services/users/service.py @@ -1,9 +1,183 @@ +import os import mysql.connector import pytz from datetime import datetime -from utils import generate_uuid, encrypt_password +from utils import generate_uuid, encrypt_password, verify_password from database import DB_CONFIG +# 从环境变量获取超级管理员配置 +ADMIN_USERNAME = os.getenv("MANAGEMENT_ADMIN_USERNAME", "admin") +ADMIN_PASSWORD = os.getenv("MANAGEMENT_ADMIN_PASSWORD", "12345678") + + +def authenticate_user(username: str, password: str): + """ + 验证用户登录: + 1. 超级管理员(admin)使用环境变量验证 + 2. 团队负责人(owner)使用数据库验证 + username: 可以是email或nickname + 返回: (success: bool, user_info: dict, error_message: str) + """ + # 首先检查是否是超级管理员(使用环境变量验证) + if username == ADMIN_USERNAME: + if password == ADMIN_PASSWORD: + return True, { + 'id': 'admin', + 'username': ADMIN_USERNAME, + 'role': 'admin', + 'is_superuser': True, + 'tenant_id': None + }, None + else: + return False, None, "密码错误" + + # 其他用户使用数据库验证 + try: + conn = mysql.connector.connect(**DB_CONFIG) + cursor = conn.cursor(dictionary=True) + + # 查询用户信息 - 支持email或nickname登录 + query = """ + SELECT id, nickname, email, password, is_superuser + FROM user + WHERE email = %s OR nickname = %s + """ + cursor.execute(query, (username, username)) + user = cursor.fetchone() + + if not user: + cursor.close() + conn.close() + return False, None, "用户名或邮箱不存在" + + # 验证密码 + if not verify_password(password, user['password']): + cursor.close() + conn.close() + return False, None, "密码错误" + + user_id = user['id'] + is_superuser = user['is_superuser'] + + # 检查是否是数据库中标记的超级管理员 + if is_superuser: + cursor.close() + conn.close() + return True, { + 'id': user_id, + 'username': user['nickname'], + 'role': 'admin', + 'is_superuser': True, + 'tenant_id': None + }, None + + # 检查是否是团队负责人 (owner) + owner_query = """ + SELECT tenant_id + FROM user_tenant + WHERE user_id = %s AND role = 'owner' + """ + cursor.execute(owner_query, (user_id,)) + owner_record = cursor.fetchone() + + cursor.close() + conn.close() + + if owner_record: + return True, { + 'id': user_id, + 'username': user['nickname'], + 'role': 'team_owner', + 'is_superuser': False, + 'tenant_id': owner_record['tenant_id'] + }, None + + # 既不是超级管理员也不是团队负责人 + return False, None, "无权限登录管理系统,只有超级管理员和团队负责人可以登录" + + except mysql.connector.Error as err: + print(f"数据库错误: {err}") + return False, None, f"数据库错误: {str(err)}" + + +def get_user_info_by_id(user_id: str): + """ + 根据用户ID获取用户信息和角色 + """ + # 如果是环境变量配置的超级管理员 + if user_id == 'admin': + return { + 'id': 'admin', + 'username': ADMIN_USERNAME, + 'role': 'admin', + 'roles': ['admin'], + 'is_superuser': True, + 'tenant_id': None + } + + try: + conn = mysql.connector.connect(**DB_CONFIG) + cursor = conn.cursor(dictionary=True) + + # 查询用户信息 + query = """ + SELECT id, nickname, is_superuser + FROM user + WHERE id = %s + """ + cursor.execute(query, (user_id,)) + user = cursor.fetchone() + + if not user: + cursor.close() + conn.close() + return None + + username = user['nickname'] + is_superuser = user['is_superuser'] + + # 检查是否是数据库中标记的超级管理员 + if is_superuser: + cursor.close() + conn.close() + return { + 'id': user_id, + 'username': username, + 'role': 'admin', + 'roles': ['admin'], + 'is_superuser': True, + 'tenant_id': None + } + + # 检查是否是团队负责人 + owner_query = """ + SELECT tenant_id + FROM user_tenant + WHERE user_id = %s AND role = 'owner' + """ + cursor.execute(owner_query, (user_id,)) + owner_record = cursor.fetchone() + + cursor.close() + conn.close() + + if owner_record: + return { + 'id': user_id, + 'username': username, + 'role': 'team_owner', + 'roles': ['team_owner'], + 'is_superuser': False, + 'tenant_id': owner_record['tenant_id'] + } + + return None + + except mysql.connector.Error as err: + print(f"数据库错误: {err}") + return None + + def get_users_with_pagination(current_page, page_size, username='', email='', sort_by="create_time",sort_order="desc"): """查询用户信息,支持分页和条件筛选""" try: diff --git a/management/server/utils.py b/management/server/utils.py index 220072c..852b635 100644 --- a/management/server/utils.py +++ b/management/server/utils.py @@ -4,7 +4,7 @@ from Cryptodome.Cipher import PKCS1_v1_5 from Cryptodome.PublicKey import RSA from flask import jsonify -from werkzeug.security import generate_password_hash +from werkzeug.security import generate_password_hash, check_password_hash # 生成随机的 UUID 作为 id @@ -30,6 +30,12 @@ def encrypt_password(raw_password: str) -> str: return generate_password_hash(base64_password) +# 验证密码 +def verify_password(raw_password: str, hashed_password: str) -> bool: + base64_password = base64.b64encode(raw_password.encode()).decode() + return check_password_hash(hashed_password, base64_password) + + # 标准响应格式 def success_response(data=None, message="操作成功", code=0): return jsonify({"code": code, "message": message, "data": data}) diff --git a/management/web/src/router/index.ts b/management/web/src/router/index.ts index b0bb10e..d603910 100644 --- a/management/web/src/router/index.ts +++ b/management/web/src/router/index.ts @@ -45,20 +45,43 @@ export const constantRoutes: RouteRecordRaw[] = [ meta: { hidden: true } - }, + } +] + +/** + * @name 动态路由 + * @description 用来放置有权限 (Roles 属性) 的路由 + * @description 必须带有唯一的 Name 属性 + */ +export const dynamicRoutes: RouteRecordRaw[] = [ { path: "/", component: Layouts, - redirect: "/dashboard", + redirect: "/file", + name: "Root", + meta: { + roles: ["admin", "team_owner"] + }, + children: [] + }, + { + path: "/dashboard", + component: Layouts, + redirect: "/dashboard/index", + name: "Dashboard", + meta: { + roles: ["admin"] + }, children: [ { - path: "dashboard", + path: "index", component: () => import("@/pages/user-management/index.vue"), name: "UserManagement", meta: { title: "用户管理", svgIcon: "user-management", - affix: true + affix: true, + roles: ["admin"] } } ] @@ -67,6 +90,10 @@ export const constantRoutes: RouteRecordRaw[] = [ path: "/team", component: Layouts, redirect: "/team/index", + name: "TeamManagement", + meta: { + roles: ["admin", "team_owner"] + }, children: [ { path: "index", @@ -76,7 +103,8 @@ export const constantRoutes: RouteRecordRaw[] = [ title: "团队管理", svgIcon: "team-management", affix: false, - keepAlive: true + keepAlive: true, + roles: ["admin", "team_owner"] } } ] @@ -85,6 +113,10 @@ export const constantRoutes: RouteRecordRaw[] = [ path: "/config", component: Layouts, redirect: "/config/index", + name: "ConfigManagement", + meta: { + roles: ["admin"] + }, children: [ { path: "index", @@ -94,7 +126,8 @@ export const constantRoutes: RouteRecordRaw[] = [ title: "用户配置", svgIcon: "user-config", affix: false, - keepAlive: true + keepAlive: true, + roles: ["admin"] } } ] @@ -103,6 +136,10 @@ export const constantRoutes: RouteRecordRaw[] = [ path: "/file", component: Layouts, redirect: "/file/index", + name: "FileManagement", + meta: { + roles: ["admin", "team_owner"] + }, children: [ { path: "index", @@ -112,7 +149,8 @@ export const constantRoutes: RouteRecordRaw[] = [ title: "文件管理", svgIcon: "file", affix: false, - keepAlive: true + keepAlive: true, + roles: ["admin", "team_owner"] } } ] @@ -121,6 +159,10 @@ export const constantRoutes: RouteRecordRaw[] = [ path: "/knowledgebase", component: Layouts, redirect: "/knowledgebase/index", + name: "KnowledgeBaseManagement", + meta: { + roles: ["admin", "team_owner"] + }, children: [ { path: "index", @@ -130,7 +172,8 @@ export const constantRoutes: RouteRecordRaw[] = [ title: "知识库管理", svgIcon: "kb", affix: false, - keepAlive: true + keepAlive: true, + roles: ["admin", "team_owner"] } } ] @@ -139,65 +182,27 @@ export const constantRoutes: RouteRecordRaw[] = [ path: "/conversation", component: Layouts, redirect: "/conversation/index", + name: "ConversationManagement", + meta: { + roles: ["admin"] + }, children: [ { path: "index", component: () => import("@/pages/conversation/index.vue"), - name: "conversation", + name: "Conversation", meta: { title: "用户会话管理", svgIcon: "conversation", affix: false, - keepAlive: true + keepAlive: true, + roles: ["admin"] } } ] } ] -/** - * @name 动态路由 - * @description 用来放置有权限 (Roles 属性) 的路由 - * @description 必须带有唯一的 Name 属性 - */ -export const dynamicRoutes: RouteRecordRaw[] = [ - // { - // path: "/permission", - // component: Layouts, - // redirect: "/permission/page-level", - // name: "Permission", - // meta: { - // title: "权限演示", - // elIcon: "Lock", - // // 可以在根路由中设置角色 - // roles: ["admin", "editor"], - // alwaysShow: true - // }, - // children: [ - // { - // path: "page-level", - // component: () => import("@/pages/demo/permission/page-level.vue"), - // name: "PermissionPageLevel", - // meta: { - // title: "页面级", - // // 或者在子路由中设置角色 - // roles: ["admin"] - // } - // }, - // { - // path: "button-level", - // component: () => import("@/pages/demo/permission/button-level.vue"), - // name: "PermissionButtonLevel", - // meta: { - // title: "按钮级", - // // 如果未设置角色,则表示:该页面不需要权限,但会继承根路由的角色 - // roles: undefined - // } - // } - // ] - // } -] - /** 路由实例 */ export const router = createRouter({ history: routerConfig.history,