Skip to content

Packages 详解

CondorAdmin 前端采用 Monorepo 架构,将公共模块抽离为独立的内部包(Packages),提高代码复用性和可维护性。

Packages 架构

整体结构

packages/
├── materials/          # UI 布局组件
├── hooks/              # 组合式函数
├── utils/              # 工具函数
├── axios/              # Axios HTTP 客户端
├── alova/              # Alova HTTP 客户端(可选)
├── ofetch/             # oFetch HTTP 客户端(可选)
├── color/              # 颜色工具
├── uno-preset/         # UnoCSS 预设
├── elegant-router-core/# 路由核心
└── scripts/            # 构建脚本

依赖关系

src/main.ts

materials (布局组件)

hooks (组合式函数)

utils (工具函数) + axios/alova/ofetch (HTTP客户端)

color (颜色处理)

核心 Packages

1. @sa/materials

功能:提供管理后台的核心布局组件。

位置packages/materials/

导出内容

typescript
// packages/materials/src/index.ts
export { 
  AdminLayout,          // 主布局组件
  LAYOUT_MAX_Z_INDEX,   // 布局最大 z-index
  LAYOUT_SCROLL_EL_ID,  // 滚动容器 ID
  PageTab,              // 标签页组件
  SimpleScrollbar       // 简易滚动条
};

核心组件

AdminLayout

管理后台主布局,包含头部、侧边栏、主内容区、标签页。

vue
<template>
  <AdminLayout>
    <template #header>
      <!-- 自定义头部 -->
    </template>
    <template #sider>
      <!-- 自定义侧边栏 -->
    </template>
    <template #tab>
      <!-- 自定义标签页 -->
    </template>
    <router-view />
  </AdminLayout>
</template>

配置项

  • mode:布局模式(vertical/horizontal)
  • scrollElId:滚动容器 ID
  • scrollMode:滚动模式(wrapper/content)

PageTab

标签页组件,支持多种风格。

typescript
// 标签页样式
type TabMode = 'chrome' | 'button' | 'slider';

使用

vue
<PageTab
  :tabs="tabs"
  :active-tab="activeTab"
  mode="chrome"
  @close="handleClose"
/>

2. @sa/hooks

功能:提供可复用的 Vue 组合式函数(Composables)。

位置packages/hooks/

核心 Hooks

useBoolean

管理布尔状态及其切换。

typescript
import { useBoolean } from '@sa/hooks';

const [visible, { setTrue, setFalse, toggle }] = useBoolean(false);

// 使用
setTrue();    // visible.value = true
setFalse();   // visible.value = false
toggle();     // visible.value = !visible.value

useLoading

管理加载状态。

typescript
import { useLoading } from '@sa/hooks';

const { loading, startLoading, endLoading } = useLoading();

async function fetchData() {
  startLoading();
  try {
    await api.getData();
  } finally {
    endLoading();
  }
}

useCountDown

倒计时功能。

typescript
import { useCountDown } from '@sa/hooks';

const { count, start, stop, reset } = useCountDown(60);

// 开始倒计时
start(() => {
  console.log('倒计时结束');
});

3. @sa/utils

功能:提供通用工具函数。

位置packages/utils/

核心工具

类型判断

typescript
import { isArray, isObject, isString, isNumber } from '@sa/utils';

isArray([1, 2, 3]);      // true
isObject({ a: 1 });      // true
isString('hello');       // true
isNumber(123);           // true

数据转换

typescript
import { objectToCamelCase, objectToSnakeCase } from '@sa/utils';

// 对象键名转驼峰
const camel = objectToCamelCase({ user_name: 'admin' });
// { userName: 'admin' }

// 对象键名转下划线
const snake = objectToSnakeCase({ userName: 'admin' });
// { user_name: 'admin' }

防抖节流

typescript
import { debounce, throttle } from '@sa/utils';

// 防抖:300ms 内多次调用只执行最后一次
const debouncedSearch = debounce((keyword: string) => {
  search(keyword);
}, 300);

// 节流:300ms 内最多执行一次
const throttledScroll = throttle(() => {
  handleScroll();
}, 300);

存储工具

typescript
import { localStg, sessionStg } from '@sa/utils';

// LocalStorage
localStg.set('token', 'abc123');
const token = localStg.get('token');
localStg.remove('token');

// SessionStorage
sessionStg.set('tempData', { id: 1 });
const data = sessionStg.get('tempData');

4. @sa/axios

功能:基于 Axios 的 HTTP 客户端,内置拦截器和错误处理。

位置packages/axios/

特性

  • 请求/响应拦截器
  • 统一错误处理
  • Token 自动注入
  • 请求取消
  • 请求重试

使用示例

typescript
import { createRequest } from '@sa/axios';

// 创建实例
const request = createRequest({
  baseURL: import.meta.env.VITE_SERVICE_BASE_URL,
  timeout: 10000
});

