---
name: swing-sdk-vue
description: "Swing SDK Web Vue3 — 基于 Vue 3 + TypeScript 的 Monorepo 前端开发工具包，包含 8 个 npm 包 (@swing/core-vue3, @swing/system-vue3, @swing/flow-vue3, @swing/gds-vue3, @swing/notice-vue3, @swing/oss-vue3, @swing/iot-vue3, @swing/widget-vue3)。当用户说「swing-sdk」「安装 swing 包」「使用 swing」「@swing/」「SwingRequest」「widget 组件」「SSelectUser」「SOrganizationCascader」「useFetchPaging」以及任何涉及 Swing 平台前端开发的任务时使用。提供包结构说明、安装指引、四类文件规范、Hook 签名速查、Service API 索引、Widget 组件 Props 参考和 Core 环境约束分类。"
---

# Swing SDK Web Vue3 — 前端开发工具包

> Vue 3 + TypeScript Monorepo SDK，为 Swing 平台提供模块化的前端解决方案。

## When to Use / 何时使用

- 用户提到安装或使用 `@swing/*-vue3` 系列包
- 用户说「swing-sdk」「swing 前端」「swing 组件」
- 需要与 Swing 平台后端 API 交互（系统管理 / 工作流 / 数据字典 / 消息通知 / 文件存储 / IoT）
- 需要使用 Swing 封装的 UI 组件（以 `S` 前缀开头的组件）
- 需要使用 `SwingRequest` 进行 HTTP 请求
- 使用 `useFetchPaging` / `useFetchList` 进行分页/列表查询
- 需要在 uni-app 等非浏览器环境使用 SDK（通过 `setAdapter` 注入适配器）

## 包定位

```
Core (@swing/core-vue3)           ← 基础工具函数 + 类型工具 + HTTP 客户端 + 通用 Hooks
  └── System (system-vue3)        ← 系统管理服务 SDK（用户/组织/角色/站点/账号 API + Hooks）
        ├── Flow (flow-vue3)      ← 工作流引擎服务 SDK（模型/实例/审批/任务 API + Hooks）
        ├── GDS (gds-vue3)        ← 通用数据服务 SDK（数据字典/日历/编码规则 API + Hooks）
        ├── Notice (notice-vue3)  ← 消息通知服务 SDK（消息/主题/模板 API + Hooks）
        ├── OSS (oss-vue3)        ← 文件存储服务 SDK（OSS/OnlyOffice/模板填充 API + Hooks）
        ├── IOT (iot-vue3)        ← 物联网服务 SDK（设备/测点/告警 API + Hooks）
        └── Widget (widget-vue3)  ← PC 端业务 UI 组件库（基于以上 SDK 封装的 SaaS 应用组件）
```

> core/system/gds/notice/oss/flow/iot 维护的是各平台服务的基础 API 和 SDK 封装；
> widget 是基于这些服务 SDK 开发的 **SaaS 应用级 PC 端 UI 组件库**（Vue 3 + TSX，`S` 前缀），
> 把业务组件聚合在一起，消费方直接引入即可获得完整的业务 UI。

## 四类文件规范

SDK 中每个包遵循统一的四类文件组织结构：

```
packages/{name}/src/
├── index.ts           # 入口，统一 re-export 所有公开 API
├── interface.ts       # 📌 TypeScript 类型定义（单文件聚合）
├── enum.ts            # 📌 枚举定义（必须，不允许混在 interface.ts 中）
├── request.ts         # HTTP 请求工厂（基于 core 的 SwingRequest）
├── services/          # 📌 API 接口函数层
│   ├── domainA.ts     #    每个 domain 一个文件，函数式导出
│   └── domainB.ts
└── hooks/             # 📌 Vue Composition API 可复用逻辑
    ├── useXxx.ts      #    可按子域分目录（如 hooks/model/）
    └── useYyy.ts
```

### 1. `interface.ts` — 类型定义

- **每个包一个**，单文件聚合所有 TypeScript 接口和类型
- **不允许定义 `export enum`**（枚举必须放在 `enum.ts`）
- 通过 `import type { ... } from "./enum"` 引用枚举类型
- 核心类型继承链：`Model → DomainModel`（来自 `@swing/core-vue3`）
- 消费方项目中命名 `interface.d.ts`（声明文件），SDK 内部命名 `interface.ts`（源码）

### 2. `enum.ts` — 枚举定义（必选项）

- **不是可选的** — 每个包必须将枚举放在本文件
- **禁止与 interface 混在一起**（如 flow 包已抽出 18 个枚举）
- 每个枚举使用字符串值：`NAME = 'NAME'`
- 枚举值用于类型引用时，在 interface.ts 中 `import type`
- 一些包（notice、oss）还包含 display map 和 options 数组，也放在本文件

