# `@deppon/deppon-auth`

登录与权限管理包，提供登录守卫和路由解析功能。

## 功能特性

-   ✅ **登录守卫（AuthGuard）**：基于 Cookie 的登录状态检测和路由守卫
-   ✅ **路由解析器（RouteResolver）**：将菜单树转换为 Vue Router 路由配置
-   ✅ **Token 解析工具**：提供 Cookie 读取和 Base64 解码工具函数
-   ✅ **TypeScript 支持**：完整的类型定义
-   ✅ **轻量级**：只包含核心逻辑，不包含业务状态管理

## 安装

```bash
npm install @deppon/deppon-auth
```

## 使用方式

### 1. 登录守卫（AuthGuard）

#### 方式一：与 `@deppon/deppon-router` 集成（推荐）

如果使用 `@deppon/deppon-router` 创建路由，可以直接在 `createDepponRouter` 的 `guards.beforeEach` 中使用：

```typescript
import { createApp } from 'vue';
import { createDepponRouter } from '@deppon/deppon-router';
import { createAuthGuard } from '@deppon/deppon-auth';
import routes from './routes';

const app = createApp(App);

// 创建路由并集成登录守卫
const router = createDepponRouter({
    routes,
    guards: {
        beforeEach: createAuthGuard({
            cookieKeys: ['TGC', '_TOKENUUMS', 'U'],
            checkLogin: cookies => {
                // 检查登录状态：存在 TGC 和 _TOKENUUMS 视为已登录
                return !!(cookies.TGC && cookies._TOKENUUMS);
            },
            onRequireLogin: () => {
                // 未登录时跳转到登录页
                window.location.href = BASE_API + 'login/login.action';
            },
            whiteList: ['/login', '/404'], // 可选：白名单路由
            onLoggedIn: async cookies => {
                // 可选：登录后的回调，可以在这里处理用户信息
                console.log('用户已登录', cookies);
            },
        }),
    },
});

app.use(router);
app.mount('#app');
```

#### 方式二：直接为 Router 实例注册（兼容旧用法）

如果直接使用 Vue Router，可以使用 `setupAuthGuard`：

```typescript
import { createApp } from 'vue';
import { createRouter } from 'vue-router';
import { setupAuthGuard } from '@deppon/deppon-auth';
import router from './router';

const app = createApp(App);

// 初始化登录守卫
setupAuthGuard({
    router,
    guardOptions: {
        cookieKeys: ['TGC', '_TOKENUUMS', 'U'],
        checkLogin: cookies => {
            // 检查登录状态：存在 TGC 和 _TOKENUUMS 视为已登录
            return !!(cookies.TGC && cookies._TOKENUUMS);
        },
        onRequireLogin: () => {
            // 未登录时跳转到登录页
            window.location.href = BASE_API + 'login/login.action';
        },
        whiteList: ['/login', '/404'], // 可选：白名单路由
        onLoggedIn: async cookies => {
            // 可选：登录后的回调，可以在这里处理用户信息
            console.log('用户已登录', cookies);
        },
    },
});

app.use(router);
app.mount('#app');
```

#### 使用进度条（可选）

如果需要使用进度条，需要安装 `nprogress` 并传入进度条实例：

```typescript
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import { createDepponRouter } from '@deppon/deppon-router';
import { createAuthGuard } from '@deppon/deppon-auth';

const router = createDepponRouter({
    routes,
    guards: {
        beforeEach: createAuthGuard({
            cookieKeys: ['TGC', '_TOKENUUMS'],
            checkLogin: cookies => !!(cookies.TGC && cookies._TOKENUUMS),
            onRequireLogin: () => {
                window.location.href = BASE_API + 'login/login.action';
            },
            showProgress: true,
            progressBar: {
                start: () => NProgress.start(),
                done: () => NProgress.done(),
            },
        }),
    },
});
```

### 2. 路由解析器（RouteResolver）

将菜单树转换为路由配置：