// 请求拦截器
request.interceptors.request.use(config => {
  const token = localStg.get('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器
request.interceptors.response.use(
  response => {
    const { code, data, msg } = response.data;
    if (code === '0000') {
      return { data, error: null };
    }
    return { data: null, error: { code, msg } };
  },
  error => {
    return { data: null, error };
  }
);

5. @sa/color

功能:颜色处理工具,支持颜色格式转换、混合、主题色生成。

位置packages/color/

核心功能

颜色格式转换

typescript
import { hexToRgb, rgbToHex, hexToHsl } from '@sa/color';

hexToRgb('#3b82f6');     // { r: 59, g: 130, b: 246 }
rgbToHex(59, 130, 246);  // '#3b82f6'
hexToHsl('#3b82f6');     // { h: 217, s: 91, l: 60 }

颜色混合

typescript
import { mixColor } from '@sa/color';

// 混合两种颜色
const mixed = mixColor('#3b82f6', '#ffffff', 0.5);
// 返回中间色

主题色生成

typescript
import { generateColorPalette } from '@sa/color';

// 生成主题色板(10个层级)
const palette = generateColorPalette('#3b82f6');
// ['#eff6ff', '#dbeafe', ..., '#1e40af', '#1e3a8a']

6. @sa/uno-preset

功能:UnoCSS 自定义预设,定义项目的设计令牌和快捷方式。

位置packages/uno-preset/

预设内容

typescript
// uno.config.ts
import { defineConfig, presetUno } from 'unocss';
import { presetSoybean } from '@sa/uno-preset';

export default defineConfig({
  presets: [
    presetUno(),
    presetSoybean()
  ]
});

自定义快捷方式

typescript
// 快捷类名
<div class="flex-center">  // flex + justify-center + items-center
<div class="card">         // 卡片样式
<div class="btn-primary">  // 主要按钮样式

7. @sa/elegant-router-core

功能:路由核心库,提供文件路由和路由转换功能。

位置packages/elegant-router-core/

特性

  • 基于文件系统的路由生成
  • 支持动态路由
  • 路由元信息配置
  • 权限路由过滤

路由转换

typescript
import { transformElegantRouter } from '@sa/elegant-router-core';

// 将文件路由转换为 Vue Router 路由
const routes = transformElegantRouter(elegantRoutes);

8. @sa/scripts

功能:构建和开发脚本工具。

位置packages/scripts/

命令

bash
# 清理依赖
pnpm sa cleanup

# 生成路由
pnpm sa gen-route

# 提交代码(规范化)
pnpm sa git-commit

# 更新依赖
pnpm sa update-pkg

HTTP 客户端对比

CondorAdmin 提供三种 HTTP 客户端选择:

特性@sa/axios@sa/alova@sa/ofetch
基础库AxiosAlovaoFetch
请求缓存
请求共享
SSR 支持⚠️
包体积~14KB~8KB~4KB
学习成本

推荐

  • 通用项目@sa/axios(稳定、生态完善)
  • 性能优化@sa/alova(缓存、请求共享)
  • 轻量化@sa/ofetch(最小体积)

使用指南

安装依赖

项目使用 pnpm workspace,所有内部包已自动链接:

bash
pnpm install

导入使用

typescript
// 导入布局组件
import { AdminLayout, PageTab } from '@sa/materials';

// 导入 Hooks
import { useBoolean, useLoading } from '@sa/hooks';

// 导入工具函数
import { debounce, isArray } from '@sa/utils';

// 导入 HTTP 客户端
import { createRequest } from '@sa/axios';

// 导入颜色工具
import { hexToRgb, generateColorPalette } from '@sa/color';

类型支持

所有 Packages 都提供完整的 TypeScript 类型定义:

typescript
import type { AdminLayoutProps } from '@sa/materials';
import type { UseLoadingReturn } from '@sa/hooks';

自定义 Package

创建新 Package

bash
# 1. 在 packages/ 下创建目录
mkdir packages/my-package

# 2. 初始化 package.json
cd packages/my-package
pnpm init

# 3. 配置 package.json
{
  "name": "@sa/my-package",
  "version": "1.0.0",
  "exports": {
    ".": "./src/index.ts"
  }
}

# 4. 创建入口文件
mkdir src
echo "export const myUtil = () => {}" > src/index.ts

在项目中使用

typescript
// 1. 在根 package.json 添加依赖
{
  "dependencies": {
    "@sa/my-package": "workspace:*"
  }
}

// 2. 导入使用
import { myUtil } from '@sa/my-package';

最佳实践

1. Package 职责划分

  • materials:仅放置 UI 布局组件
  • hooks:通用业务逻辑的 Composables
  • utils:纯函数工具,无副作用
  • axios/alova/ofetch:HTTP 客户端封装

2. 避免循环依赖

✅ 正确:utils ← hooks ← materials
❌ 错误:utils ← hooks ← materials ← utils

3. 导出规范

typescript
// ✅ 具名导出(推荐)
export { useBoolean, useLoading };

// ❌ 默认导出(避免)
export default { useBoolean, useLoading };

4. 版本管理

所有内部 Packages 使用 workspace:* 版本,自动同步:

json
{
  "dependencies": {
    "@sa/hooks": "workspace:*"
  }
}

常见问题

Q1: 如何调试 Package 代码?

直接修改 packages/ 下的源码,项目会自动热更新。

Q2: Package 之间如何共享类型?

packages/utils/src/types/ 定义公共类型,其他包导入:

typescript
import type { CommonType } from '@sa/utils';

Q3: 如何发布内部 Package?

内部 Package 不需要发布到 npm,通过 workspace 本地引用即可。


相关文档