### 3. `services/` — API 接口函数层

- 每个业务 domain 一个 `.ts` 文件
- 每个文件导出纯异步函数，调用本包的 `request` 工厂
- 方法命名约定：

```
pagingXxx(params?)   → GET   分页查询
listXxx(params?)     → GET   列表查询
getXxx(id)           → GET   单条查询
createXxx(data)      → POST  新增
batchCreateXxx(data) → POST  批量新增
updateXxx(id, data)  → PUT   更新
removeXxx(id)        → DELETE 删除
batchRemoveXxx(ids)  → DELETE 批量删除
```

### 4. `hooks/` — Vue Composition API 可复用逻辑

- 一个文件一个 Hook，`export default function useXxx`
- 可按子域分目录组织（如 `hooks/model/useModelFetch.ts`）
- 标准 Hook 签名：

```typescript
useXxxFetch(options?)         → { fetch(id?), data, loading }
useXxxList(options?, params?) → { list, data, loading }
useXxxPagination(options?)    → { paging(params?), data, total, loading }
useXxxRemove()                → { remove(id), batchRemove?(ids), submitting }
useXxxSaveOrUpdate()          → { add(data), update?(id, data), submitting }
```

### 例外：`packages/widget`

`widget` 是 UI 组件库，不使用 services/hooks 顶层目录。组件按业务域放在 `components/{domain}/{ComponentName}/` 下，hooks 嵌入组件目录内。

---

## 用户体验文档

## Quick Start / 快速开始

```bash
# 安装某个包
pnpm add @swing/core-vue3                    # 核心
pnpm add @swing/system-vue3                  # 系统管理
pnpm add @swing/flow-vue3                    # 工作流
pnpm add @swing/gds-vue3                     # 通用数据服务
pnpm add @swing/notice-vue3                  # 通知服务
pnpm add @swing/oss-vue3                     # 对象存储
pnpm add @swing/iot-vue3                     # 物联网
pnpm add @swing/widget-vue3                  # UI 组件库
```

```typescript
// 在 main.ts 中注册全局组件
import { installAllComponents } from '@swing/widget-vue3'
app.use(installAllComponents)

// 或在组件中按需引入
import { SSelectUser } from '@swing/widget-vue3'

// 使用 Hook
import useUserList from '@swing/system-vue3'
const { list, data, loading } = useUserList({ cacheable: true })
```

## 模块导航