```typescript
import { resolveRoutes } from '@deppon/deppon-auth';
import { RouteRecordRaw } from 'vue-router';
import router from './router';

// 从接口获取菜单树
const menuTree = await getMenu();

// 转换为路由配置
const routes = resolveRoutes(menuTree, {
    componentResolver: path => {
        // 根据环境动态加载组件
        // 例如：path = 'user/list' -> import('@/views/user/list.vue')
        return () => import(`@/views/${path}.vue`);
    },
    metaTransformer: node => ({
        title: node.name,
        icon: node.icon,
        // 其他元信息
    }),
    pathTransformer: node => node.path || `/${node.id}`,
    nameTransformer: node => node.name || node.id,
    skipNoComponent: true, // 跳过没有 component 的节点
});

// 添加到路由
routes.forEach(route => router.addRoute(route));
```

### 3. Token 解析工具

```typescript
import { getToken, getCookies, parseToken, parseTokenJSON, base64Decode } from '@deppon/deppon-auth';

// 读取单个 Cookie
const tgc = getToken('TGC');
console.log(tgc); // TGC 的值，不存在返回 false

// 批量读取 Cookie
const cookies = getCookies(['TGC', '_TOKENUUMS', 'U']);
console.log(cookies.TGC); // TGC 的值

// 解析 Token（标准格式：Base64 编码的逗号分隔字符串）
// 格式：1,userName,userCode,deptCode,deptName
const userInfo = parseToken(cookies._TOKENUUMS);
console.log(userInfo); // { userName, userCode, deptCode, deptName }

// 解析 Token（JSON 格式，兼容其他格式）
const userInfoJSON = parseTokenJSON(cookies._TOKENUUMS);
console.log(userInfoJSON); // 解析后的用户信息对象

// Base64 解码
const decoded = base64Decode('SGVsbG8gV29ybGQ=');
console.log(decoded); // 'Hello World'
```

## API 文档

### createAuthGuard(options)

创建登录守卫函数，返回的守卫函数可直接用于 `createDepponRouter` 的 `guards.beforeEach`。

#### 参数

-   `options.cookieKeys` (string[]) - 需要检测的 Cookie 键名数组
-   `options.checkLogin` (Function) - 登录状态检查函数，接收 cookies 对象，返回 boolean
-   `options.onRequireLogin` (Function) - 未登录时的处理函数，可以是同步或异步
-   `options.showProgress` (boolean, 可选) - 是否显示进度条，默认 false
-   `options.progressBar` (ProgressBar, 可选) - 进度条实例，需要实现 start() 和 done() 方法
-   `options.whiteList` (string[], 可选) - 白名单路由路径数组，默认 []
-   `options.onLoggedIn` (Function, 可选) - 登录后的回调函数，接收 cookies 对象

#### 返回

`Function` - 守卫函数，可直接用于 `createDepponRouter` 的 `guards.beforeEach`

### setupAuthGuard(options)

直接为 Router 实例注册登录守卫（兼容旧用法）。

#### 参数

-   `options.router` (Router) - Vue Router 实例
-   `options.guardOptions` (AuthGuardOptions) - 登录守卫配置选项

### resolveRoutes(menuTree, options)

将菜单树转换为路由配置。

#### 参数

-   `menuTree` (MenuNode[]) - 菜单树数组
-   `options.componentResolver` (Function) - 组件路径解析函数，接收 componentPath，返回组件
-   `options.metaTransformer` (Function, 可选) - 路由元信息转换函数，接收 MenuNode，返回元信息对象
-   `options.pathTransformer` (Function, 可选) - 路由路径转换函数，接收 MenuNode，返回路径字符串
-   `options.nameTransformer` (Function, 可选) - 路由名称转换函数，接收 MenuNode，返回名称字符串
-   `options.skipNoComponent` (boolean, 可选) - 是否跳过没有 component 的节点，默认 false

#### 返回

`RouteRecordRaw[]` - 路由配置数组

### getCookies(keys)

读取指定的 Cookie。

#### 参数

-   `keys` (string[]) - Cookie 键名数组

#### 返回

`Record<string, string>` - Cookie 键值对对象

### getToken(key)

获取单个 Cookie 值。

#### 参数

-   `key` (string) - Cookie 键名

#### 返回

