# @macroui/macroui-vue

<div align="center">

**Vue 3 + Element Plus API + MacroUI 视觉风格的组件库**

[![npm version](https://img.shields.io/npm/v/@macroui/macroui-vue.svg)](https://www.npmjs.com/package/@macroui/macroui-vue)
[![license](https://img.shields.io/npm/l/@macroui/macroui-vue.svg)](https://github.com/mobiui/macroui/blob/master/LICENSE)
[![downloads](https://img.shields.io/npm/dt/@macroui/macroui-vue.svg)](https://www.npmjs.com/package/@macroui/macroui-vue)

</div>

> 🎯 **@macroui/macroui-vue** 是基于 **Vue 3**（Composition API + `<script setup>`）开发的 **90+ 组件库**，提供与 Element Plus **完全一致** 的 API（组件名、props、events、slots），同时视觉层基于 **@macroui/macroui**（DaisyUI 风格）与 Tailwind CSS，覆盖 30+ 主题。

- 官方网站：https://github.com/mobiui/macroui
- API 兼容：100% Element Plus API（迁移零成本）
- 主题切换：`data-theme` CSS 变量
- TypeScript：完整类型定义
- 多语言：67+ 语言内置

---

## 目录

- [1. 项目简介](#1-项目简介)
- [2. 安装](#2-安装)
- [3. 快速开始](#3-快速开始)
- [4. 主题系统](#4-主题系统)
- [5. 国际化 i18n](#5-国际化-i18n)
- [6. 全局配置 ElConfigProvider](#6-全局配置-elconfigprovider)
- [7. 组件完整参考](#7-组件完整参考)
- [8. 工具函数与 Hooks](#8-工具函数与-hooks)
- [9. 按需引入](#9-按需引入)
- [10. SSR / Nuxt](#10-ssr--nuxt)
- [11. 浏览器支持](#11-浏览器支持)
- [12. 开发与构建](#12-开发与构建)
- [13. NPM 发布](#13-npm-发布)
- [14. 项目结构](#14-项目结构)
- [15. 常见问题](#15-常见问题)
- [16. 许可证](#16-许可证)

---

## 1. 项目简介

`@macroui/macroui-vue` 是 **Element Plus** API 风格 + **MacroUI** 视觉风格 的 Vue 3 组件库。

### 1.1 与 Element Plus 的关系

| 比较项 | Element Plus | macroui-vue |
|--------|--------------|-------------|
| API 兼容 | ✅ | ✅ 100% 兼容 |
| 主题方案 | CSS 变量 + SCSS | CSS 变量（DaisyUI） |
| 内置主题数 | 4 | 30+ |
| 主题切换 | 静态 SCSS | 运行时 `data-theme` |
| 样式覆盖 | SCSS 变量 | Tailwind class |
| 体积 | ~250KB | ~150KB |
| 多语言 | 55+ | 67+ |

迁移成本 = 0：只需替换 `element-plus` → `@macroui/macroui-vue`。

### 1.2 特性

- 🎨 **30+ MacroUI 主题**（light / dark / corporate / synthwave / cyberpunk / dracula ...）
- 📦 **90+ 组件** — 表单、数据展示、反馈、容器、导航全覆盖
- 🧩 **Element Plus 兼容 API** — `el-button`、`el-table`、`el-form`、`el-input` 命名一致
- 🌐 **多语言 (i18n)** — 67 种语言内置，支持运行时切换
- 🛠 **TypeScript 优先** — 完整类型定义
- ⚡ **按需引入** — 全量注册 / 具名导入皆可
- 🎯 **Vue 3 Composition API** — 完全基于 `<script setup>` 风格开发
- 💡 **Tailwind + DaisyUI** — 可直接使用 `btn btn-primary` 等 DaisyUI 类

### 1.3 适用项目

- 后台管理系统
- 中后台 CRUD 页面
- 数据可视化大屏
- 表单密集型应用
- 需要快速统一视觉风格的多端项目

---

## 2. 安装

### 2.1 通过 npm / pnpm / yarn 安装

```bash
# npm
npm install @macroui/macroui-vue @macroui/macroui @macroui/macroui-icons vue

# pnpm (推荐)
pnpm add @macroui/macroui-vue @macroui/macroui @macroui/macroui-icons vue

# yarn
yarn add @macroui/macroui-vue @macroui/macroui @macroui/macroui-icons vue
```

> Vue 3 是 **peerDependency**，必须显式安装。

### 2.2 peerDependencies

| 包名 | 版本 |
|------|------|
| `vue` | `>= 3.2.0`（推荐 3.4+） |

### 2.3 dependencies

| 包名 | 版本 | 说明 |
|------|------|------|
| `@macroui/macroui` | `^4.3.0` | MacroUI 主题与样式 |
| `@macroui/macroui-icons` | `^2.1.0` | 图标库 |
| `lodash-unified` | `^1.0.3` | 工具函数（按需 tree-shake） |

### 2.4 可选 peerDependencies（按需引入时视使用组件而定）

`@macroui/macroui-vue` 在构建期将这些运行时依赖标记为 `external`，不会打包进 bundle，请按需安装：

| 包名 | 何时需要 |
|------|----------|
| `@vueuse/core` | Tooltip / Dropdown / 弹层类组件用到的基础 hook |
| `@popperjs/core` | `el-tooltip`、`el-dropdown`、`el-popover` 等 Popper 定位 |
| `@floating-ui/dom` | `el-floating` 工具与新定位算法 |
| `lodash-unified` | 通用工具函数 |

> 一键安装建议：
>
> ```bash
> pnpm add @vueuse/core @popperjs/core @floating-ui/dom lodash-unified
> ```

---

## 3. 快速开始

### 3.1 完整注册（推荐）

```ts
// main.ts
import { createApp } from 'vue'
import App from './App.vue'

// 1. 加载 CSS（**顺序很重要**：先主题，再组件）
import '@macroui/macroui/dist/themes.css'      // MacroUI 主题变量
import '@macroui/macroui/dist/styled.css'      // MacroUI styled 组件类
import '@macroui/macroui-vue/dist/style.css'   // Vue 组件覆盖样式

// 2. 导入组件与插件
import MacrouiVue from '@macroui/macroui-vue'
import { zhCn } from '@macroui/macroui-vue/locale'

// 3. 创建应用
const app = createApp(App)

// 4. 全局注册全部组件 + ElConfigProvider
app.use(MacrouiVue, { locale: zhCn })

// 5. 挂载
app.mount('#app')
```

### 3.2 按需引入

```ts
// main.ts
import { createApp } from 'vue'
import {
  ElButton,
  ElInput,
  ElTable,
  ElTableColumn,
  ElConfigProvider,
} from '@macroui/macroui-vue'
import { zhCn } from '@macroui/macroui-vue/locale'

import App from './App.vue'

import '@macroui/macroui/dist/themes.css'
import '@macroui/macroui/dist/styled.css'
import '@macroui/macroui-vue/dist/style.css'

const app = createApp(App)

app.use(ElButton)
app.use(ElInput)
app.use(ElTable)
app.use(ElTableColumn)
app.use(ElConfigProvider, { locale: zhCn })

app.mount('#app')
```

### 3.3 在组件内使用

```vue
<template>
  <div>
    <el-button type="primary" @click="handleClick">主要按钮</el-button>
    <el-input v-model="value" placeholder="请输入" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const value = ref('')
const handleClick = () => {
  ElMessage.success('点击成功！')
}
</script>
```

### 3.4 CSS 加载顺序说明

```
必须按以下顺序加载：
1. themes.css  - 主题变量 (CSS Custom Properties)
2. styled.css  - MacroUI 通用组件类
3. style.css   - Vue 组件的微调和覆盖
```

颠倒顺序可能导致主题变量被覆盖而出现视觉异常。

### 3.5 函数式调用（无需注册）

```ts
import { ElMessage, ElMessageBox, ElNotification, ElLoading } from '@macroui/macroui-vue'

ElMessage.success('成功')
ElNotification.error('失败')
const loader = ElLoading.service({ text: '加载中...' })
```

---

## 4. 主题系统

### 4.1 切换主题

通过设置 `<html data-theme="...">` 即可切换：

```html
<html data-theme="light">     <!-- 默认 -->
<html data-theme="dark">      <!-- 暗色 -->
<html data-theme="cupcake">   <!-- 糖果粉 -->
<html data-theme="corporate"> <!-- 商务 -->
<html data-theme="dracula">   <!-- 德古拉 -->
```

### 4.2 动态切换（带持久化）

```ts
// src/utils/theme.ts
import { ref, watchEffect } from 'vue'

const theme = ref(localStorage.getItem('theme') || 'light')

export function useTheme() {
  watchEffect(() => {
    document.documentElement.setAttribute('data-theme', theme.value)
    localStorage.setItem('theme', theme.value)
  })

  const setTheme = (name: string) => {
    theme.value = name
  }

  const toggleDark = () => {
    setTheme(theme.value === 'dark' ? 'light' : 'dark')
  }

  return { theme, setTheme, toggleDark }
}
```

```vue
<!-- 组件中 -->
<script setup lang="ts">
import { useTheme } from '@/utils/theme'
const { theme, setTheme } = useTheme()
</script>

<template>
  <el-select v-model="theme" @change="setTheme">
    <el-option label="浅色" value="light" />
    <el-option label="暗色" value="dark" />
    <el-option label="赛博朋克" value="cyberpunk" />
  </el-select>
</template>
```

### 4.3 内置主题清单（30+）

`light`、`dark`、`cupcake`、`bumblebee`、`emerald`、`corporate`、`synthwave`、`retro`、`cyberpunk`、`valentine`、`halloween`、`garden`、`forest`、`aqua`、`lofi`、`pastel`、`fantasy`、`wireframe`、`black`、`luxury`、`dracula`、`cmyk`、`autumn`、`business`、`acid`、`lemonade`、`night`、`coffee`、`winter`、`dim`、`nord`、`abyss`、`silk`、`caramellatte`、`sunset`。

### 4.4 自定义主题

```css
/* src/styles/theme.css */
[data-theme="mytheme"] {
  /* HSL 格式：H S% L% */
  --p: 220 90% 56%;     /* primary */
  --pc: 0 0% 100%;
  --s: 160 84% 39%;
  --a: 30 90% 50%;
  --n: 220 14% 28%;
  --b1: 0 0% 100%;
  --b2: 220 13% 95%;
  --b3: 220 13% 90%;
  --bc: 220 14% 10%;
  --in: 198 93% 60%;
  --su: 158 64% 52%;
  --wa: 38 92% 50%;
  --er: 0 91% 71%;
}
```

```html
<html data-theme="mytheme">
```

---

## 5. 国际化 i18n

### 5.1 全局配置

```ts
import { ElConfigProvider } from '@macroui/macroui-vue'
import zhCn from '@macroui/macroui-vue/locale/lang/zh-cn'
import en from '@macroui/macroui-vue/locale/lang/en'

app.use(ElConfigProvider, {
  locale: zhCn,
  // size: 'default',  // 全局组件尺寸
})
```

### 5.2 运行时切换

```vue
<template>
  <el-config-provider :locale="locale">
    <app />
  </el-config-provider>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import zhCn from '@macroui/macroui-vue/locale/lang/zh-cn'
import en from '@macroui/macroui-vue/locale/lang/en'

const locale = ref(zhCn)
function switchLang(lang: 'zh' | 'en') {
  locale.value = lang === 'zh' ? zhCn : en
}
</script>
```

### 5.3 useLocale Hook

```vue
<script setup lang="ts">
import { useLocale } from '@macroui/macroui-vue'
const { t } = useLocale()

console.log(t('el.pagination.total', { total: 100 }))  // '共 100 条'
console.log(t('el.table.empty'))                        // '暂无数据'
</script>
```

### 5.4 支持的语言（67 种）

| 区域 | 语言 |
|------|------|
| 中国大陆 | zh-cn |
| 中国香港 | zh-hk |
| 中国澳门 | zh-mo |
| 中国台湾 | zh-tw |
| 亚洲 | ja, ko, vi, th, my, hi, bn, ta, te, km, ur, pa, id, ms |
| 欧洲 | en, de, fr, es, it, pt, pt-br, nl, ru, pl, tr, el, cs, da, sv, nb-no, fi, hu, ro, bg, uk, sk, hr, sl, sr, ca, eo, eu, et, lv, lt |
| 中东 | ar, fa, he |
| 非洲 | af, sw, mg |
| 中亚 | kk, ky, tk, uz-uz |
| 其他 | az, hy-am, ku, ckb, mn |

> **完整语言文件路径**：`@macroui/macroui-vue/locale/lang/<code>`

### 5.5 扩展新语言

```ts
// src/locales/ja.ts
export default {
  name: 'ja',
  el: {
    button: {
      confirm: '確認',
      cancel: 'キャンセル',
    },
    pagination: {
      total: '合計 {total} 件',
    },
  },
}
```

```ts
// 注入到全局
import ja from '@/locales/ja'
app.use(ElConfigProvider, {
  locale: ja,
})
```

### 5.6 ⚠️ 重要规则

> **邮箱地址不要写入语言包**，应写死在页面组件中。

```vue
✅ 正确：写死在组件中
<template>
  <div>联系我们: support@example.com</div>
</template>

❌ 错误：写入语言包
export default {
  contact: 'support@example.com',
}
```

---

## 6. 全局配置 ElConfigProvider

`ElConfigProvider` 用于全局配置组件默认值。

```vue
<template>
  <el-config-provider
    :locale="locale"
    :size="size"
    :button-type="buttonType"
    :message-options="messageOptions"
    :z-index="zIndex"
  >
    <app />
  </el-config-provider>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import zhCn from '@macroui/macroui-vue/locale/lang/zh-cn'
import type { SizeType, ButtonType } from '@macroui/macroui-vue'

const locale = ref(zhCn)
const size = ref<SizeType>('default')
const buttonType = ref<ButtonType>('primary')
const zIndex = ref(2000)
</script>
```

### 6.1 ElConfigProvider Props

| Prop | 类型 | 默认 | 说明 |
|------|------|------|------|
| `locale` | `Language` | `zhCn` | 当前语言 |
| `size` | `SizeType` | `'default'` | 全局尺寸 |
| `buttonType` | `ButtonType` | - | 全局按钮类型 |
| `messageOptions` | `MessageOptions` | - | Message 全局配置 |
| `zIndex` | `number` | `2000` | 弹窗 z-index 起始值 |
| `namespace` | `string` | `'el'` | 组件 CSS 类前缀 |

### 6.2 SizeType

`'large' | 'default' | 'small' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'`

> 注：扩展尺寸（`xs` / `sm` / `md` / `lg` / `xl`）仅 macroui-vue 支持，Element Plus 仅有前三档。

---

## 7. 组件完整参考

### 7.1 基础组件

#### ElButton — 按钮

```vue
<template>
  <el-button>默认按钮</el-button>
  <el-button type="primary">主要按钮</el-button>
  <el-button type="success">成功按钮</el-button>
  <el-button type="warning">警告按钮</el-button>
  <el-button type="danger">危险按钮</el-button>
  <el-button type="info">信息按钮</el-button>
  <el-button plain>朴素按钮</el-button>
  <el-button round>圆角</el-button>
  <el-button circle>圆</el-button>
  <el-button icon="Search">搜索</el-button>
  <el-button loading>加载中</el-button>
  <el-button size="large">大</el-button>
  <el-button>默认</el-button>
  <el-button size="small">小</el-button>
  <el-button size="xs">超小</el-button>

  <el-button-group>
    <el-button icon="ArrowLeft">上一页</el-button>
    <el-button icon="ArrowRight">下一页</el-button>
  </el-button-group>
</template>
```

**Props 关键字段**：`type`（`'primary' | 'success' | 'warning' | 'danger' | 'info' | 'default'`）、`size`、`plain`、`round`、`circle`、`loading`、`disabled`、`icon`、`native-type`、`autofocus`、`tag`、`text` / `bg` / `link`。

**Events**：`click`、`mousedown`。

**Slots**：`default`、`loading`、`icon`。

#### ElLink — 文字链接

```vue
<el-link href="https://element-plus.org" target="_blank">默认</el-link>
<el-link type="primary">主要</el-link>
<el-link :underline="false">无下划线</el-link>
<el-link disabled>禁用</el-link>
```

#### ElText — 文本

```vue
<el-text>默认</el-text>
<el-text type="primary">主要</el-text>
<el-text type="danger">危险</el-text>
<el-text size="large">大号</el-text>
<el-text truncated>省略...</el-text>
```

#### ElIcon — 图标

```vue
<el-icon><Edit /></el-icon>
<el-icon :size="20" color="#ff6b6b"><Search /></el-icon>
```

#### ElSpace — 间距

```vue
<el-space>
  <el-button>1</el-button>
  <el-button>2</el-button>
</el-space>

<el-space direction="vertical" :size="20">
  <el-card>1</el-card>
  <el-card>2</el-card>
</el-space>

<el-space wrap :size="[10, 20]">
  <el-button v-for="i in 10">按钮{{ i }}</el-button>
</el-space>
```

#### ElDivider — 分割线

```vue
<el-divider />
<el-divider>文字</el-divider>
<el-divider direction="vertical">垂直</el-divider>
<el-divider border-style="dashed" />
```

#### ElScrollbar — 滚动条

```vue
<el-scrollbar height="200px">
  <p v-for="i in 50">{{ i }}</p>
</el-scrollbar>
```

### 7.2 表单组件

#### ElInput — 输入框

```vue
<template>
  <el-input v-model="value" placeholder="请输入" />
  <el-input v-model="value" size="large" />
  <el-input v-model="value" disabled />
  <el-input v-model="value" clearable />
  <el-input v-model="value" show-password />
  <el-input v-model="value" :prefix-icon="Search" />
  <el-input v-model="value" type="textarea" :rows="4" />
  <el-input v-model="url">
    <template #prepend>https://</template>
    <template #append>.com</template>
  </el-input>
  <el-input v-model="value" maxlength="10" show-word-limit />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { Search } from '@macroui/macroui-icons'
const value = ref('')
const textarea = ref('')
const url = ref('')
</script>
```

**Props**：`modelValue`、`type`、`placeholder`、`disabled`、`clearable`、`show-password`、`prefix-icon`、`suffix-icon`、`maxlength`、`minlength`、`show-word-limit`、`autocomplete`、`name`、`size`、`input-style`。

**Events**：`update:modelValue`、`input`、`change`、`focus`、`blur`、`clear`、`keydown`、`keyup`。

**Slots**：`prefix`、`suffix`、`prepend`、`append`。

#### ElInputNumber — 数字输入

```vue
<el-input-number v-model="num" :min="1" :max="10" :step="1" />
<el-input-number v-model="num" controls-position="right" />
<el-input-number v-model="num" :precision="2" :step="0.1" />
```

#### ElInputTag — 标签输入

```vue
<el-input-tag v-model="tags" placeholder="输入后回车" />
```

#### ElInputOtp — 一次性密码

```vue
<el-input-otp v-model="otp" :length="6" />
```

#### ElSelect — 选择器

```vue
<el-select v-model="value" placeholder="请选择" clearable filterable>
  <el-option label="选项A" value="A" />
  <el-option label="选项B" value="B" />
  <el-option-group label="分组1">
    <el-option label="选项C" value="C" />
  </el-option-group>
</el-select>

<el-select v-model="values" multiple collapse-tags>
  <el-option v-for="o in options" :key="o.value" :label="o.label" :value="o.value" />
</el-select>
```

**Props**：`modelValue`、`multiple`、`disabled`、`size`、`clearable`、`filterable`、`remote`、`loading-text`、`no-match-text`、`no-data-text`、`placeholder`、`collapse-tags`、`multiple-limit`、`value-key`。

#### ElOption / ElOptionGroup

**ElOption Props**：`value`、`label`、`disabled`。

**ElOptionGroup Props**：`label`、`disabled`。

#### ElCascader — 级联选择

```vue
<el-cascader
  v-model="value"
  :options="options"
  :props="{ value: 'id', label: 'name' }"
  clearable
  filterable
/>
```

**Props**：`modelValue`、`options`、`props`、`size`、`placeholder`、`disabled`、`clearable`、`filterable`、`show-all-levels`、`collapse-tags`、`separator`、`before-filter`、`max-collapse-tags`。

#### ElTreeSelect — 树形选择

```vue
<el-tree-select v-model="value" :data="treeData" :props="defaultProps" />
```

#### ElTree — 树形控件

```vue
<el-tree
  :data="data"
  :props="{ children: 'children', label: 'name' }"
  show-checkbox
  node-key="id"
  default-expand-all
  @check-change="handleCheck"
/>

<el-tree :load="loadNode" lazy :props="defaultProps" />
```

#### ElTreeV2 — 虚拟树

```vue
<el-tree-v2 :data="data" :props="{ label: 'name' }" />
```

#### ElCheckbox — 多选框

```vue
<el-checkbox v-model="checked">选项</el-checkbox>

<el-checkbox-group v-model="list">
  <el-checkbox value="A">A</el-checkbox>
  <el-checkbox value="B">B</el-checkbox>
</el-checkbox-group>

<el-checkbox :true-value="1" :false-value="0" v-model="value" />
```

#### ElCheckboxGroup / ElCheckboxButton

```vue
<el-checkbox-group v-model="list">
  <el-checkbox-button value="A">A</el-checkbox-button>
  <el-checkbox-button value="B">B</el-checkbox-button>
</el-checkbox-group>
```

#### ElRadio — 单选

```vue
<el-radio v-model="radio" :label="1">男</el-radio>
<el-radio-group v-model="radio">
  <el-radio :label="1">男</el-radio>
  <el-radio :label="2">女</el-radio>
</el-radio-group>

<el-radio-group v-model="radio">
  <el-radio-button :label="1">男</el-radio-button>
  <el-radio-button :label="2">女</el-radio-button>
</el-radio-group>
```

#### ElSwitch — 开关

```vue
<el-switch v-model="value" />
<el-switch v-model="value" active-text="开" inactive-text="关" />
<el-switch v-model="value" inline-prompt active-text="Y" inactive-text="N" />
<el-switch v-model="value" loading />
```

#### ElSlider — 滑块

```vue
<el-slider v-model="value" :min="0" :max="100" />
<el-slider v-model="range" range :min="0" :max="100" :step="10" />
<el-slider v-model="marks" :marks="{ 0: '0°C', 50: '50°C', 100: '100°C' }" />
```

#### ElRate — 评分

```vue
<el-rate v-model="rate" />
<el-rate v-model="rate" show-score :max="10" />
<el-rate v-model="rate" allow-half />
```

#### ElColorPicker — 颜色选择器

```vue
<el-color-picker v-model="color" show-alpha />
<el-color-picker v-model="color" color-format="hex" />
<el-color-picker v-model="color" :predefine="['#ff4500', '#ff8c00']" />
```

#### ElDatePicker — 日期选择器

```vue
<el-date-picker v-model="date" type="date" placeholder="选择日期" />
<el-date-picker v-model="range" type="daterange" />
<el-date-picker v-model="month" type="month" />
<el-date-picker v-model="year" type="year" />
<el-date-picker v-model="datetime" type="datetime" />
```

#### ElTimePicker — 时间选择器

```vue
<el-time-picker v-model="time" placeholder="选择时间" />
<el-time-picker v-model="range" is-range />
<el-time-picker v-model="time" format="HH:mm" arrow-control />
```

#### ElTimeSelect — 时间段选择

```vue
<el-time-select
  v-model="time"
  start="08:30"
  end="18:30"
  step="00:15"
  placeholder="选择时间"
/>
```

#### ElUpload — 上传

```vue
<el-upload
  action="/api/upload"
  :headers="{ token: 'xxx' }"
  :before-upload="beforeUpload"
  :on-success="onSuccess"
  :on-error="onError"
>
  <el-button>点击上传</el-button>
</el-upload>

<el-upload drag>
  <el-icon class="el-icon--upload"><upload-filled /></el-icon>
  <div class="el-upload__text">
    将文件拖到此处，或<em>点击上传</em>
  </div>
</el-upload>
```

#### ElForm — 表单

```vue
<template>
  <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
    <el-form-item label="用户名" prop="username">
      <el-input v-model="form.username" />
    </el-form-item>
    <el-form-item label="邮箱" prop="email">
      <el-input v-model="form.email" />
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="submit">提交</el-button>
      <el-button @click="reset">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { FormInstance, FormRules } from '@macroui/macroui-vue'

const formRef = ref<FormInstance>()
const form = ref({ username: '', email: '' })

const rules: FormRules = {
  username: [
    { required: true, message: '请输入用户名', trigger: 'blur' },
    { min: 3, max: 20, message: '长度在 3-20', trigger: 'blur' },
  ],
  email: [
    { required: true, message: '请输入邮箱', trigger: 'blur' },
    { type: 'email', message: '邮箱格式不正确', trigger: 'blur' },
  ],
}

const submit = async () => {
  if (!await formRef.value?.validate()) return
}

const reset = () => formRef.value?.resetFields()
</script>
```

**Form Methods（ref）**：`validate(cb?)`、`validateField(props, cb?)`、`resetFields()`、`clearValidate(props?)`、`scrollToField(prop)`。

**ElFormItem Props**：`prop`、`label`、`label-width`、`required`、`rules`、`error`、`show-message`、`inline-message`、`size`。

#### ElMention — @提及

```vue
<el-mention
  v-model="value"
  :options="[{ value: '张三', label: '张三' }]"
  :prefix="['@', '#']"
/>
```

#### ElAutocomplete — 自动补全

```vue
<el-autocomplete
  v-model="value"
  :fetch-suggestions="querySearch"
  @select="handleSelect"
/>
```

### 7.3 数据展示

#### ElTable — 表格

```vue
<el-table
  :data="tableData"
  stripe
  border
  height="400"
  @selection-change="handleSelection"
>
  <el-table-column type="selection" width="55" />
  <el-table-column type="index" label="#" width="60" />
  <el-table-column prop="name" label="姓名" sortable />
  <el-table-column prop="age" label="年龄" sortable />
  <el-table-column prop="address" label="地址" show-overflow-tooltip />
  <el-table-column label="操作" width="120" fixed="right">
    <template #default="{ row }">
      <el-button size="small" @click="edit(row)">编辑</el-button>
    </template>
  </el-table-column>
</el-table>

<el-pagination
  v-model:current-page="page"
  v-model:page-size="size"
  :total="total"
  layout="total, sizes, prev, pager, next, jumper"
/>
```

**关键 Props**：`data`、`height`、`max-height`、`stripe`、`border`、`size`、`fit`、`show-header`、`highlight-current-row`、`row-class-name`、`row-style`、`cell-class-name`、`cell-style`、`empty-text`、`show-summary`、`summary-method`、`span-method`、`lazy`、`load`、`tree-props`、`default-expand-all`、`default-sort`。

**关键 Events**：`select`、`select-all`、`selection-change`、`cell-click`、`row-click`、`row-dblclick`、`sort-change`、`current-change`、`expand-change`、`filter-change`。

#### ElTableColumn — 表格列

**关键 Props**：`type`（`selection` / `index` / `expand`）、`prop`、`label`、`width`、`min-width`、`fixed`、`sortable`、`sort-orders`、`sort-method`、`sort-by`、`resizable`、`formatter`、`show-overflow-tooltip`、`align`、`header-align`、`class-name`、`label-class-name`、`selectable`、`reserve-selection`、`filters`、`filter-placement`、`filter-multiple`、`filter-method`、`filtered-value`、`render-header`。

#### ElTableV2 — 虚拟表格

```vue
<el-table-v2 :columns="columns" :data="data" :width="800" :height="400" fixed />
```

#### ElPagination — 分页

```vue
<el-pagination
  v-model:current-page="currentPage"
  v-model:page-size="pageSize"
  :total="100"
  :page-sizes="[10, 20, 50, 100]"
  layout="total, sizes, prev, pager, next, jumper"
  background
/>
```

#### ElTag — 标签

```vue
<el-tag>标签1</el-tag>
<el-tag type="success">成功</el-tag>
<el-tag type="warning" closable>警告</el-tag>
<el-tag type="danger" effect="dark">危险</el-tag>
<el-tag size="large">大号</el-tag>
<el-tag round>圆角</el-tag>
<el-check-tag v-model="checked">可选中</el-check-tag>
```

#### ElBadge — 徽章

```vue
<el-badge :value="12">
  <el-button>按钮</el-button>
</el-badge>

<el-badge :value="3" :max="9">
  <el-button>≤9</el-button>
</el-badge>

<el-badge is-dot>
  <el-button>红点</el-button>
</el-badge>
```

#### ElAvatar — 头像

```vue
<el-avatar :size="50" src="user.jpg" />
<el-avatar :size="50" icon="User" />
<el-avatar :size="50">USER</el-avatar>
<el-avatar-group>
  <el-avatar src="1.jpg" />
  <el-avatar src="2.jpg" />
</el-avatar-group>
```

#### ElSkeleton — 骨架屏

```vue
<el-skeleton v-if="loading" :rows="5" animated />

<el-skeleton>
  <template #template>
    <el-skeleton-item variant="image" style="width: 200px; height: 100px;" />
    <el-skeleton-item variant="h1" />
    <el-skeleton-item variant="text" :rows="3" />
  </template>
  <template #default>真实内容</template>
</el-skeleton>
```

#### ElProgress — 进度条

```vue
<el-progress :percentage="50" />
<el-progress :percentage="100" status="success" />
<el-progress :percentage="50" type="circle" :width="100" />
<el-progress :percentage="50" :duration="2" striped />
```

#### ElEmpty — 空状态

```vue
<el-empty description="暂无数据" />
<el-empty :image-size="200">
  <el-button type="primary">重新加载</el-button>
</el-empty>
```

#### ElResult — 结果页

```vue
<el-result icon="success" title="成功" sub-title="操作已完成">
  <template #extra>
    <el-button type="primary">返回</el-button>
  </template>
</el-result>
```

#### ElDescriptions — 描述列表

```vue
<el-descriptions title="用户信息" :column="2" border>
  <el-descriptions-item label="用户名">张三</el-descriptions-item>
  <el-descriptions-item label="邮箱">zhang@example.com</el-descriptions-item>
  <el-descriptions-item label="电话">13800138000</el-descriptions-item>
  <el-descriptions-item label="地址" :span="2">北京市朝阳区</el-descriptions-item>
</el-descriptions>
```

#### ElStatistic — 统计数值

```vue
<el-statistic title="活跃用户" :value="1024" />
<el-statistic :value="12345.67" :precision="2" title="销售额">
  <template #suffix><span>元</span></template>
</el-statistic>
<el-countdown :value="Date.now() + 1000 * 60" title="倒计时" />
```

#### ElTimeline — 时间轴

```vue
<el-timeline>
  <el-timeline-item timestamp="2024-01-01" placement="top">
    <el-card>创建项目</el-card>
  </el-timeline-item>
  <el-timeline-item timestamp="2024-02-01" type="primary" hollow>
    <el-card>里程碑 1</el-card>
  </el-timeline-item>
</el-timeline>
```

#### ElCalendar — 日历

```vue
<el-calendar v-model="date">
  <template #date-cell="{ data }">
    <p :class="data.isSelected ? 'is-selected' : ''">
      {{ data.day.split('-').slice(1).join('-') }}
    </p>
  </template>
</el-calendar>
```

#### ElImage — 图片

```vue
<el-image
  src="image.jpg"
  :fit="'cover'"
  :lazy="true"
  :preview-src-list="[src1, src2]"
/>
```

#### ElCarousel — 走马灯

```vue
<el-carousel :interval="3000" arrow="always" indicator-position="outside" height="200px">
  <el-carousel-item v-for="item in 4" :key="item">
    <h3>{{ item }}</h3>
  </el-carousel-item>
</el-carousel>
```

#### ElCollapse — 折叠面板

```vue
<el-collapse v-model="activeNames" accordion>
  <el-collapse-item title="标题1" name="1">内容1</el-collapse-item>
  <el-collapse-item title="标题2" name="2">内容2</el-collapse-item>
</el-collapse>
```

#### ElTransfer — 穿梭框

```vue
<el-transfer
  v-model="value"
  :data="data"
  filterable
  :props="{ key: 'id', label: 'name' }"
/>
```

#### ElWatermark — 水印

```vue
<el-watermark content="机密文件">
  <div>有水印内容</div>
</el-watermark>
```

### 7.4 反馈组件

#### ElAlert — 警告提示

```vue
<el-alert title="成功提示" type="success" />
<el-alert title="警告提示" type="warning" show-icon />
<el-alert title="错误提示" type="error" description="详细说明" closable />
```

#### ElDialog — 对话框

```vue
<el-dialog v-model="visible" title="标题" width="500px" :before-close="handleClose">
  <span>内容</span>
  <template #footer>
    <el-button @click="visible = false">取消</el-button>
    <el-button type="primary" @click="confirm">确认</el-button>
  </template>
</el-dialog>
```

**关键 Props**：`modelValue`、`title`、`width`、`fullscreen`、`top`、`modal`、`append-to-body`、`lock-scroll`、`close-on-click-modal`、`close-on-press-escape`、`show-close`、`before-close`、`draggable`、`overflow`、`center`、`align-center`、`destroy-on-close`、`transition`。

#### ElDrawer — 抽屉

```vue
<el-drawer v-model="visible" title="抽屉" direction="rtl" size="400px">
  <span>内容</span>
</el-drawer>
```

**关键 Props**：`modelValue`、`direction`、`size`、`title`、`modal`、`drawer-class`、`wrapper-closable`、`close-on-press-escape`、`destroy-on-close`、`with-header`、`show-close`、`z-index`、`append-to-body`、`lock-scroll`。

#### ElMessage — 消息提示（函数式）

```ts
import { ElMessage } from '@macroui/macroui-vue'

ElMessage.success('操作成功')
ElMessage.warning('警告信息')
ElMessage.error('错误信息')
ElMessage.info('提示信息')

ElMessage({
  message: '带图标',
  type: 'success',
  duration: 3000,
  showClose: true,
  dangerouslyUseHTMLString: true,
  customClass: 'my-message',
  onClose: () => console.log('closed'),
})
```

**MessageOptions**：`message`、`type`、`icon`、`dangerouslyUseHTMLString`、`customClass`、`duration`、`showClose`、`center`、`onClose`、`offset`、`appendTo`、`grouping`。

#### ElMessageBox — 消息弹框

```ts
import { ElMessageBox } from '@macroui/macroui-vue'

await ElMessageBox.alert('内容', '标题', { type: 'warning' })

try {
  await ElMessageBox.confirm('确定删除？', '提示', { type: 'warning' })
} catch {
  // 取消
}

const { value } = await ElMessageBox.prompt('请输入名字', '提示', {
  inputPattern: /^.{2,20}$/,
  inputErrorMessage: '长度 2-20',
})
```

#### ElNotification — 通知

```ts
import { ElNotification } from '@macroui/macroui-vue'

ElNotification({
  title: '标题',
  message: '内容',
  type: 'success',
  position: 'top-right',
  duration: 4500,
})
```

**位置 options**：`top-right` / `top-left` / `bottom-right` / `bottom-left`。

#### ElPopconfirm — 弹出确认

```vue
<el-popconfirm title="确定删除？" @confirm="handleConfirm" @cancel="handleCancel">
  <template #reference>
    <el-button>删除</el-button>
  </template>
</el-popconfirm>
```

#### ElTooltip — 文字提示

```vue
<el-tooltip content="提示内容" placement="top">
  <el-button>悬停</el-button>
</el-tooltip>

<el-tooltip placement="bottom" effect="light">
  <template #content>
    <p>多行内容</p>
  </template>
  <el-button>亮色</el-button>
</el-tooltip>
```

**关键 Props**：`content`、`placement`、`disabled`、`offset`、`transition`、`show-after`、`hide-after`、`controlled`、`visible`、`trigger`（`hover` / `click` / `focus` / `contextmenu`）、`teleported`。

#### ElPopover — 弹出框

```vue
<el-popover placement="top" :width="200" trigger="click">
  <template #default>
    <p>弹出内容</p>
  </template>
  <template #reference>
    <el-button>点击触发</el-button>
  </template>
</el-popover>
```

#### ElLoading — 加载状态

```vue
<template v-loading="loading">内容</template>

<el-button @click="openFullScreen">全屏 loading</el-button>
<script setup lang="ts">
import { ElLoading } from '@macroui/macroui-vue'
const openFullScreen = () => {
  const loading = ElLoading.service({
    lock: true,
    text: '加载中...',
    background: 'rgba(0, 0, 0, 0.7)',
  })
  setTimeout(() => loading.close(), 3000)
}
</script>
```

**Options**：`target`、`fullscreen`、`lock`、`text`、`spinner`、`background`、`customClass`。

#### ElTour — 漫游式引导

```vue
<el-tour v-model="open">
  <el-tour-step target="#btn1" title="步骤1" description="说明1" />
  <el-tour-step target="#btn2" title="步骤2" description="说明2" />
</el-tour>
```

### 7.5 导航组件

#### ElMenu — 导航菜单

```vue
<el-menu :default-active="active" mode="horizontal">
  <el-menu-item index="1">首页</el-menu-item>
  <el-sub-menu index="2">
    <template #title>产品</template>
    <el-menu-item index="2-1">产品A</el-menu-item>
    <el-menu-item index="2-2">产品B</el-menu-item>
  </el-sub-menu>
  <el-menu-item index="3">关于</el-menu-item>
</el-menu>

<el-menu default-active="1" :collapse="collapse">
  ...
</el-menu>
```

**关键 Props**：`mode`（`horizontal` / `vertical`）、`default-active`、`collapse`、`default-openeds`、`unique-opened`、`menu-trigger`、`router`、`collapse-transition`、`arrow-icon`、`ellipsis-icon`。

#### ElMenuItem / ElSubMenu / ElMenuItemGroup

```vue
<el-menu-item index="1">
  <template #title>
    <el-icon><Edit /></el-icon>
    <span>编辑</span>
  </template>
</el-menu-item>

<el-sub-menu index="2">
  <template #title>子菜单</template>
  <el-menu-item-group title="分组">
    <el-menu-item index="2-1">选项1</el-menu-item>
  </el-menu-item-group>
</el-sub-menu>
```

#### ElTabs — 标签页

```vue
<el-tabs v-model="activeName">
  <el-tab-pane label="用户管理" name="first">内容1</el-tab-pane>
  <el-tab-pane label="配置管理" name="second">内容2</el-tab-pane>
  <el-tab-pane label="角色管理" name="third" lazy>内容3</el-tab-pane>
</el-tabs>

<el-tabs type="card">
  ...
</el-tabs>

<el-tabs type="border-card">
  ...
</el-tabs>
```

**关键 Props**：`v-model`、`type`（`card` / `border-card`）、`tab-position`、`closable`、`addable`、`editable`、`before-leave`。

#### ElTabPane — 标签页内容

**关键 Props**：`name`、`label`、`disabled`、`lazy`、`closable`、`contextmenu`。

#### ElBreadcrumb — 面包屑

```vue
<el-breadcrumb separator="/">
  <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
  <el-breadcrumb-item :to="{ path: '/list' }">列表</el-breadcrumb-item>
  <el-breadcrumb-item>详情</el-breadcrumb-item>
</el-breadcrumb>
```

#### ElDropdown — 下拉菜单

```vue
<el-dropdown trigger="click">
  <span class="el-dropdown-link">下拉菜单<el-icon class="el-icon--right"><arrow-down /></el-icon></span>
  <template #dropdown>
    <el-dropdown-menu>
      <el-dropdown-item>选项1</el-dropdown-item>
      <el-dropdown-item disabled>选项2（禁用）</el-dropdown-item>
      <el-dropdown-item divided>选项3</el-dropdown-item>
    </el-dropdown-menu>
  </template>
</el-dropdown>

<el-dropdown split-button @click="handleClick">
  操作
  <template #dropdown>
    <el-dropdown-menu>
      <el-dropdown-item>...</el-dropdown-item>
    </el-dropdown-menu>
  </template>
</el-dropdown>
```

#### ElSteps — 步骤条

```vue
<el-steps :active="active" finish-status="success" align-center>
  <el-step title="步骤1" description="说明1" />
  <el-step title="步骤2" description="说明2" />
  <el-step title="步骤3" description="说明3" />
</el-steps>

<el-steps :active="1" direction="vertical">
  ...
</el-steps>
```

**关键 Props**：`space`、`direction`、`active`、`align-center`、`simple`、`finish-status`、`process-status`。

**ElStep Props**：`title`、`description`、`icon`、`status`。

#### ElPageHeader — 页头

```vue
<el-page-header title="返回" content="详情页" @back="goBack" />
```

#### ElAffix — 固钉

```vue
<el-affix :offset="100">
  <el-button>固定按钮</el-button>
</el-affix>
```

#### ElBacktop — 回到顶部

```vue
<el-backtop :right="20" :bottom="20" />
```

#### ElAnchor — 锚点

```vue
<el-anchor :offset="80">
  <el-anchor-link href="#section-1" title="章节1" />
  <el-anchor-link href="#section-2" title="章节2">
    <el-anchor-link href="#section-2-1" title="章节2-1" />
  </el-anchor-link>
</el-anchor>
```

### 7.6 布局组件

#### ElContainer / ElHeader / ElAside / ElMain / ElFooter

```vue
<el-container>
  <el-header>Header</el-header>
  <el-container>
    <el-aside width="200px">Aside</el-aside>
    <el-main>Main</el-main>
  </el-container>
  <el-footer>Footer</el-footer>
</el-container>
```

#### ElRow / ElCol — 栅格

```vue
<el-row :gutter="20">
  <el-col :span="8">8/24</el-col>
  <el-col :span="8">8/24</el-col>
  <el-col :span="8">8/24</el-col>
</el-row>

<el-row :gutter="20" justify="center">
  ...
</el-row>

<el-row :gutter="20">
  <el-col :xs="24" :sm="12" :md="8" :lg="6">响应式</el-col>
</el-row>

<el-row :gutter="20" type="flex" justify="start" align="middle">
  <el-col :span="6">flex</el-col>
</el-row>
```

**ElRow**：`gutter`、`type`、`justify`、`align`、`tag`。

**ElCol**：`span`、`offset`、`push`、`pull`、`xs`、`sm`、`md`、`lg`、`xl`、`tag`。

#### ElSplitter — 分隔面板

```vue
<el-splitter>
  <el-splitter-panel :size="200">左</el-splitter-panel>
  <el-splitter-panel>右</el-splitter-panel>
</el-splitter>
```

#### ElSegmented — 分段控制器

```vue
<el-segmented v-model="value" :options="['日', '周', '月']" />
<el-segmented v-model="value" :options="options" size="large" block />
```

#### ElBorder — 边框

```vue
<el-border>带边框容器</el-border>
<el-border :width="2" color="primary">自定义边框</el-border>
```

#### ElInfiniteScroll — 无限滚动指令

```vue
<ul v-infinite-scroll="loadMore" :infinite-scroll-delay="200" :infinite-scroll-disabled="disabled">
  <li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
```

---

## 8. 工具函数与 Hooks

### 8.1 入口导出

```ts
import * as MacrouiVue from '@macroui/macroui-vue'

import {
  ElMessage,
  ElMessageBox,
  ElNotification,
  ElLoading,
} from '@macroui/macroui-vue'
```

### 8.2 useLocale

```ts
import { useLocale } from '@macroui/macroui-vue'

const { t, locale } = useLocale()
t('el.pagination.total', { total: 100 })
```

### 8.3 useZIndex

```ts
import { useZIndex } from '@macroui/macroui-vue'

const { initialZIndex, currentZIndex, nextZIndex } = useZIndex()
nextZIndex()  // 取得下一个 z-index
```

### 8.4 useNamespace

```ts
import { useNamespace } from '@macroui/macroui-vue'

const ns = useNamespace('button')
ns.b()        // 'el-button'
ns.is('disabled')  // 'is-disabled'
ns.m('primary')   // 'el-button--primary'
```

### 8.5 类型导出

```ts
import type {
  ButtonType,
  ButtonSize,
  SizeType,
  FormInstance,
  FormRules,
  FormItemRule,
  MenuItemRegistered,
  TableInstance,
  TreeInstance,
  UploadFile,
  UploadFiles,
  UploadUserFile,
  TreeNodeData,
  CascaderOption,
  CascaderValue,
  SelectOption,
  SelectOptionGroup,
  MessageOptions,
  MessageType,
  NotificationOptions,
  DateCell,
  CalendarInstance,
} from '@macroui/macroui-vue'
```

### 8.6 工具函数

| 函数 | 用途 |
|------|------|
| `useLocale()` | 获取当前 locale |
| `useZIndex()` | z-index 管理 |
| `useNamespace(name)` | 类名前缀 |
| `useId()` | 生成唯一 id |
| `useResizeObserver()` | 监听 DOM 尺寸 |
| `useEventListener()` | 监听事件 |
| `useThrottleFn()` | 节流 |
| `useDebounceFn()` | 防抖 |
| `useDraggable()` | 拖拽（Dialog 等内部使用） |

---

## 9. 按需引入

### 9.1 自动按需（unplugin-vue-components）

```bash
npm install -D unplugin-vue-components
```

```ts
// vite.config.ts
import Components from 'unplugin-vue-components/vite'
import { MacrouiVueResolver } from 'unplugin-vue-components/resolvers'

export default {
  plugins: [
    Components({
      resolvers: [MacrouiVueResolver()],
    }),
  ],
}
```

### 9.2 手动按需（推荐用于 SSR）

```ts
import {
  ElButton,
  ElInput,
  ElConfigProvider,
} from '@macroui/macroui-vue'
```

### 9.3 在 Webpack 中按需

```js
// babel.config.js
module.exports = {
  plugins: [
    ['babel-plugin-import', {
      libraryName: '@macroui/macroui-vue',
      libraryDirectory: 'es',
      style: true,
    }, '@macroui/macroui-vue'],
  ],
}
```

### 9.4 性能优化要点（v2.17.84+）

`@macroui/macroui-vue` 的 ESM 产物里，下述运行时依赖全部走 `external`，**不会被打进 bundle**：

| 依赖 | 角色 |
|------|------|
| `vue` | peer（必需） |
| `@macroui/macroui-icons` | 图标默认 external；通过 `MacrouiIconsResolver` 按需引入 |
| `@vueuse/core` | Tooltip / Dropdown 等使用的基础 hook |
| `@popperjs/core` | 旧版 Popper 定位（`el-tooltip` 等） |
| `@floating-ui/dom` | 新版 Floating 定位（`el-floating`） |
| `lodash-unified` | 通用工具函数 |

**当前 ESM 体积拆解**：

```
dist/index.mjs  ~849 KB │ gzip: ~183 KB   // 90+ 组件代码本体
dist/index.js   ~613 KB │ gzip: ~151 KB   // CJS 同步产物
dist/style.css  ~291 KB │ gzip:  ~41 KB   // 全局样式
dist/locale/    ~216 KB │ gzip:   ~6 KB   // 67 种语言（仅打包用到的）
```

按需引入结论：

1. **全量注册**：`app.use(MacrouiVue, ...)`，体积 ≈ 849 KB / 183 KB (gzip)。
2. **按需 resolver**（推荐）：配合 `MacrouiVueResolver` + `MacrouiIconsResolver`，仅打包实际用到的组件，通常 < 200 KB (gzip)。
3. **手动按需**：参考 §9.2。

---

## 10. SSR / Nuxt

### 10.1 Nuxt 3 配置

```ts
// nuxt.config.ts
export default defineNuxtConfig({
  build: {
    transpile: ['@macroui/macroui-vue'],
  },
  vite: {
    optimizeDeps: {
      include: ['@macroui/macroui-vue'],
    },
  },
  css: [
    '@macroui/macroui/dist/themes.css',
    '@macroui/macroui/dist/styled.css',
    '@macroui/macroui-vue/dist/style.css',
  ],
})
```

```ts
// plugins/macroui.ts
import MacrouiVue from '@macroui/macroui-vue'
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(MacrouiVue)
})
```

### 10.2 SSR 注意事项

- 引用的组件必须出现在 template 中，否则会在 server 端跳过
- 使用 `<client-only>` 包裹需要 client 行为的组件（如 ElTooltip、ElPopover）
- 表单的 `el-form` SSR 推荐只渲染结构，校验放客户端

---

## 11. 浏览器支持

| 浏览器 | 版本 |
|--------|------|
| Chrome / Edge | 最近 2 年 |
| Firefox | 最近 2 年 |
| Safari | 14+ |
| iOS Safari | 14+ |
| Android Chrome | 90+ |
| IE | ❌ 不支持 |

支持 Vue 3（>= 3.4）。

---

## 12. 开发与构建

### 12.1 仓库克隆

```bash
git clone https://github.com/mobiui/macroui.git
cd macroui-vue
pnpm install
```

### 12.2 开发命令

```bash
# 库开发模式（监听源码，自动 build）
pnpm dev:lib

# 示例页面（examples/）
pnpm dev:demo

# 仅构建库
pnpm build:lib

# 完整构建（含 locale）
pnpm build:all

# 单元测试
pnpm test
pnpm test:watch
pnpm test:coverage

# E2E 测试
pnpm test:e2e
pnpm test:e2e:ui

# 代码风格
pnpm lint

# TypeScript 类型检查
pnpm typecheck
```

### 12.3 目录结构

```
macroui-vue/
├── examples/                # 示例项目（vite demo）
│   ├── pages/
│   ├── App.vue
│   ├── main.ts
│   └── vite.config.ts
├── packages/
│   ├── components/          # 90+ 组件
│   │   ├── ElButton/
│   │   │   ├── button.vue
│   │   │   ├── props.ts
│   │   │   └── index.ts
│   │   └── ...
│   ├── constants/           # 常量
│   │   ├── aria.ts
│   │   ├── date.ts
│   │   ├── form.ts
│   │   ├── key.ts
│   │   └── size.ts
│   ├── hooks/               # Hooks
│   ├── locale/              # i18n 语言包
│   │   ├── index.ts
│   │   └── lang/
│   ├── styles/              # 组件局部样式
│   └── utils/               # 工具函数
├── tests/
│   ├── unit/                # 单元测试 (vitest)
│   └── e2e/                 # E2E 测试 (playwright)
├── styles/
│   └── index.scss           # 全局样式入口
├── docs/                    # 文档
├── dist/                    # 构建产物
└── types/                   # 全局类型
```

### 12.4 创建新组件

```bash
mkdir -p packages/components/ElNewComponent
touch packages/components/ElNewComponent/new-component.vue
touch packages/components/ElNewComponent/props.ts
touch packages/components/ElNewComponent/index.ts
```

**props.ts：**

```ts
import { buildProps } from '@macroui/macroui-vue'
import type { ExtractPropTypes, PropType } from 'vue'

export const newComponentProps = buildProps({
  modelValue: {
    type: [String, Number, Boolean],
    default: '',
  },
  size: {
    type: String,
    values: ['large', 'default', 'small'],
    default: 'default',
  },
  disabled: Boolean,
})

export const newComponentEmits = {
  'update:modelValue': (value: any) => true,
}

export type NewComponentProps = ExtractPropTypes<typeof newComponentProps>
export type NewComponentEmits = typeof newComponentEmits
```

**new-component.vue：**

```vue
<template>
  <div class="el-new-component" :class="classes">
    <slot />
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { newComponentProps, newComponentEmits } from './props'

defineOptions({ name: 'ElNewComponent' })

const props = defineProps(newComponentProps)
const emit = defineEmits(newComponentEmits)

const classes = computed(() => [
  `el-new-component--${props.size}`,
  { 'is-disabled': props.disabled },
])
</script>
```

**index.ts：**

```ts
import { withInstall } from '@macroui/macroui-vue'
import NewComponent from './new-component.vue'

export const ElNewComponent = withInstall(NewComponent)
export default ElNewComponent
```

### 12.5 添加到 packages/components/index.ts

```ts
export { ElNewComponent } from './ElNewComponent'
```

---

## 13. NPM 发布

```bash
# 1. 确认登录状态
npm whoami

# 2. 修改 package.json 中的 version（遵循 semver）
# major：破坏性变更
# minor：向下兼容的新功能
# patch：bugfix

# 3. 构建
pnpm build:lib

# 4. 发布
npm publish --access public
```

### 13.1 2FA Token 设置

如账户启用 2FA，需要 Granular Access Token：

1. 访问 https://www.npmjs.com/settings/tokens
2. 点击 **Generate New Token** → **Granular Access Token**
3. 配置：
   - Token Name: `macroui-vue-publish`
   - Organization: `@macroui`
   - Permissions: **Publish packages** ✅
   - **2FA Bypass**: ✅ **必须启用**
4. 生成 Token

```bash
npm config set //registry.npmjs.org/:_authToken=YOUR_GRANULAR_TOKEN
npm publish --access public
```

---

## 14. 项目结构

详见 [12.3 目录结构](#123-目录结构)。

---

## 15. 常见问题

### Q1: 样式不生效？
- ✅ 检查 CSS 加载顺序：`themes.css` → `styled.css` → `style.css`
- ✅ 确认 `<html data-theme="...">` 已设置
- ✅ 检查 Tailwind config 中是否覆盖了类名前缀
- ✅ 检查 scoped 样式是否影响组件

### Q2: 组件不显示？
- ✅ 检查是否全局注册（`app.use(MacrouiVue)`）
- ✅ 检查是否局部注册 + 命名拼写
- ✅ 检查 `<template>` 根节点是否就只有一个元素

### Q3: TypeScript 类型错误？
- ✅ 运行 `pnpm typecheck`
- ✅ 确保 `@types/node` 已安装
- ✅ 检查 `tsconfig.json` 中 `moduleResolution` 为 `bundler` 或 `node16`

### Q4: 主题切换无效？
- ✅ 注意 `data-theme` 是属性，不是 CSS class
- ✅ 自定义主题需匹配 HSL 格式（`H S% L%`）

### Q5: Dialog / Tooltip 不显示？
- ✅ 检查 z-index（默认 2000+），被遮挡时可显式设置 `z-index`
- ✅ 检查 `teleported` 是否为 `true`

### Q6: 国际化切换无效果？
- ✅ 使用 `el-config-provider` 包裹根组件
- ✅ 邮箱地址不应写在语言包

### Q7: 表单校验不生效？
- ✅ `el-form-item` 必须设置 `prop`
- ✅ `rules` 中使用 `{ required: true, message, trigger }`
- ✅ 校验触发使用 `await formRef.value?.validate()` 是 Promise 式

---

## 16. 许可证

[MIT License](LICENSE)

联系方式：peng.deyou@gmail.com

---

## 🔗 相关项目

- [@macroui/macroui](https://www.npmjs.com/package/@macroui/macroui) — MacroUI 主题与样式
- [@macroui/macroui-icons](https://www.npmjs.com/package/@macroui/macroui-icons) — 图标库
- [Element Plus](https://element-plus.org/) — API 兼容性参考

## 🔗 链接

- 仓库地址：https://github.com/mobiui/macroui
- 问题反馈：https://github.com/mobiui/macroui/issues