| 模块 | 包名 | 主要用途 | 参考 |
|------|------|----------|------|
| **Core** | `@swing/core-vue3` | 22 个工具函数（11 通用 + 8 浏览器 + 3 需适配器）、6 个 Hooks、类型工具、SwingRequest | [REFERENCE §1](references/REFERENCE.md#1-core-core-vue3) |
| **System** | `@swing/system-vue3` | 7 个 Hooks、5 个 Service、权限控制 | [REFERENCE §2](references/REFERENCE.md#2-system-system-vue3) |
| **Flow** | `@swing/flow-vue3` | 24 个 Hooks、5 个 Service、18 个流程枚举 | [REFERENCE §3](references/REFERENCE.md#3-flow-flow-vue3) |
| **GDS** | `@swing/gds-vue3` | 29 个 Hooks、7 个 Service | [REFERENCE §4](references/REFERENCE.md#4-gds-gds-vue3) |
| **Notice** | `@swing/notice-vue3` | 20 个 Hooks、4 个 Service、消息枚举 | [REFERENCE §5](references/REFERENCE.md#5-notice-notice-vue3) |
| **OSS** | `@swing/oss-vue3` | useOss/useOnlyOffice/useOssTemplate、SSE 下载进度 | [REFERENCE §6](references/REFERENCE.md#6-oss-oss-vue3) |
| **IOT** | `@swing/iot-vue3` | 12 个 Service、3 个 Hooks、G2 图表 | [REFERENCE §7](references/REFERENCE.md#7-iot-iot-vue3) |
| **Widget** | `@swing/widget-vue3` | 20+ 个 `S` 前缀 PC 端 UI 组件（SaaS 业务组件） | [REFERENCE §8](references/REFERENCE.md#8-widget-widget-vue3) |

## 通用模式

### 导出模式（双通道）

所有 SDK 包支持 **两种导入方式**：

```typescript
// 方式一：命名空间（向后兼容）
import { AccountService } from '@swing/system-vue3'
AccountService.pagingAccount(params)
AccountService.createUser(data)

// 方式二：ES Module 直接具名导入
import { pagingAccount, createUser } from '@swing/system-vue3'
pagingAccount(params)
createUser(data)
```

`index.ts` 中同时保留两套导出：

```typescript
export * as AccountService from "./services/account"  // 命名空间
export * from "./services/account"                     // 直接具名
```

> **core 包例外**：由于工具函数间存在同名冲突（如 `deepCopy` 同时出现在 ArrayUtil 和 ObjectUtil），
> core 的工具函数仅支持命名空间方式：`ArrayUtils.deepCopy()`。

### Hook 模式

所有业务 Hook 遵循统一签名模式：

```typescript
// 查询单条
useXxxFetch(options?: HookBaseOptions): { fetch, data, loading }

// 列表查询
useXxxList(options?: HookBaseOptions): { list, data, loading }

// 分页查询
useXxxPagination(options?: HookBaseOptions): { paging, data, total, loading }

// 删除
useXxxRemove(): { remove, batchRemove, submitting }

// 新增/更新
useXxxSaveOrUpdate(): { add, update, submitting }

// HookBaseOptions
interface HookBaseOptions {
  cacheable?: boolean       // 是否自动加载并缓存数据
  manual?: boolean          // 是否手动触发（不自动加载）
  message?: MessageHandler  // 消息回调（不传则不显示 toast）
}

// MessageHandler（UI 无耦合）
interface MessageHandler {
  success: (msg: string) => void
  error: (msg: string) => void
  warning?: (msg: string) => void
}
```

### SwingRequest（HTTP 客户端）

```typescript
import { RequestUtils } from '@swing/core-vue3'
// 每个模块有独立实例:
import { systemRequest } from '@swing/system-vue3'
import { gdsRequest } from '@swing/gds-vue3'
// 用法: systemRequest<R>(url, { method, data, params })
// 自动处理 JWT token、401/403 跳转、blob 下载
```

### 核心类型

```typescript
interface Model { id?: string; createTime?: string; updateTime?: string }
interface DomainModel extends Model { creatorId?: string; creatorName?: string; orgId?: string; tenantId?: string }
interface RestResponse<T> { success: boolean; message: string; data: T }
interface QueryParameter { id?: string; ids?: string[]; page?: number; pageSize?: number }
type PageInfo<T> = { list?: T[]; total?: number; page?: number; pageSize?: number }
```

### 跨平台适配器（PC / uni-app 兼容）

默认使用 `axios` + `localStorage`（PC 浏览器），**未调用 `setAdapter` 时老项目零影响**。
uni-app 等特殊环境需注入适配器，**在项目入口注册一次，全局生效**：

<details>
<summary>👆 点击展开 UniAdapter 示例</summary>

```typescript
// src/adapters/UniAdapter.ts（在 uni-app 项目中实现）
import type { HttpClient } from '@swing/core-vue3'

export const uniAdapter: HttpClient = {
  getToken(key) { return uni.getStorageSync(key) },
  setToken(key, value) { uni.setStorageSync(key, value) },
  removeToken(key) { uni.removeStorageSync(key) },
  navigate(url) { uni.navigateTo({ url }) },

  async request<R>(config) {
    return new Promise((resolve, reject) => {
      uni.request({
        url: config.url!, method: config.method as any,
        data: config.data, header: config.headers,
        success: (res) => resolve({
          data: res.data as R,
          headers: res.header || {},
          status: res.statusCode,
        }),
        fail: (err) => reject(err),
      })
    })
  },
}

// main.ts — 注册一次，全局生效
import { RequestUtils } from '@swing/core-vue3'
import { uniAdapter } from './adapters/UniAdapter'
RequestUtils.setAdapter(uniAdapter)
// 之后 systemRequest / gdsRequest / flowRequest 等自动走 uni.request
```

</details>

### HttpClient 接口

```typescript
interface HttpClient {
  request<R>(config: HttpRequestConfig): Promise<HttpResponse<R>>
  getToken?(key: string): string | null
  setToken?(key: string, value: string): void
  removeToken?(key: string): void
  navigate?(url: string): void
}

interface HttpRequestConfig {
  url?: string; method?: string; data?: any; params?: any
  headers?: Record<string, string>
  responseType?: 'json' | 'blob' | 'text'
  timeout?: number; getResponse?: boolean
  appTokenName?: string; loginUrl?: string
}

interface HttpResponse<R = any> {
  data: R; headers: Record<string, string | string[] | undefined>
  status: number
}
```

> ⚠️ 关键原则：未调用 `setAdapter()` 时，行为与之前完全一致（axios + localStorage）。
> 适配器模式**只影响 HTTP 传输层**，不改变 `LsUtil` / `TokenUtil` 等工具。

## Core 包环境约束

`@swing/core-vue3` 的 `utils/` 目录包含 22 个工具函数文件 + 1 个 `type.ts`（类型工具），
按运行环境分三类：

### 通用（11 个）— 可在 Node.js / SSR / uni-app / 浏览器运行

| 文件 | 主要导出 | 用途 |
|------|----------|------|
| `ArrayUtil` | `groupBy`, `toMap`, `arrayDistinct`, `deepCopy`, `treeToArray` | 纯数组操作 |
| `ColorUtil` | `stringToColor`, `getContrastColor` | 确定性颜色哈希 |
| `DateUtil` | `getWeekOfYear`, `getDayDiff`, `convertDayjsDate` | 日期计算 |
| `DateTimeUtil` | `getTimeRange`, `getEarliestAndLatestDateTime` | 日期时间范围 |
| `EnumUtil` | `createEnumHelpers` | 枚举转 label/value |
| `ExcelUtil` | `jsonToExcel` | 导出 Excel（基于 xlsx 库） |
| `NumberUtil` | `numberToPercent`, `decimalAdjust`, `hex2Decimal` | 数字处理 |
| `ObjectUtil` | `deepCopy`, `deepMerge`, `omit`, `flattenObject` | 纯对象操作 |
| `StringUtil` | `toCamelCase`, `toSnakeCase`, `parseTemplate` | 字符串处理 |
| `TreeUtil` | `arrayToTree`, `getFlatList`, `findParentNode` | 树形数据 CRUD |
| `TypeUtil` | `isString`, `isNumber`, `isArray`, `isEmpty` | 运行时类型判断（`Object.prototype.toString`） |

### 仅浏览器（8 个）— 依赖 window / document / localStorage

| 文件 | 依赖 | 主要导出 |
|------|------|----------|
| `BlobUtil` | `window.URL.createObjectURL` | `toWindowUrl` |
| `CommonUtil` | `window`, `document`, `navigator.clipboard` | `debounce`, `throttle`, `copyToClipboard`, `isDarkMode` |
| `DomUtil` | `HTMLElement.classList`, `MutationObserver` | `addClass`, `removeClass`, `observerDomResize` |
| `FileUtil` | `window.showSaveFilePicker`, `FileReader`, `fetch` | `fileDownload`, `blobToDataURL`, `readAsText` |
| `KeyCodeUtil` | `navigator.userAgent` | `KeyCode` 常量, `isCharacterKey` |
| `LsUtil` | `localStorage`（有 isBrowser 守卫） | `localGet`, `localSet`, `localClear` |
| `TokenUtil` | `localStorage.clear()`（非浏览器会崩溃） | `isValid`, `isExpired` |
| `UrlUtil` | `window.location.href`（**模块级初始化，SSR 会崩溃，需改惰性初始化**） | `getUrlParams`, `isUrl` |

### 需要适配器（3 个）— 有条件代码路径

| 文件 | 说明 |
|------|------|
| `EnvUtil` | `isBrowser` 守卫，`userAgent` 解析在非浏览器中需回退 |
| `RequestUtil` | 核心适配器模式 — `HttpClient` + `setAdapter()`，有适配器时绕过所有浏览器 API |
| `VuePlugin` | `withInstall`, `installAllComponents`，需 Vue `App` 实例 |

### type.ts — TypeScript 类型工具

`core/src/type.ts` 定义 18 个类型体操工具（通用，无环境依赖）：

| 类型 | 说明 |
|------|------|
| `GetOptional<T>` | 提取 T 中所有可选属性 |
| `GetRequired<T>` | 提取 T 中所有必需属性 |
| `ValueOf<T>` | 获取对象所有值的联合类型 |
| `GetObjectKeyPath<T>` | 验证深层嵌套属性的点路径（`"a.b.c"`） |
| `Data` | `Record<string, any>` 别名 |
| `FocusEventHandler` / `MouseEventHandler` / 等 | 8 种 DOM 事件处理类型 |

## 构建命令

```bash
# 在 SDK 项目根目录执行
pnpm install            # 安装依赖
pnpm build              # 构建所有包
pnpm run core           # 构建 single core
pnpm run system         # 构建 single system
pnpm publish            # Lerna 发布到私有 registry
```

## 构建产物结构

```
dist/
├── types/index.d.ts    # TypeScript 类型定义
├── es/index.js         # ES Module 格式
├── cjs/index.js        # CommonJS 格式
└── umd/index.js        # UMD 格式（浏览器直接引用）
```

## 详细参考

所有模块的完整导出清单、Hook 签名、Service API 方法表、组件 Props 定义详见：

**[📖 REFERENCES/REFERENCE.md](references/REFERENCE.md)**