`string | false` - Cookie 值，不存在返回 false

### parseToken(token)

解析 Token 中的用户信息（标准格式：Base64 编码的逗号分隔字符串）。

#### 参数

-   `token` (string) - Token 字符串（格式：`1,userName,userCode,deptCode,deptName`）

#### 返回

`Record<string, string> | null` - 解析后的用户信息对象 `{ userName, userCode, deptCode, deptName }`，解析失败返回 null

### parseTokenJSON(token)

解析 Token 中的用户信息（JSON 格式，兼容其他格式）。

#### 参数

-   `token` (string) - Token 字符串（Base64 编码的 JSON）

#### 返回

`Record<string, any> | null` - 解析后的用户信息对象，解析失败返回 null

### base64Decode(str)

Base64 解码。

#### 参数

-   `str` (string) - Base64 编码的字符串

#### 返回

`string` - 解码后的字符串

## 类型定义

```typescript
// 菜单节点（标准格式）
interface MenuNode {
    id: string;
    text?: string; // 菜单文本/名称
    name?: string; // 菜单名称（兼容字段）
    uri?: string; // 路由路径/URI
    path?: string; // 路由路径（兼容字段）
    iconCls?: string; // 图标类名
    leaf?: boolean; // 是否为叶子节点
    component?: string; // 组件路径（用于动态导入）
    children?: MenuNode[];
    hidden?: boolean; // 是否隐藏
    [key: string]: any;
}

// 登录守卫选项
interface AuthGuardOptions {
    router: Router;
    cookieKeys: string[];
    checkLogin: (cookies: Record<string, string>) => boolean;
    onRequireLogin: () => void | Promise<void>;
    showProgress?: boolean;
    progressBar?: ProgressBar;
    whiteList?: string[];
    onLoggedIn?: (cookies: Record<string, string>) => void | Promise<void>;
}

// 路由解析器选项
interface RouteResolverOptions {
    componentResolver: (componentPath: string) => any;
    metaTransformer?: (menuNode: MenuNode) => Record<string, any>;
    pathTransformer?: (menuNode: MenuNode) => string;
    nameTransformer?: (menuNode: MenuNode) => string;
    skipNoComponent?: boolean;
}
```

## 完整示例

### 使用 `@deppon/deppon-router`（推荐）

```typescript
// main.ts
import { createApp } from 'vue';
import { createDepponRouter } from '@deppon/deppon-router';
import { createAuthGuard, parseToken } from '@deppon/deppon-auth';
import routes from './routes';

const app = createApp(App);

// 创建路由并集成登录守卫
const router = createDepponRouter({
    routes,
    guards: {
        beforeEach: createAuthGuard({
            cookieKeys: ['TGC', '_TOKENUUMS', 'U'],
            checkLogin: cookies => {
                return !!(cookies.TGC && cookies._TOKENUUMS);
            },
            onRequireLogin: () => {
                window.location.href = BASE_API + 'login/login.action';
            },
            onLoggedIn: async cookies => {
                // 解析用户信息并存储
                const userInfo = parseToken(cookies._TOKENUUMS);
                if (userInfo) {
                    // 存储到 localStorage 或 Vuex/Pinia
                    localStorage.setItem('userInfo', JSON.stringify(userInfo));
                }
            },
            whiteList: ['/login', '/404', '/500'],
        }),
    },
});

app.use(router);
app.mount('#app');
```

### 直接使用 Vue Router

```typescript
// main.ts
import { createApp } from 'vue';
import { createRouter } from 'vue-router';
import { setupAuthGuard, parseToken } from '@deppon/deppon-auth';
import router from './router';

const app = createApp(App);

// 初始化登录守卫
setupAuthGuard({
    router,
    guardOptions: {
        cookieKeys: ['TGC', '_TOKENUUMS', 'U'],
        checkLogin: cookies => {
            return !!(cookies.TGC && cookies._TOKENUUMS);
        },
        onRequireLogin: () => {
            window.location.href = BASE_API + 'login/login.action';
        },
        onLoggedIn: async cookies => {
            // 解析用户信息并存储
            const userInfo = parseToken(cookies._TOKENUUMS);
            if (userInfo) {
                // 存储到 localStorage 或 Vuex/Pinia
                localStorage.setItem('userInfo', JSON.stringify(userInfo));
            }
        },
        whiteList: ['/login', '/404', '/500'],
    },
});

app.use(router);
app.mount('#app');
```

```typescript
// main.ts
import { createApp } from 'vue';
import { createDepponRouter } from '@deppon/deppon-router';
import { createAuthGuard, resolveRoutes, getToken, parseToken } from '@deppon/deppon-auth';
import { getMenu } from './api/login';
import routes from './routes';

const app = createApp(App);

// 创建路由并集成登录守卫
const router = createDepponRouter({
    routes,
    guards: {
        beforeEach: createAuthGuard({
            cookieKeys: ['TGC', '_TOKENUUMS', 'U'],
            checkLogin: cookies => {
                return !!(cookies.TGC && cookies._TOKENUUMS);
            },
            onRequireLogin: () => {
                window.location.href = BASE_API + 'login/login.action';
            },
            onLoggedIn: async cookies => {
                // 解析用户信息并存储
                const userInfo = parseToken(cookies._TOKENUUMS);
                if (userInfo) {
                    localStorage.setItem('userInfo', JSON.stringify(userInfo));
                }
            },
            whiteList: ['/login', '/404', '/500'],
            showProgress: true,
            progressBar: {
                start: () => NProgress.start(),
                done: () => NProgress.done(),
            },
        }).beforeEach,
        afterEach: createAuthGuard({
            cookieKeys: ['TGC', '_TOKENUUMS'],
            checkLogin: () => true,
            onRequireLogin: () => {},
            showProgress: true,
            progressBar: {
                start: () => NProgress.start(),
                done: () => NProgress.done(),
            },
        }).afterEach,
    },
});

app.use(router);

// 在应用创建后初始化动态路由
app.mount('#app');

// 初始化动态路由（在 Vue 实例创建后执行）
async function initRoutes() {
    // 检查是否已登录
    const tgc = getToken('TGC');
    const tokenNums = getToken('_TOKENUUMS');

    if (tgc && tokenNums) {
        try {
            // 从接口获取菜单树
            const result = await getMenu('0');
            const menuTree = result.data.nodes || [];

            // 转换为路由配置
            const dynamicRoutes = resolveRoutes(menuTree, {
                componentResolver: path => {
                    // 动态加载组件
                    return () => import(`@/views/${path}.vue`);
                },
                metaTransformer: node => ({
                    title: node.text || node.name,
                    icon: node.iconCls || node.icon,
                    hidden: node.hidden === false ? false : node.hidden || false,
                }),
                pathTransformer: node => {
                    return node.uri === null ? '' : node.uri || node.path || `/${node.id}`;
                },
                nameTransformer: node => node.text || node.name || node.id,
                skipNoComponent: true,
            });

            // 添加到路由
            dynamicRoutes.forEach(route => router.addRoute(route));
        } catch (error) {
            console.error('[deppon-auth] 路由初始化失败:', error);
        }
    }
}

// 执行路由初始化
initRoutes();
```

**注意**：`initRoutes()` 应该在应用挂载后执行，通常在 `main.ts` 中直接调用，或者在 Vue 实例的 `created` 生命周期中调用。

## 依赖说明

### dependencies

-   `@deppon/deppon-router@^1.15.8` - Vue Router 封装包（推荐使用）
-   `@babel/runtime@^7.17.7` - Babel 运行时

### peerDependencies

-   `vue@^3.0.0` - Vue 3 框架
-   `vue-router@^4.0.0` - Vue Router 4

## 注意事项

1. **登录状态检查**：`checkLogin` 函数需要根据实际业务逻辑实现，本包只提供框架
2. **组件路径解析**：`componentResolver` 需要根据项目的组件路径规则实现
3. **进度条**：如果需要使用进度条，需要自行安装 `nprogress` 或其他进度条库
4. **用户信息管理**：本包不包含用户信息的状态管理，需要在业务项目中自行处理

## License

MIT
