# Movk Nuxt - Nuxt 4 模块化工程套件 ::u-page-hero --- ui: container: lg:py-20 class: dark:bg-gradient-to-b from-neutral-900 to-neutral-950 orientation: horizontal --- :home #top :hero-background #title :::motion Nuxt 4 模块化 [工程套件]{.text-primary} ::: #description :::motion --- transition: duration: 0.6 delay: 0.3 --- 基于 Nuxt 4 和 Nuxt UI 的模块化扩展库,提供 Schema 驱动的自动表单生成、API 集成系统、常用 composables 工具等完整的表单与数据处理解决方案。 ::: #links :::motion --- transition: duration: 0.6 delay: 0.5 class: flex flex-wrap gap-x-6 gap-y-3 --- ::::u-button --- size: xl to: https://nuxt.mhaibaraai.cn/docs/getting-started trailing-icon: i-lucide-arrow-right --- 快速入门 :::: ::::u-button --- color: neutral icon: i-simple-icons-github size: xl target: _blank to: https://github.com/mhaibaraai/movk-nuxt variant: outline --- 查看源码 :::: ::: :: ::u-page-section --- class: dark:bg-neutral-950 --- #title 核心特性 #description 模块化设计,渐进式采用,从简单的工具函数到完整的自动化系统,满足各种开发需求。 #features :::u-page-feature --- icon: i-lucide-zap --- #title Schema 驱动 #description 通过 Zod Schema 定义数据结构,自动生成完整的表单界面和验证逻辑。 ::: :::u-page-feature --- icon: i-lucide-cloud --- #title API 集成 #description useApiFetch、useApiAuth、useUploadWithProgress 等完整的 API 请求和认证方案。 ::: :::u-page-feature --- icon: i-lucide-blocks --- #title 模块化架构 #description 清晰的分层架构:Core Systems、API System、Standalone Components、Composables。 ::: :::u-page-feature --- icon: i-lucide-package --- #title 独立组件库 #description 10+ 个高质量 UI 组件,无需依赖 AutoForm 即可独立使用。 ::: :::u-page-feature --- icon: i-lucide-shield-check --- #title 类型安全 #description 完整的 TypeScript 类型推断,从 Schema 到数据全程类型安全。 ::: :::u-page-feature --- icon: i-lucide-wrench --- #title 通用工具函数 #description useDateFormatter 等强大的 composables,提升开发效率。 ::: :: # 快速开始 ## 介绍 Movk Nuxt 是一个为 Nuxt 4 设计的**模块化工程套件** (Modular Engineering Suite)。它采用**分层架构**设计,旨在通过高度抽象的自动化系统(如 AutoForm、API System)解决重复性工作,同时提供高质量的独立组件(如 DatePicker)和组合式函数以满足定制化需求。 构建在 [Nuxt UI](https://ui.nuxt.com){rel=""nofollow""} 之上,Movk Nuxt 遵循相同的设计原则:美观、可访问、开发者友好。 ::callout{color="primary" icon="i-lucide-rocket"} 遵循 **渐进式采用** 原则——你可以只用它的日期处理工具,也可以只用它的 UI 组件,或者使用全自动化的 **AutoForm** 和 **API System** 。 :: ## 核心特性 ::card-group :::card{icon="i-lucide-blocks" title="模块化设计"} 按需使用。你可以只使用 UI 组件、日期工具函数,或者启用全套自动化系统。 ::: :::card{icon="i-lucide-zap" title="自动化系统"} **AutoForm** 通过 Schema 定义自动生成完整表单界面, **API System** 提供统一的请求和认证管理。 ::: :::card{icon="i-lucide-package" title="独立组件库"} 内置 `DatePicker` 、 `WithCopy` 、 `StarRating` 等 10+ 个通用 UI 组件,开箱即用。 ::: :::card{icon="i-lucide-wrench" title="通用工具函数"} `useDateFormatter` 、 `useApiFetch` 、 `useApiAuth` 等高质量 composables,即使不使用自动化系统也可独立使用。 ::: :: ## 架构分层 Movk Nuxt 采用清晰的分层架构: ::note{icon="i-lucide-layers"} **架构层级** - **Core Systems** - AutoForm(Schema 驱动表单) - **API System** - useApiFetch、useApiAuth、useUploadWithProgress、useDownloadWithProgress(API 请求与认证) - **Standalone Components** - DatePicker、StarRating、WithCopy 等独立 UI 组件 - **Composables** - useDateFormatter、useAutoForm 等通用组合式函数 - **Foundation** - 基于 [Nuxt UI](https://ui.nuxt.com){rel=""nofollow""}、[Zod v4](https://zod.dev){rel=""nofollow""}、[VueUse](https://vueuse.org){rel=""nofollow""} :: ## 核心模块 ### AutoForm - Schema 驱动的表单系统 基于 Zod Schema 的「定义即渲染」表单解决方案,支持 15+ 种控件类型、布局系统、依赖联动等高级特性。 ::tip{icon="i-lucide-book-open" to="https://nuxt.mhaibaraai.cn/docs/auto-form"} 深入了解 AutoForm 的核心概念和使用方法 :: ### API System - 统一的请求与认证 提供完整的 API 请求和认证方案,与 nuxt-auth-utils 无缝集成: - `useApiFetch` - 基于 useFetch 封装,支持自动认证、业务状态码检查、Toast 提示 - `useApiAuth` - 登录、登出、Session 管理 - `useUploadWithProgress` / `useDownloadWithProgress` - 带进度监控的文件上传下载 ::tip --- icon: i-lucide-cloud to: https://nuxt.mhaibaraai.cn/docs/composables/use-api-fetch --- 了解 API System 的使用方法 :: ### 独立组件 - 开箱即用 无需依赖 AutoForm,可直接使用的高质量 UI 组件: - `DatePicker` - 基于国际化标准的日期选择器(单选/范围/多选) - `WithCopy` / `WithClear` / `WithPasswordToggle` - 输入增强组件 - `StarRating` / `ColorChooser` - 交互组件 ::tip --- icon: i-lucide-box to: https://nuxt.mhaibaraai.cn/docs/components/with-character-limit --- 浏览所有可用组件 :: ### 通用工具函数 - `useDateFormatter` - 强大的日期格式化、解析、范围处理工具 - `useAutoForm` - AutoForm 的核心逻辑封装 ::tip --- icon: i-lucide-wrench to: https://nuxt.mhaibaraai.cn/docs/composables/use-date-formatter --- 查看所有 Composables :: ## 开发路线图 ::callout{color="primary" icon="i-lucide-map"} **已发布** - ✅ **AutoForm** - Schema 驱动的表单系统 - ✅ **API System** - API 请求封装和认证管理(useApiFetch、useApiAuth、useUploadWithProgress、useDownloadWithProgress) - ✅ **独立组件库** - DatePicker、StarRating、WithCopy 等组件 :: # 安装 ## 环境要求 ::callout{color="info"} - **Node.js** - `^22.x || ^24.x` - **Nuxt** - `^4.2.0`(需要 Nuxt 4) - **包管理器** - pnpm / npm / yarn :: ## 添加到 Nuxt 项目 ::steps{level="3"} ### 安装所需依赖 :::warning `@movk/nuxt` 依赖 **@nuxt/ui** 和 **zod** ,请确保已安装。 ::: :::warning 如果你使用的是 pnpm,请确保你在 `.npmrc` 文件中设置 `shamefully-hoist=true` ,或者在项目的根目录中安装 `tailwindcss` 。 ::: :::code-group ```bash [pnpm] pnpm add @movk/nuxt @nuxt/ui zod ``` ```bash [npm] npm install @movk/nuxt @nuxt/ui zod ``` ```bash [yarn] yarn add @movk/nuxt @nuxt/ui zod ``` ::: ### 模块配置 在 `nuxt.config.ts` 中注册模块: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@movk/nuxt'] }) ``` :: ## 可选配置 ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@movk/nuxt'], movk: { // 组件前缀 (默认: 'M') prefix: 'M' } }) ``` ## TypeScript 支持 Movk Nuxt 提供完整的 TypeScript 类型定义,安装后自动注入到项目中。 ```ts import type { z } from 'zod/v4' const { afz } = useAutoForm() const schema = afz.object({ email: afz.email(), // ✅ 完整的类型提示 age: afz.number() }) // 从 schema 推断数据类型 type FormData = z.output ``` ## Zod v4 集成 Movk Nuxt 使用 **Zod v4**,它引入了一些重要的 API 变更: ::code-group ```ts [✅ 正确用法 (Zod v4)] import { z } from 'zod/v4' // 使用专用验证函数 const emailSchema = z.email() const urlSchema = z.url() const uuidSchema = z.uuid() const datetimeSchema = z.iso.datetime() ``` ```ts [❌ 已废弃 (Zod v3)] import { z } from 'zod' // 不要使用旧的字符串验证方法 const emailSchema = z.string().email() const urlSchema = z.string().url() const uuidSchema = z.string().uuid() ``` :: ::tip{icon="i-lucide-book-open" to="https://zod.dev/api"} 查看 Zod 官方文档了解更多 v4 变更 :: # 模块配置 ## 介绍 Movk Nuxt 使用 `movk` 作为配置键,在 `nuxt.config.ts` 中进行配置: ## `prefix` - **类型**: `string` - **默认值**: `'M'` 组件前缀,用于避免与其他组件库的命名冲突。 ```ts [nuxt.config.ts] export default defineNuxtConfig({ movk: { prefix: 'My' // 组件变为 MyAutoForm, MyDatePicker 等 } }) ``` ## api API 系统提供完整的请求封装和认证管理,通过 `api` 选项配置。 ::note{icon="i-lucide-info"} API 系统的完整使用方法请参考 [useApiFetch](https://nuxt.mhaibaraai.cn/docs/composables/use-api-fetch) 和 [useApiAuth](https://nuxt.mhaibaraai.cn/docs/composables/use-api-auth) 。 :: ### 基础配置 ```ts [nuxt.config.ts] export default defineNuxtConfig({ movk: { api: { // 是否启用 API 功能 enabled: true, // 默认使用的端点名称 defaultEndpoint: 'default', // 是否启用调试模式 debug: false } } }) ``` ::field-group :::field{name="enabled" type="boolean"} 是否启用 API 功能。默认 `true` 。 ::: :::field{name="defaultEndpoint" type="string"} 默认使用的端点名称。默认 `'default'` 。 ::: :::field{name="debug" type="boolean"} 是否启用调试模式,开启后会在控制台输出请求日志。默认 `false` 。 ::: :: ### `endpoints` 支持配置多个 API 端点,每个端点可以有独立的配置: ```ts [nuxt.config.ts] export default defineNuxtConfig({ movk: { api: { endpoints: { // 默认端点 default: { baseURL: '/api' }, // 管理端点 admin: { baseURL: '/admin-api', auth: { tokenType: 'Bearer' } }, // 第三方 API external: { baseURL: 'https://api.example.com', headers: { 'X-API-Key': 'your-api-key' } } } } } }) ``` #### 端点选项 ::field-group :::field --- required: true name: baseURL type: string --- 端点的基础 URL。 ::: :::field{name="alias" type="string"} 端点别名。 ::: :::field{name="headers" type="Record"} 该端点的默认请求头 **仅服务端使用,不会暴露给客户端** 。 ::: :::field{name="auth" type="ApiAuthConfig"} 该端点的认证配置,会与全局配置合并。 ::: :::field{name="toast" type="ApiToastConfig"} 该端点的 Toast 配置,会与全局配置合并。 ::: :::field{name="success" type="ApiSuccessConfig"} 该端点的成功判断配置,会与全局配置合并。 ::: :: ### `auth` 配置自动认证行为,与 `nuxt-auth-utils` 集成: ```ts [nuxt.config.ts] export default defineNuxtConfig({ movk: { api: { auth: { // 启用认证 enabled: true, // Token 来源 tokenSource: 'session', // Session 中 token 的路径 sessionTokenPath: 'custom', // Token 类型 tokenType: 'Bearer', // 请求头名称 headerName: 'Authorization', // 401 未授权处理配置 unauthorized: { // 401 时跳转登录页 redirect: false, loginPath: '/login', // 401 时清除 session clearSession: false } } } } }) ``` #### 认证选项 ::field-group :::field{name="enabled" type="boolean"} 是否启用认证。默认 `false` 。 ::: :::field{name="tokenSource" type="'session' | 'custom'"} Token 来源。 `'session'` 从 nuxt-auth-utils 的 session 中获取(推荐), `'custom'` 使用自定义方式。默认 `'session'` 。 ::: :::field{name="sessionTokenPath" type="string"} Session 中 token 的路径。例如 `'token'` 对应 `session.token` , `'user.token'` 对应 `session.user.token` 。默认 `'token'` 。 ::: :::field{name="tokenType" type="'Bearer' | 'Basic' | 'Custom'"} Token 类型。默认 `'Bearer'` 。 ::: :::field{name="customTokenType" type="string"} 自定义 Token 类型值,当 `tokenType` 为 `'Custom'` 时使用。 ::: :::field{name="headerName" type="string"} 请求头名称。默认 `'Authorization'` 。 ::: :::field{name="unauthorized" type="ApiUnauthorizedConfig"} 401 未授权处理配置。 ::::collapsible :::::field-group ::::::field{name="redirect" type="boolean"} 401 错误时是否自动跳转登录页。默认 `false` 。 :::::: ::::::field{name="loginPath" type="string"} 登录页路径。默认 `'/login'` 。 :::::: ::::::field{name="clearSession" type="boolean"} 401 错误时是否自动清除 session。默认 `false` 。 :::::: ::::: :::: ::: :: ### `response` 配置如何判断 API 响应是否成功: ```ts [nuxt.config.ts] export default defineNuxtConfig({ movk: { api: { response: { // 成功状态码列表 successCodes: [200, 0], // 状态码字段名 codeKey: 'code', // 消息字段名 messageKey: 'message', // 数据字段名 dataKey: 'data' } } } }) ``` #### 成功判断选项 ::field-group :::field{name="successCodes" type="(number | string)[]"} 成功状态码列表,响应中的 `code` 值在此列表中视为成功。默认 `[200, 0]` 。 ::: :::field{name="codeKey" type="string"} 响应中状态码的字段名。默认 `'code'` 。 ::: :::field{name="messageKey" type="string"} 响应中消息的字段名。默认 `'message'` 。 ::: :::field{name="dataKey" type="string"} 响应中数据的字段名。默认 `'data'` 。 ::: :: ### `toast` 配置全局 Toast 提示行为: ```ts [nuxt.config.ts] export default defineNuxtConfig({ movk: { api: { toast: { // 全局启用提示 enabled: true, // 成功提示配置 success: { show: true, color: 'success', icon: 'i-lucide-circle-check', duration: 3000 }, // 错误提示配置 error: { show: true, color: 'error', icon: 'i-lucide-circle-x', duration: 3000 } } } } }) ``` #### Toast 选项 ::field-group :::field{name="enabled" type="boolean"} 是否全局启用提示。默认 `true` 。 ::: :::field{name="success" type="ApiToastItemConfig"} 成功提示配置。 ::::collapsible :::::field-group ::::::field{name="show" type="boolean"} 是否显示成功提示。默认 `true` 。 :::::: ::::::field{name="color" type="string"} 成功提示颜色。默认 `'success'` 。 :::::: ::::::field{name="icon" type="string"} 成功提示图标。 :::::: ::::::field{name="duration" type="number"} 成功提示持续时间(毫秒)。默认 `3000` 。 :::::: ::::: :::: ::: :::field{name="error" type="ApiToastItemConfig"} 错误提示配置。 ::::collapsible :::::field-group ::::::field{name="show" type="boolean"} 是否显示错误提示。默认 `true` 。 :::::: ::::::field{name="color" type="string"} 错误提示颜色。默认 `'error'` 。 :::::: ::::::field{name="icon" type="string"} 错误提示图标。 :::::: ::::::field{name="duration" type="number"} 错误提示持续时间(毫秒)。默认 `3000` 。 :::::: ::::: :::: ::: :: ## 配置优先级 配置按以下优先级合并(后者覆盖前者): 1. **Schema 默认值** - Zod Schema 定义的默认值 2. **全局配置** - `movk.api.auth`、`movk.api.toast`、`movk.api.response` 3. **端点配置** - `movk.api.endpoints[name].auth` 等 4. **请求级配置** - `useApiFetch` 的 `toast` 选项等 ```ts // 示例:请求级配置覆盖全局配置 const { data } = await useApiFetch('/users', { toast: { successMessage: '获取成功', // 覆盖全局配置 error: false // 禁用错误提示 } }) ``` ## 环境变量 对于敏感配置(如 API 密钥),建议使用环境变量: ```ts [nuxt.config.ts] export default defineNuxtConfig({ movk: { api: { endpoints: { external: { baseURL: process.env.EXTERNAL_API_URL, headers: { 'X-API-Key': process.env.EXTERNAL_API_KEY } } } } } }) ``` ```bash [.env] EXTERNAL_API_URL=https://api.example.com EXTERNAL_API_KEY=your-api-key ``` ::warning 不要将敏感信息(如 API 密钥、密码)直接写在配置文件中,始终使用环境变量。 :: # MCP Server ## 什么是 MCP? MCP (Model Context Protocol) 是一个标准化协议,使 AI 助手能够访问外部数据源和工具。@movk/nuxt 提供 MCP 服务器,让 AI 工具能够直接访问组件信息、API 文档和使用示例。 ## 可用资源 MCP 服务器提供以下资源供 AI 发现和浏览: | 资源 URI | 描述 | | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | `resource://movk-nuxt/components`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 所有组件及其分类 | | `resource://movk-nuxt/composables`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 所有 Composables 信息 | | `resource://movk-nuxt/auto-form`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | AutoForm Schema 定义和控件类型 | | `resource://movk-nuxt/documentation-pages`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 所有文档页面列表 | | `resource://movk-nuxt/examples`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 所有可用示例 | 在 Claude Code 等工具中使用 `@` 访问这些资源。 ## 可用工具 ### 列表查询 | 工具名称 | 描述 | | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | | `list_components`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 列出所有组件及其分类和基本信息 | | `list_composables`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 列出所有 Composables 信息 | | `list_examples`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 列出所有可用示例 | | `list_documentation_pages`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 列出所有文档页面 | | `list_getting_started_guides`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 列出入门指南 | ### 详情获取 | 工具名称 | 描述 | | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | `get_documentation_page`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 获取特定文档页面的完整内容 | | `get_example`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 获取示例的实现代码 | ### 搜索过滤 | 工具名称 | 描述 | | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | | `search_components_by_category`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 按分类或关键词搜索组件 | | `search_auto_form_by_category`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 按分类搜索 AutoForm 文档 | ## 可用提示 MCP 服务器为常见工作流提供引导式提示: | 提示名称 | 描述 | | ------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | | `find_component_for_usecase`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 根据使用场景推荐最适合的组件 | | `find_autoform_solution`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 根据表单需求推荐字段类型和配置方案 | 在 Claude Code 等工具中使用 `/` 访问这些提示。 ## 使用示例 配置完成后,可以向 AI 助手提出如下问题: **入门与配置** - "如何安装 @movk/nuxt?" - "支持哪些 Nuxt 版本?" - "如何配置组件前缀?" **组件查询** - "列出所有可用组件" - "DatePicker 接受哪些属性?" - "如何使用 StarRating 组件?" **AutoForm 表单** - "如何创建基础表单?" - "支持哪些字段类型?" - "如何实现条件渲染?" **API 系统** - "useApiFetch 和 $fetch 有什么区别?" - "如何监控文件上传进度?" - "如何处理认证登录?" **场景匹配** - "我需要构建用户注册表单,应该用什么?" - "帮我找适合实现日期选择的组件" - "我想实现带进度条的文件上传功能" AI 助手将使用 MCP 服务器获取结构化数据,并为 @movk/nuxt 提供引导式协助。 # LLMs.txt ## 什么是 LLMs.txt? LLMs.txt 是专为大型语言模型(LLM)设计的结构化文档格式。@movk/nuxt 提供 LLMs.txt 文件,其中包含 Nuxt 模块的完整信息,使 AI 工具能够更好地理解和协助 @movk/nuxt 的开发。 这些文件针对 AI 消费进行了优化,包含组件 API、Composables 签名、Schema 定义、使用模式和最佳实践的结构化信息。 ## 可用端点 我们提供 LLMs.txt 端点来帮助 AI 工具访问我们的文档: - `/llms.txt` - 包含所有组件和 Composables 的结构化概览及其文档链接(约 5K tokens) - `/llms-full.txt` - 提供完整文档,包括实现细节、示例、类型定义和最佳实践(100K+ tokens) ## 选择正确的文件 ::note **大多数用户应该从 /llms.txt 开始** \- 它包含所有基本信息,适用于标准 LLM 上下文窗口。仅当您需要完整的实现示例且 AI 工具支持大型上下文(200K+ tokens)时,才使用 `/llms-full.txt` 。 :: ## 重要使用说明 ::warning **@ 符号必须手动输入** \- 使用 Cursor 或 Windsurf 等工具时,必须在聊天界面中手动输入 `@` 符号。复制粘贴会破坏工具识别它作为上下文引用的能力。 :: ## AI 工具使用方法 任何支持 LLMs.txt 的 AI 工具都可以使用这些端点来更好地理解 @movk/nuxt。 - "使用 @movk/nuxt 文档 {rel=""nofollow""}" - "遵循 @movk/nuxt 完整指南 {rel=""nofollow""}" # ColorChooser ## 简介 `MColorChooser` 是一个可视化的颜色选择器组件,提供直观的颜色拾取界面。用户可以通过色盘、HSL 滑块等方式选择颜色,支持多种颜色格式输出。 ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/color-picker"} 基于 Nuxt UI 的 ColorPicker 组件封装 :: ## 基础用法 最简单的颜色选择器: :component-example{name="components-color-chooser-basic-example"} ## 颜色格式 通过 `format` 指定颜色输出格式,在表单中选择颜色配置: :component-example{name="components-color-chooser-format-example"} ## 自定义按钮 通过 `buttonProps` 自定义按钮样式: :component-example{name="components-color-chooser-button-example"} ## 自定义插槽 使用默认插槽自定义触发元素: :component-example{name="components-color-chooser-slot-example"} ## API ### Props :component-props{slug="MColorChooser"} ### Emits :component-emits{slug="MColorChooser"} ### Slots :component-slots{slug="MColorChooser"} ## Changelog :commit-changelog{prefix="/components"} # DatePicker ## 简介 `MDatePicker` 是一个功能强大的日期选择器组件,基于 `@internationalized/date` 提供国际化日期处理能力。支持单日期、日期范围、多日期选择等多种模式,并提供丰富的日期验证和格式化选项。 ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/calendar"} 基于 Nuxt UI 的 Calendar 组件封装 :: ::note{to="https://react-spectrum.adobe.com/internationalized/date/index.html"} 使用 `@internationalized/date` 库进行日期处理,确保时区安全和国际化支持。 :: ## 基础用法 最基础的日期选择器: :component-example{name="components-date-picker-basic-example"} ## 日期范围 通过 `range` 启用范围选择模式: :component-example{name="components-date-picker-range-example"} ## 多日期选择 通过 `multiple` 启用多选模式: :component-example{name="components-date-picker-multiple-example"} ## 日期限制 限制可选日期范围: :component-example{name="components-date-picker-validation-example"} ## 禁用特定日期 使用 `isDateUnavailable` 禁用特定日期: :component-example{name="components-date-picker-unavailable-example"} ## 格式化显示 支持多种日期格式输出: :component-example{name="components-date-picker-format-example"} ## 多月份显示 通过 `numberOfMonths` 同时显示多个月份的日历: :component-example{name="components-date-picker-months-example"} ## 自定义按钮 通过 `buttonProps` 自定义按钮样式: :component-example{name="components-date-picker-button-example"} ## API ### Props :component-props{slug="MDatePicker"} ### Emits :component-emits{slug="MDatePicker"} ### Slots :component-slots{slug="MDatePicker"} ## Changelog :commit-changelog{prefix="/components"} # SlideVerify ## 简介 `MSlideVerify` 是一个现代化的滑动验证组件,提供流畅的拖拽交互体验。基于 `motion-v` 实现丰富的动画效果,可用于表单验证、敏感操作确认等场景。 ::tip 使用 Motion 动画库提供流畅的拖拽交互和状态转换动画 :: ## 基础用法 最简单的滑动验证: :component-example{name="components-slide-verify-basic-example"} ## 自定义文本 自定义提示文本和图标: :component-example{name="components-slide-verify-text-example"} ## 自定义尺寸 通过 `height` 和 `sliderWidth` 调整组件尺寸: :component-example{name="components-slide-verify-size-example"} ## 禁用状态 禁用滑动验证: :component-example{name="components-slide-verify-disabled-example"} ## 自定义阈值 通过 `threshold` 设置完成验证所需的滑动距离百分比(0-1): :component-example{name="components-slide-verify-threshold-example"} ## 事件处理 监听验证事件并使用 `reset` 方法重置验证状态: :component-example{name="components-slide-verify-events-example"} ## API ### Props :component-props{slug="MSlideVerify"} ### Emits :component-emits{slug="MSlideVerify"} ### Slots :component-slots{slug="MSlideVerify"} ### Expose 您可以通过 [`useTemplateRef`](https://vuejs.org/api/composition-api-helpers.html#usetemplateref){rel=""nofollow""} 访问该类型化组件实例。 ```vue ``` 这将使您能够访问以下内容: | Name | Type | | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `reset()`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | `Promise`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} :br ::div --- className: - text-toned - mt-1 --- 重置验证状态 :: | ## Changelog :commit-changelog{prefix="/components"} # StarRating ## 简介 `MStarRating` 是一个功能丰富的星级评分组件。支持整星和半星评分、只读展示、自定义星星数量等多种模式,适用于评价、评级等场景。 ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/button"} 基于 Nuxt UI 的 Button 和 Badge 组件构建 :: ## 基础用法 默认 5 星评分系统: :component-example{name="components-star-rating-basic-example"} ## 自定义星星数量 通过 `max` 设置星星总数: :component-example{name="components-star-rating-max-example"} ## 只读模式 用于展示评分结果,不可交互: :component-example{name="components-star-rating-readonly-example"} ## 半星评分 通过 `allow-half` 启用半星评分: :component-example{name="components-star-rating-half-example"} ## 可清除评分 通过 `clearable` 允许点击已选星星清除评分: :component-example{name="components-star-rating-clearable-example"} ## 隐藏评分徽章 通过 `show-badge` 控制评分数字徽章的显示: :component-example{name="components-star-rating-badge-example"} ## 不同尺寸 通过 `size` 调整星星大小: :component-example{name="components-star-rating-size-example"} ## 自定义颜色 通过 `color` 设置选中星星的颜色: :component-example{name="components-star-rating-color-example"} ## 自定义图标 通过图标属性使用不同的图标样式: :component-example{name="components-star-rating-icon-example"} ## 事件监听 通过事件监听评分和悬停状态: :component-example{name="components-star-rating-events-example"} ## 键盘导航 组件支持完整的键盘交互,提升无障碍访问性: - **方向键**:`←` `→` `↑` `↓` 增减评分(支持半星步进) - **快捷键**:`Home` 设置最小评分,`End` 设置最大评分 - **数字键**:`0-9` 直接跳转到对应评分 - **清除键**:`Backspace` / `Delete` 清除评分(需启用 `clearable`) ::callout{color="primary" icon="i-lucide-keyboard"} 组件遵循 WCAG 无障碍规范,包含完整的 ARIA 属性和键盘焦点管理 :: ## 禁用状态 通过 `disabled` 禁用交互: :component-example{name="components-star-rating-disabled-example"} ## 自定义插槽 通过插槽添加前缀、后缀或自定义徽章: :component-example{name="components-star-rating-slot-example"} ## API ### Props :component-props{slug="MStarRating"} ### Emits :component-emits{slug="MStarRating"} ### Slots :component-slots{slug="MStarRating"} ## Changelog :commit-changelog{prefix="/components"} # ThemePicker ## 简介 `ThemePicker` 是一个可视化的主题配置组件,允许用户实时预览和自定义应用主题。用户可以调整主色调、中性色、圆角、字体、图标集和颜色模式,并导出配置代码。 ::callout --- color: info to: https://github.com/nuxt/ui/tree/v4/docs/app/components/theme-picker --- 配置变更会存储在本地,下次访问时自动恢复。组件源码来自 Nuxt UI :: ## 功能特性 ### 主色调配置 - **Primary**:选择主品牌色,支持黑色或预设颜色 - **Neutral**:配置中性色调,影响文本和背景 ### 样式配置 - **Radius**:调整全局圆角大小(0-5 级别) - **Font**:选择字体系列 - **Icons**:切换图标集(Lucide、Heroicons 等) ### 颜色模式 支持三种颜色模式: - `light`:浅色模式 - `dark`:深色模式 - `system`:跟随系统 ### 导出配置 配置完成后可导出: - `main.css`:CSS 变量配置 - `app.config.ts`:应用配置代码 ## 基础用法 ThemePicker 通常作为全局主题配置入口,放置在应用的头部或工具栏中: :component-example{name="components-theme-picker-basic-example"} ## Changelog :commit-changelog{prefix="/components/theme-picker"} # WithCharacterLimit ## 简介 `MWithCharacterLimit` 是一个带有字符数限制和实时计数显示的输入框组件。在输入框右侧实时显示当前字符数和最大字符限制,帮助用户控制输入长度。 ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input"} 基于 Nuxt UI 的 Input 组件封装 :: ## 基础用法 默认限制 50 个字符: :component-example{name="components-with-character-limit-basic-example"} ## 自定义限制 通过 `maxLength` 设置最大字符数: :component-example{name="components-with-character-limit-custom-example"} ## 带图标 为输入框添加语义化图标: :component-example{name="components-with-character-limit-icon-example"} ## 不同尺寸 支持多种尺寸: :component-example{name="components-with-character-limit-size-example"} ## 自定义计数器样式 通过 `counterClass` 自定义计数器样式: :component-example{name="components-with-character-limit-counter-example"} ## 表单场景 在表单字段中限制输入长度: :component-example{name="components-with-character-limit-form-example"} ## API ### Props :component-props{slug="MWithCharacterLimit"} ### Emits :component-emits{slug="MWithCharacterLimit"} ### Slots :component-slots{slug="MWithCharacterLimit"} ## Changelog :commit-changelog{prefix="/components/input"} # WithClear ## 简介 `MWithClear` 是一个带有一键清除功能的输入框组件。当输入框有内容时,会在右侧显示一个清除按钮(×),点击可快速清空输入框内容。 ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input"} 基于 Nuxt UI 的 Input 组件封装 :: ## 基础用法 最简单的清除功能输入框: :component-example{name="components-with-clear-basic-example"} ## 带图标 为输入框添加语义化图标: :component-example{name="components-with-clear-icon-example"} ## 搜索框 常用于搜索场景,快速清除搜索关键词: :component-example{name="components-with-clear-search-example"} ## 清除事件 通过 `@clear` 事件监听清除操作: :component-example{name="components-with-clear-event-example"} ## 自定义按钮 通过 `buttonProps` 自定义清除按钮样式: :component-example{name="components-with-clear-custom-example"} ## API ### Props :component-props{slug="MWithClear"} ### Emits :component-emits{slug="MWithClear"} ### Slots :component-slots{slug="MWithClear"} ## Changelog :commit-changelog{prefix="/components/input"} # WithCopy ## 简介 `WithCopy` 是一个带有一键复制功能的输入框组件。当输入框有内容时,会在右侧显示一个复制按钮,点击可快速复制输入框中的内容到剪贴板。 ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input"} 基于 Nuxt UI 的 Input 组件封装 :: ## 基础用法 最简单的复制功能输入框: :component-example{name="components-with-copy-basic-example"} ## 带图标 为输入框添加语义化图标: :component-example{name="components-with-copy-icon-example"} ## 自定义样式 通过 `buttonProps` 自定义复制按钮样式: :component-example{name="components-with-copy-custom-example"} ## 复制事件 通过 `@copy` 事件监听复制操作: :component-example{name="components-with-copy-event-example"} ## API ### Props :component-props{slug="MWithCopy"} ### Emits :component-emits{slug="MWithCopy"} ### Slots :component-slots{slug="MWithCopy"} ## Changelog :commit-changelog{prefix="/components/input"} # WithPasswordToggle ## 简介 `MWithPasswordToggle` 是一个带有显示/隐藏切换功能的密码输入框组件。提供眼睛图标按钮,点击可在明文和密文之间切换显示,提升密码输入的用户体验。 ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input"} 基于 Nuxt UI 的 Input 组件封装 :: ## 基础用法 最简单的密码切换输入框: :component-example{name="components-with-password-toggle-basic-example"} ## 带图标 为密码框添加锁的图标: :component-example{name="components-with-password-toggle-icon-example"} ## 登录表单 结合表单字段使用: :component-example{name="components-with-password-toggle-login-example"} ## 不同尺寸 支持多种尺寸: :component-example{name="components-with-password-toggle-size-example"} ## 自定义按钮 通过 `buttonProps` 自定义切换按钮样式: :component-example{name="components-with-password-toggle-custom-example"} ## API ### Props :component-props{slug="MWithPasswordToggle"} ### Emits :component-emits{slug="MWithPasswordToggle"} ### Slots :component-slots{slug="MWithPasswordToggle"} ## Changelog :commit-changelog{prefix="/components/input"} # 快速开始 ## 什么是 AutoForm AutoForm 是一个基于 Zod Schema 的自动表单生成系统。通过声明式的 Schema 定义,自动生成完整的表单界面,包括输入控件、验证规则和布局结构。 ::callout{color="primary" icon="i-lucide-lightbulb"} **核心优势** - **Schema 即表单** - 一份 Schema 同时定义数据结构、验证规则和 UI 配置 - **类型安全** - 完整的 TypeScript 类型推断,从 Schema 到表单数据 - **零模板代码** - 无需手写 `v-for` 或重复的表单字段模板 :: ## 快速开始 ### 基础示例 创建一个简单的用户注册表单: ::component-example --- collapse: true props: class: px-4 name: auto-form-basic-example --- :: ### 代码解析 ```vue ``` ## 核心概念 ### Schema 定义 [`useAutoForm`](https://nuxt.mhaibaraai.cn/docs/composables/use-auto-form) 返回核心对象 `afz`(AutoForm Zod),它是 [Zod](https://zod.dev/){rel=""nofollow""} 的增强版封装。 ```ts const { afz } = useAutoForm() const schema = afz.object({ username: afz.string().min(3), email: afz.email(), age: afz.number().min(18) }) ``` ::warning{title="为什么使用 afz 而不是原生 z?"} `afz` 代理了 Zod 的方法,自动拦截并注入 AutoForm 元数据。这确保了: 1. **元数据传递** - UI 配置(如 `label`、`placeholder`)能正确传递给表单组件 2. **链式调用保留** - 调用 `.optional()`、`.default()` 等方法时元数据不会丢失 3. **控件映射** - 自动将 Zod 类型映射到对应的 UI 控件 :::code-group ```ts [✅ 正确用法] // 使用 afz,元数据完整传递 const schema = afz.object({ username: afz.string().meta({ label: '用户名' }).optional() }) ``` ```ts [⚠️ 不推荐] // 原生 Zod 可能丢失 UI 配置 import { z } from 'zod/v4' const schema = z.object({ username: z.string() // 无法配置 label、placeholder 等 }) ``` ::: :: ### 控件映射 AutoForm 根据 Zod 类型自动选择对应的 UI 控件: | afz 方法 | 默认控件 | 说明 | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----- | | `afz.string()` | `UInput`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 文本输入框 | | `afz.number()` | `UInputNumber`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 数字输入框 | | `afz.boolean()` | `UCheckbox`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 复选框 | | `afz.enum()` | `USelect`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 下拉选择器 | | `afz.file()` | `UFileUpload`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 文件上传 | | `afz.calendarDate()` | `DatePicker`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 日期选择器 | ::tip{to="https://nuxt.mhaibaraai.cn/docs/auto-form/configuration#控件类型映射"} 查看所有可用的控件类型和自定义映射方式。 :: ### 元数据配置 使用 `.meta()` 方法配置字段的 UI 属性: ```ts const schema = afz.object({ username: afz.string() .min(3, '用户名至少 3 个字符') .meta({ label: '用户名', description: '请输入您的用户名', hint: '3-20 个字符', placeholder: '请输入', required: true }) }) ``` ::tip{to="https://nuxt.mhaibaraai.cn/docs/auto-form/configuration#元数据配置"} 了解所有可用的元数据属性和配置优先级。 :: ## Changelog :commit-changelog{prefix="/components"} # API 参考 ## Props :component-props{slug="MAutoForm"} ## Slots :component-slots{slug="MAutoForm"} ### 字段级插槽 ::note{icon="i-lucide-sparkles"} 除了组件级别的插槽外,AutoForm 还支持 **字段级别的动态插槽** ,并提供完整的 TypeScript 类型推断和编辑器自动补全。 :: **命名规则**: | 插槽模式 | 示例 | 说明 | | ----------------------------- | --------------------------------------------- | ------------------- | | `field-{slotType}` | `field-label`, `field-default` | 通用插槽,适用于所有字段 | | `field-{slotType}:{fieldKey}` | `field-label:username`, `field-default:email` | 特定字段插槽,享受**完整类型推导** | | `field-{position}:{fieldKey}` | `field-before:profile`, `field-content:tasks` | 嵌套字段布局插槽 | **可用的插槽类型**(`slotType`): | Slot Type | Description | | ------------- | ----------- | | `label` | 自定义字段标签 | | `hint` | 自定义提示信息 | | `description` | 自定义字段描述 | | `help` | 自定义帮助文本 | | `error` | 自定义错误信息显示 | | `default` | 自定义字段控件 | ::tip --- icon: i-lucide-sparkles to: https://nuxt.mhaibaraai.cn/docs/auto-form/slots --- **类型提示优势**:当你在模板中使用字段插槽时(如 `#field-default:username`),编辑器会自动提示插槽参数的类型,包括 `state`、`path`、`value`、`setValue` 等上下文属性,并根据 Schema 精确推断字段值类型。 查看插槽系统文档了解详细用法。 :: ## Emits :component-emits{slug="MAutoForm"} ## Expose 使用 [`useTemplateRef`](https://cn.vuejs.org/guide/essentials/template-refs){rel=""nofollow""} 访问类型化的组件实例: ```vue ``` **AutoForm 暴露的方法和属性**: | 名称 | 类型 | 描述 | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- | | `reset()` | `void`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 重置表单到初始状态,包括恢复 props 中的 `state` 和应用所有默认值 | | `clear()` | `void`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 清空表单所有字段数据 | | `formRef` | `Ref | null>`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | UForm 组件的模板引用,可访问底层表单的所有方法和属性 | ::tip{to="https://ui.nuxt.com/docs/components/form#expose"} 通过 `formRef` 可以访问 UForm 的所有底层功能,包括 `submit()` 、 `validate()` 、 `clear()` 、 `getErrors()` 、 `setErrors()` 等方法,以及 `errors` 、 `disabled` 、 `dirty` 、 `dirtyFields` 、 `touchedFields` 、 `blurredFields` 等响应式属性。详见 Nuxt UI Form 文档。 :: ## 类型定义 ### AutoFormFieldContext 字段上下文类型,在插槽和响应式函数中可用: ```ts interface AutoFormFieldContext { /** 表单数据 */ readonly state: S /** 字段路径 */ readonly path: P /** 字段值(可能为 undefined) */ readonly value: FieldValueType /** 设置字段值的回调函数 */ setValue: { (value: FieldValueType): void >>( relativePath: K extends never ? string : K, value: any ): void (relativePath: string, value: any): void } /** 字段错误列表 */ readonly errors: unknown[] /** 表单提交加载状态 */ readonly loading: boolean /** 字段折叠状态 */ readonly open?: boolean /** 数组元素下标 */ readonly count?: number } ``` ### AutoFormLayoutConfig 布局配置类型: ```ts interface AutoFormLayoutConfig { /** 布局容器组件 */ component?: C /** 布局组件属性(根据组件类型自动推断,支持响应式) */ props?: ReactiveValue, AutoFormFieldContext> /** 布局组件 CSS 类名(支持响应式) */ class?: ReactiveValue /** 布局组件插槽(根据组件类型自动推断,支持响应式) */ slots?: ReactiveValue>, AutoFormFieldContext> /** 布局内的字段定义(类似 z.object() 的 shape) */ fields: Record /** 所有字段统一渲染到的插槽名称(支持响应式) */ fieldSlot?: ReactiveValue & string, AutoFormFieldContext> /** 字段到插槽的映射关系(支持响应式,优先级高于 fieldSlot) */ fieldSlots?: ReactiveValue & string>>, AutoFormFieldContext> } ``` ### AutoFormNestedCollapsible 嵌套字段折叠配置类型: ```ts interface AutoFormNestedCollapsible { /** 是否启用折叠功能(默认:true) */ enabled?: boolean /** 默认打开状态 */ defaultOpen?: boolean /** 控制打开/关闭 */ open?: boolean /** 禁用折叠功能 */ disabled?: boolean /** 隐藏时卸载内容 */ unmountOnHide?: boolean /** 渲染的元素或组件类型(默认:'div') */ as?: unknown /** CSS 类名 */ class?: unknown /** UI 配置 */ ui?: { root?: ClassNameValue content?: ClassNameValue } } ``` # 配置 ## 核心类型 ### ReactiveValue AutoForm 的配置系统基于 `ReactiveValue` 类型,它支持三种值的形式: ```ts type ReactiveValue = [CTX] extends [never] ? MaybeRefOrGetter // 无上下文:ref 或 getter : MaybeRefOrGetter | ((ctx: CTX) => T) // 有上下文:ref、getter 或上下文函数 ``` **三种使用形式**: ```ts // 1. 静态值 label: '用户名' // 2. 响应式引用(ref 或 computed) const labelRef = ref('用户名') // 3. 上下文函数(可访问表单状态) label: ({ state }) => state?.userType === 'business' ? '公司名称' : '姓名' ``` ### AutoFormFieldContext 上下文函数接收的参数类型: ```ts interface AutoFormFieldContext { readonly state: S // 表单完整状态 readonly path: P // 当前字段路径 readonly value: any // 当前字段值 setValue: Function // 设置字段值 readonly errors: unknown[] // 字段错误列表 readonly loading: boolean // 表单加载状态 } ``` **使用示例**: ```ts afz.string().meta({ // 根据表单状态动态显示/隐藏 hidden: ({ state }) => state?.userType !== 'business', // 根据字段值动态提示 hint: ({ value }) => value ? `已输入 ${value.length} 个字符` : '请输入', // 根据错误状态动态样式 class: ({ errors }) => errors.length > 0 ? 'border-red-500' : '' }) ``` ## 元数据配置 ### 配置方式 使用 `.meta()` 方法配置字段的 UI 属性: ```ts afz.string() .min(3, '用户名至少 3 个字符') .meta({ label: '用户名', description: '请输入您的用户名', hint: '3-20 个字符', required: true }) ``` ### 可用属性 | 属性 | 类型 | 说明 | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | | `label` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段标签 | | `description` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段描述 | | `hint` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 提示信息 | | `help` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 帮助文本 | | `error` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 自定义错误信息 | | `required` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 标记字段为必填 | | `size` | `ReactiveValue<'xs' | 'sm' | 'md' | 'lg' | 'xl'>`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段大小 | | `as` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 渲染的元素或组件类型 | | `name` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段名称,用于匹配表单错误 | | `errorPattern` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 正则表达式,用于匹配表单错误名称 | | `if` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 条件渲染 | | `hidden` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 隐藏字段 | | `eagerValidation` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 启用立即验证 | | `validateOnInputDelay` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 输入验证延迟 | | `class` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 自定义 CSS 类名 | | `ui` | `ReactiveValue>`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | Nuxt UI 样式自定义对象 | | `collapsible` | `ReactiveValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 嵌套字段折叠配置 | | `fieldSlots` | `ReactiveValue>`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段插槽内容 | ### 配置优先级 AutoForm 按以下优先级合并配置(从低到高): 1. **自动默认值** - 系统根据字段类型自动生成 - `label` - 根据字段名生成(`userName` → `"User Name"`) - `required` - 根据 `.optional()` 自动判断 2. **全局配置** (`globalMeta`) - 统一配置所有字段 3. **字段级配置** (`.meta()`) - 针对特定字段的配置 ::note **优先级示例** :如果全局设置 `size: 'lg'` ,字段设置 `size: 'md'` ,最终生效的是 `'md'` 。 :: ## 控件配置 ### AutoFormControlsMeta 控件配置支持以下属性: ```ts interface AutoFormControlsMeta { type?: string // 控件类型名称 component?: C // 直接传入组件 controlProps?: ReactiveValue> // 控件属性 controlSlots?: ReactiveValue> // 控件插槽 error?: string // 验证错误消息 } ``` ### 使用方式 **方式 1:使用 type 指定控件** ```ts afz.string({ type: 'textarea', controlProps: { rows: 5, placeholder: '请输入...' } }) ``` **方式 2:直接传入组件** ```ts import CustomComponent from './CustomComponent.vue' afz.number({ component: CustomComponent, controlProps: { max: 5, size: 'lg' } }) ``` **方式 3:响应式控件配置** ```ts afz.string({ // 响应式属性 controlProps: ({ state }) => ({ disabled: state?.readonly === true, placeholder: state?.userType === 'business' ? '公司邮箱' : '个人邮箱' }), // 响应式插槽 controlSlots: ({ value }) => ({ trailing: () => h(UBadge, { label: `${value?.length || 0}` }) }) }) ``` ### 简写语法 支持直接传入字符串作为错误消息: ```ts // 完整写法 afz.email({ error: '请输入有效的邮箱地址' }) // 简写形式 afz.email('请输入有效的邮箱地址') ``` ## 全局配置 ### globalMeta 属性 通过 `AutoForm` 组件的 `globalMeta` prop 统一配置所有字段: ```vue ``` ### app.config.ts 全局样式 通过 `app.config.ts` 全局配置表单样式: ```ts [app.config.ts] export default defineAppConfig({ ui: { form: { base: 'space-y-4 min-w-0 sm:min-w-md' }, collapsible: { slots: { content: 'space-y-4' } } } }) ``` ## 控件类型映射 ### 默认映射 | afz 方法 | 默认控件 | 说明 | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----- | | `afz.string()` | `UInput`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 文本输入框 | | `afz.number()` | `UInputNumber`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 数字输入框 | | `afz.boolean()` | `UCheckbox`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 复选框 | | `afz.enum()` | `USelect`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 下拉选择器 | | `afz.file()` | `UFileUpload`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 文件上传 | | `afz.calendarDate()` | `DatePicker`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 日期选择器 | | `inputDate` | `UInputDate`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 日期输入框 | | `inputTime` | `UInputTime`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 时间输入框 | ### 可用的 type 值 | type 值 | 控件组件 | 说明 | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | `textarea` | `UTextarea`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 多行文本输入框 | | `switch` | `USwitch`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 开关切换器 | | `slider` | `USlider`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 滑块选择器 | | `pinInput` | `UPinInput`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | PIN 码输入框 | | `inputTags` | `UInputTags`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 标签输入框 | | `selectMenu` | `USelectMenu`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 下拉菜单选择器 | | `inputMenu` | `UInputMenu`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 输入菜单 | | `checkboxGroup` | `UCheckboxGroup`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 复选框组 | | `radioGroup` | `URadioGroup`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 单选框组 | | `inputDate` | `UInputDate`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 日期输入框 | | `inputTime` | `UInputTime`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 时间输入框 | | `withClear` | `WithClear`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 带清除按钮的输入框 | | `withPasswordToggle` | `WithPasswordToggle`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 带密码显示切换的输入框 | | `withCopy` | `WithCopy`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 带复制按钮的输入框 | | `withCharacterLimit` | `WithCharacterLimit`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 带字符计数的输入框 | | `colorChooser` | `ColorChooser`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 颜色选择器 | | `starRating` | `StarRating`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 星级评分器 | | `slideVerify` | `SlideVerify`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 滑动验证控件 | # String ## 基础用法 ::note{to="https://zod.dev/api#strings"} 使用 `afz.string()` 创建字符串字段: :: ### `Input` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input"} Input 组件文档 :: 基础输入框,通过 `controlProps` 配置图标、颜色等属性: ::component-example{collapse name="auto-form-field-string-input-example"} :: ### `Textarea` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/textarea"} Textarea 组件文档 :: 多行文本输入,设置 `type: 'textarea'`,支持自动调整高度: ::component-example{collapse name="auto-form-field-string-textarea-example"} :: ### `WithClear` 输入内容后显示清除按钮,设置 [`type: 'withClear'`](https://nuxt.mhaibaraai.cn/docs/components/with-clear): ::component-example{collapse name="auto-form-field-string-with-clear-example"} :: ### `WithPasswordToggle` 密码输入框,可切换显示/隐藏,设置 [`type: 'withPasswordToggle'`](https://nuxt.mhaibaraai.cn/docs/components/with-password-toggle): ::component-example --- collapse: true name: auto-form-field-string-with-password-toggle-example --- :: ### `WithCopy` 点击复制按钮快速复制内容,设置 [`type: 'withCopy'`](https://nuxt.mhaibaraai.cn/docs/components/with-copy): ::component-example{collapse name="auto-form-field-string-with-copy-example"} :: ### `WithCharacterLimit` 实时显示剩余字符数,设置 [`type: 'withCharacterLimit'`](https://nuxt.mhaibaraai.cn/docs/components/with-character-limit): ::component-example --- collapse: true name: auto-form-field-string-with-character-limit-example --- :: ### `PinInput` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/pin-input"} PinInput 组件文档 :: 验证码或 PIN 码输入,设置 `type: 'pinInput'`: ::component-example{collapse name="auto-form-field-string-pin-input-example"} :: ### `InputMenu` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input-menu"} InputMenu 组件文档 :: 支持输入和下拉选择,设置 `type: 'inputMenu'`: ::component-example{collapse name="auto-form-field-string-input-menu-example"} :: ### `ColorChooser` 颜色选择器,支持可视化颜色选择和格式化,设置 [`type: 'colorChooser'`](https://nuxt.mhaibaraai.cn/docs/components/color-chooser): ::component-example --- collapse: true name: auto-form-field-string-color-chooser-example --- :: ## 字符串格式验证 ::note{to="https://zod.dev/api#string-formats"} Zod v4 提供了专用的字符串验证方法: :: ::warning **Zod v4 迁移提示** Zod v4 移除了 `z.string().email()` 等方法,改为专用函数: ```ts // ❌ Zod v3 写法(已废弃) z.string().email() // ✅ Zod v4 正确写法 afz.email() ``` :: ## 示例 ### 动态类型 通过交互式示例探索字符串字段的各种类型和配置选项,包括:普通输入框、文本域、密码切换、清除按钮、复制功能、字符限制等增强功能,以及尺寸、颜色、行数等样式配置。 ::component-example --- collapse: true options: - name: type label: type items: - string - textarea - withPasswordToggle - withClear - withCopy - withCharacterLimit default: withCharacterLimit - name: color items: - error - primary - secondary - success - info - warning - neutral default: primary - name: size label: size items: - sm - xs - md - lg - xl default: sm - name: rows label: rows default: 5 - name: maxLength label: maxLength default: 20 props: class: px-4 name: auto-form-field-string-type-example --- :: ### 异步验证 使用 `.refine()` 方法配合异步函数实现服务端验证,例如检查用户名或邮箱是否已被占用: ::component-example --- collapse: true name: auto-form-field-string-async-validation-example --- :: # Number ## 基础用法 ::note{to="https://zod.dev/api#numbers"} 使用 `afz.number()` 创建数字字段: :: ### `InputNumber` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input-number"} InputNumber 组件文档 :: 基础数字输入框,支持整数、小数、范围限制等配置: ::component-example{collapse name="auto-form-field-number-input-example"} :: ### `Slider` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/slider"} Slider 组件文档 :: 数值滑块,设置 `type: 'slider'`,适合有明确范围的数值选择: ::component-example{collapse name="auto-form-field-number-slider-example"} :: ### `StarRating` 星级评分组件,设置 [`type: 'starRating'`](https://nuxt.mhaibaraai.cn/docs/components/star-rating),支持半星评分和自定义最大星数: ::component-example{collapse name="auto-form-field-number-star-rating-example"} :: # Boolean ## 基础用法 ::note{to="https://zod.dev/api#booleans"} 使用 `afz.boolean()` 创建布尔值字段: :: ### `Checkbox` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/checkbox"} Checkbox 组件文档 :: 默认的复选框控件,适合单个选项的勾选: ::component-example{collapse name="auto-form-field-boolean-checkbox-example"} :: ### `Switch` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/switch"} Switch 组件文档 :: 开关控件,设置 `type: 'switch'`,适合功能的启用/禁用: ::component-example{collapse name="auto-form-field-boolean-switch-example"} :: ### `SlideVerify` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/slide-verify"} SlideVerify 组件文档 :: 滑动验证控件,设置 `type: 'slideVerify'`,适合防机器人验证: ::component-example --- collapse: true name: auto-form-field-boolean-slide-verify-example --- :: # Date ::tip{to="https://nuxt.mhaibaraai.cn/docs/composables/use-date-formatter"} useDateFormatter 文档:基于 @internationalized/date 的日期格式化和处理工具。 :: ## 基础用法 使用 `afz.calendarDate()` 创建日期字段,基于 [`@internationalized/date`](https://react-spectrum.adobe.com/internationalized/date/index.html){rel=""nofollow""} 提供强大的日期处理能力: ### 日期选择器 ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/calendar"} Calendar 组件文档 :: 单日期选择,支持日期格式化和验证: ::component-example{collapse name="auto-form-field-date-picker-example"} :: ### 日期范围 日期范围选择,设置 `controlProps.range: true`: ::component-example{collapse name="auto-form-field-date-range-example"} :: ## 日期输入框 使用 `afz.inputDate()` 创建日期输入框,基于 `UInputDate` 组件提供原生的日期输入体验: ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input-date"} InputDate 组件文档 :: ### 单日期输入 简洁的日期输入框,适合表单填写场景: ::component-example{collapse name="auto-form-field-input-date-example"} :: ### 日期范围输入 支持范围选择的日期输入框: ::component-example{collapse name="auto-form-field-input-date-range-example"} :: ## 时间输入框 使用 `afz.inputTime()` 创建时间输入框,返回 `Time` 类型(来自 `@internationalized/date`): ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input-time"} InputTime 组件文档 :: ::component-example{collapse name="auto-form-field-input-time-example"} :: ## ISO 格式字符串 对于需要 ISO 标准格式字符串的场景,提供三种工厂方法: - `afz.isoDate()` - 验证 `YYYY-MM-DD` 格式 - `afz.isoTime()` - 验证 `HH:MM[:SS[.s+]]` 格式 - `afz.isoDatetime()` - 验证 ISO 8601 完整格式 ::component-example{collapse name="auto-form-field-iso-example"} :: # Enum ## 基础用法 ::note{to="https://zod.dev/api#enums"} 使用 `afz.enum()` 创建枚举字段: :: ### `Select` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/select"} Select 组件文档 :: 原生选择框,适合简单的枚举值: ::component-example{collapse name="auto-form-field-enum-select-example"} :: ### `SelectMenu` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/select-menu"} SelectMenu 组件文档 :: 下拉菜单,设置 `type: 'selectMenu'`,支持搜索、图标、分组: ::component-example{collapse name="auto-form-field-enum-select-menu-example"} :: ### `RadioGroup` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/radio-group"} RadioGroup 组件文档 :: 单选按钮组,设置 `type: 'radioGroup'`,适合 2-4 个选项: ::component-example{collapse name="auto-form-field-enum-radio-group-example"} :: ## API ```ts afz.enum(values, meta?) ``` ### Values ::note 决定字段的 **输出类型** ,支持三种形式: :: #### 1. 空数组 `[]` 返回 `ZodString`,用于运行时动态选项: ```ts const schema = afz.object({ category: afz.enum([], { type: 'selectMenu', controlProps: { items: categories.value // 运行时动态数据 } }) }) // 输出类型:string ``` #### 2. 字符串数组 返回 `ZodEnum`,提供类型安全的字面量联合类型: ```ts const Fruits = ['apple', 'banana', 'orange'] as const const schema = afz.object({ fruit: afz.enum(Fruits) }) // 输出类型:'apple' | 'banana' | 'orange' ``` #### 3. 枚举对象 返回 `ZodEnum`,支持 TypeScript 原生枚举: ```ts enum Status { Active = 'active', Inactive = 'inactive' } const schema = afz.object({ status: afz.enum(Status) }) // 输出类型:Status ``` ### Meta ::note 用于 **覆盖默认控件** 和配置元数据,支持三种配置方式: :: ::div{.rounded-md.border.border-accented.px-4} :::tabs ::::tabs-item{label="基础配置"} 不指定控件类型,使用默认的 `select` 控件: ```ts afz.enum(['a', 'b'], { label: '选择选项', hint: '请选择一个' }) ``` :::: ::::tabs-item{label="使用注册控件"} 通过 `type` 覆盖默认控件,同时保持输出类型不变: ```ts afz.enum(['a', 'b', 'c'], { type: 'radioGroup', // 覆盖默认的 select controlProps: { items: [ { label: '选项A', value: 'a' }, { label: '选项B', value: 'b' } ] } }) // 输出类型仍为:'a' | 'b' | 'c' // 但使用 radioGroup 渲染 ``` :::: ::::tabs-item{label="自定义组件"} 直接传入自定义组件实例: ```ts import MyEnumControl from './MyEnumControl.vue' afz.enum(['a', 'b'], { component: MyEnumControl, controlProps: { /* ... */ } }) ``` :::: ::: :: ## 枚举值配置优先级 ::tip `controlProps.items` 以最高优先级决定渲染内容, `enum()` 的第一个参数决定输出类型,两者可以兼用。 :: ### 配置方式 **方式 1:自动提取枚举值** 系统会自动将枚举值转换为 `items`: ```ts const Fruits = ['apple', 'banana', 'orange'] as const const schema = afz.object({ fruit: afz.enum(Fruits, { type: 'selectMenu', controlProps: { placeholder: '选择水果' // 系统自动转换:items: ['apple', 'banana', 'orange'] } }) }) ``` **方式 2:手动提供 items** 使用空数组 `[]` 作为第一个参数,完全由 `items` 决定类型和渲染: ```ts const schema = afz.object({ fruit: afz.enum([], { type: 'selectMenu', controlProps: { valueKey: 'value', items: [ { label: '苹果', value: 'apple' }, { label: '香蕉', value: 'banana' } ] } }) }) ``` **方式 3:类型 + 自定义显示(推荐)** 同时使用枚举参数和 `items`,既保证类型安全,又自定义显示: ```ts const schema = afz.object({ gender: afz.enum(['male', 'female', 'other'], { type: 'radioGroup', controlProps: { items: [ { label: '男', value: 'male' }, { label: '女', value: 'female' }, { label: '其他', value: 'other' } ] } }) }) // 输出类型:'male' | 'female' | 'other' // 显示内容:男 / 女 / 其他 ``` ::note **优先级规则**: - `controlProps.items` 存在时,以其为准进行渲染 - `enum([...])` 参数决定 TypeScript 输出类型 - 两者可以组合使用,实现类型安全 + 自定义显示 :: # Array ## 基础用法 ::note{to="https://zod.dev/api#arrays"} 使用 `afz.array()` 创建数组字段: :: ### `InputTags` ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/input-tags"} InputTags 组件文档 :: 标签输入框,设置 `type: 'inputTags'`,适合自由文本标签: ::component-example{collapse name="auto-form-field-array-input-tags-example"} :: ### `CheckboxGroup` ::callout --- color: neutral to: https://ui.nuxt.com/docs/components/checkbox-group --- CheckboxGroup 组件文档 :: 复选框组,设置 `type: 'checkboxGroup'`,适合预定义选项多选: ::component-example --- collapse: true name: auto-form-field-array-checkbox-group-example --- :: ### 对象数组 ::callout{color="neutral" to="https://ui.nuxt.com/docs/components/collapsible"} Collapsible 组件文档 :: 对象数组自动使用折叠面板渲染,支持添加、删除项: ::component-example --- collapse: true props: class: px-4 name: auto-form-field-array-object-example --- :: ### 动态数组 创建和管理动态添加或删除元素的表单数组。 ::component-example --- collapse: true props: class: px-4 name: auto-form-field-array-dynamic-example --- :: ## API ```ts afz.array(elementSchema, meta?) ``` ### ElementSchema 定义数组元素的类型,决定输出类型为 `T[]`: ```ts // 字符串数组 const schema1 = afz.array(afz.string()) // 输出类型:string[] // 对象数组 const schema2 = afz.array(afz.object({ name: afz.string(), age: afz.number() })) // 输出类型:{ name: string, age: number }[] ``` ### Meta ::note 用于 **覆盖数组元素的默认控件** 和配置元数据,同时保持输出类型不变: :: ::tip 对于对象数组(如 `afz.array(afz.object({...}))` ),系统会自动使用折叠面板渲染,无需手动指定控件类型。 :: ::div{.rounded-md.border.border-accented.px-4} :::tabs ::::tabs-item{label="基础配置"} 不指定控件类型,数组元素使用默认映射: ```ts afz.array(afz.string(), { label: '标签列表', hint: '添加多个标签' }) // string[] - 元素默认使用 input 控件 ``` :::: ::::tabs-item{label="覆盖元素控件"} 通过 `type` 覆盖数组**元素的默认控件**,输出类型保持不变: ```ts afz.array(afz.string(), { type: 'checkboxGroup', // 覆盖 string 的默认 input 控件 controlProps: { items: ['reading', 'gaming', 'sports'] } }) // 输出类型仍为:string[] // 但使用 checkboxGroup 渲染(多选) ``` :::::tip `afz.string()` 本来会被映射为 `input` 控件,通过第二个参数的 `type: 'checkboxGroup'` 覆盖了这个默认映射,将其改为多选框形式。 ::::: :::: ::::tabs-item{label="自定义组件"} 直接传入自定义组件实例: ```ts import MyArrayControl from './MyArrayControl.vue' afz.array(afz.string(), { component: MyArrayControl, controlProps: { /* ... */ } }) ``` :::: ::: :: # Object ## 基础用法 ::note{to="https://zod.dev/api#objects"} 使用 `afz.object()` 创建对象字段: :: ### 通用对象 通用对象类型通常与 `selectMenu` 结合使用,用于选择复杂数据结构: ::component-example --- collapse: true props: class: px-4 name: auto-form-field-object-basic-example --- :: ### 嵌套对象 嵌套对象结构,支持折叠展开: ::component-example --- collapse: true props: class: px-4 name: auto-form-field-object-nested-example --- :: ### 类型化对象 ::note 使用 TypeScript 接口约束对象结构,在定义 schema 时获得字段名的 IDE 提示: :: ::component-example --- collapse: true props: class: px-4 name: auto-form-field-object-typed-example --- :: ## API ### 基础调用 ```ts // 直接调用 afz.object(shape, meta?) // 柯里化调用(类型约束) afz.object()(shape, meta?) ``` **两种调用方式的区别**: ::div{.rounded-md.border.border-accented.px-4} :::tabs ::::tabs-item{label="直接调用"} 最常用的方式,直接传入字段定义: ```ts const schema = afz.object({ name: afz.string(), age: afz.number() }) ``` :::: ::::tabs-item{label="柯里化调用"} 先传入类型参数,在定义 schema 时获得字段提示: ```ts interface UserInfo { name: string age: number email: string } const schema = afz.object()({ name: afz.string(), // IDE 会提示 name | age | email email: afz.email() }) ``` :::::tip **使用场景** 基于 API 接口类型创建表单筛选器时,可以获得字段名的自动补全。 ::::: :::::note 类型参数 `` 仅用于 IDE 提示,不影响运行时验证。 ::::: :::: ::: :: ### Shape 定义对象的字段结构,决定输出类型: ```ts const schema = afz.object({ name: afz.string(), age: afz.number(), email: afz.email() }) // 输出类型:{ name: string, age: number, email: string } ``` ### Meta ::note 用于 **覆盖默认控件** 和配置元数据,支持多种场景: :: ::div{.rounded-md.border.border-accented.px-4} :::tabs ::::tabs-item{label="嵌套对象配置"} 为嵌套对象添加元数据(label、hint、折叠等): ```ts afz.object({ user: afz.object({ name: afz.string(), email: afz.email() }).meta({ label: '用户信息', collapsible: { defaultOpen: true } }) }) ``` :::: ::::tabs-item{label="通用对象控件"} 覆盖默认渲染方式,用于选择复杂对象: ```ts afz.object({ user: afz.object({ label: afz.string(), value: afz.string(), avatar: afz.object({ src: afz.url() }) }, { type: 'selectMenu', // 覆盖默认的嵌套表单 controlProps: { items: users.value, placeholder: '请选择用户' } }) }) // 输出类型:{ label: string, value: string, avatar: { src: string } } // 渲染方式:使用 selectMenu 选择器 ``` :::: ::::tabs-item{label="自定义组件"} 直接传入自定义组件实例: ```ts import MyObjectControl from './MyObjectControl.vue' afz.object({}, { component: MyObjectControl, controlProps: { /* ... */ } }) ``` :::: ::: :: ## 对象验证模式 三种对象工厂对应不同的额外字段处理策略: ::div{.rounded-md.border.border-accented.px-4} :::tabs ::::tabs-item{label="object(默认)"} 移除未定义的额外字段: ```ts const schema = afz.object({ name: afz.string() }) schema.parse({ name: '张三', extra: 'removed' }) // 结果:{ name: '张三' } ``` :::: ::::tabs-item{label="looseObject(宽松)"} 允许并保留额外字段: ```ts const schema = afz.looseObject({ name: afz.string() }) schema.parse({ name: '张三', extra: 'kept' }) // 结果:{ name: '张三', extra: 'kept' } ``` :::: ::::tabs-item{label="strictObject(严格)"} 额外字段会抛出验证错误: ```ts const schema = afz.strictObject({ name: afz.string() }) schema.parse({ name: '张三', extra: 'error' }) // 错误:Unrecognized key(s) in object: 'extra' ``` :::: ::: :: ::tip 可以在 [类型化对象示例](https://nuxt.mhaibaraai.cn/#%E7%B1%BB%E5%9E%8B%E5%8C%96%E5%AF%B9%E8%B1%A1) 中交互式体验三种验证模式的区别。 :: # File ## 基础用法 使用 `afz.file()` 创建文件上传字段: ### 单文件上传 基础的单文件上传: ::component-example{collapse name="auto-form-field-file-single-example"} :: ### 多文件上传 多文件上传,使用 `array` 配合 `type: 'file'`: ::component-example{collapse name="auto-form-field-file-multiple-example"} :: # 插槽概览 ## 概述 `AutoForm` 的插槽系统分为三个层级: - **表单插槽** - 自定义表单整体结构(header、footer、submit) - **字段插槽** - 微调每个字段的各个部分(label、hint、error 等) - **内容插槽** - 完全接管对象和数组字段的渲染(仅限对象/数组) ::tip 插槽支持 **通用模式** 和 **特定字段模式** ,让你既能统一样式,又能精确控制特定字段。 :: ## `setValue` 函数详解 `setValue` 是插槽中最重要的函数,用于更新字段值。它支持多种调用方式,能智能处理不同的数据结构。 ### 基本用法 ```ts // 直接设置当前字段的值 setValue(newValue) ``` ### 对象字段 对于对象类型的字段,`setValue` 支持相对路径: ```vue ``` ::tip 使用相对路径时,TypeScript 会自动推断可用的键名,提供完整的类型提示。 :: ### 数组字段 对于数组类型的字段,`setValue` 支持索引路径: ```vue ``` ### 类型签名 `setValue` 函数支持多种重载形式: | Signature | Return Type | Description | | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | | `setValue(value: T)` | `void`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 设置整个字段值 | | `setValue(relativePath: K, value: any)` | `void`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 设置子字段(对象)或元素属性(数组),`K` 会根据字段类型自动推导 | | `setValue(relativePath: string, value: any)` | `void`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字符串路径回退 - 支持任意路径字符串 | ## 命名规则总结 ### 插槽命名模式 ```ts // 表单级插槽 'header' | 'footer' | 'submit' // 字段级插槽 - 通用 'field-label' | 'field-hint' | 'field-description' | 'field-help' | 'field-error' | 'field-default' // 字段级插槽 - 特定字段 'field-label:username' | 'field-hint:email' | 'field-error:password' | 'field-default:bio' // 内容插槽 - 嵌套对象/数组 'field-content:profile' | 'field-before:tasks' | 'field-after:settings' ``` ## 综合示例 结合多种插槽类型,构建一个完整的自定义表单: ::component-example --- collapse: true props: class: px-4 name: auto-form-slots-mixed-example --- :: # 表单级插槽 ## 可用插槽 | Name | Description | | -------- | ----------- | | `header` | 表单顶部区域 | | `footer` | 表单底部区域 | | `submit` | 提交区域 | ### `header` 在表单字段上方添加自定义内容,适用于显示表单标题、说明文案或警告信息: ::component-example --- collapse: true props: class: px-4 name: auto-form-slots-basic-header-example --- :: ### `footer` 在表单字段下方、提交按钮上方添加自定义内容,适用于显示表单统计、进度信息或额外说明: ::component-example --- collapse: true props: class: px-4 name: auto-form-slots-basic-footer-example --- :: ### `submit` 完全控制提交按钮及其周围的逻辑和 UI,适用于实现多步骤提交、自定义按钮组或条件提交逻辑。 ::warning 使用此插槽时,需要设置 `:submit-button="false"` 禁用默认按钮。 :: ::component-example --- collapse: true props: class: px-4 name: auto-form-slots-submit-example --- :: ## API ### 插槽参数 | Name | Type | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | `errors` | `FormError[]`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 表单所有错误列表 | | `loading` | `boolean`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 表单提交加载状态 | | `fields` | `AutoFormField[]`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段配置列表 | | `state` | `FormState`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 表单当前状态 | # 字段级插槽 ## 可用插槽 | Name | Description | | ------------------- | ----------- | | `field-label` | 字段标签 | | `field-hint` | 字段提示文本 | | `field-description` | 字段描述 | | `field-help` | 字段帮助信息 | | `field-error` | 字段错误信息 | | `field-default` | 输入控件 | ## 使用方式 ### 通用模式 使用 `field-{slotType}` 格式,为**所有字段**应用统一的自定义样式: ::component-example --- collapse: true props: class: px-4 name: auto-form-slots-generic-example --- :: ### 特定字段模式 使用 `field-{slotType}:{fieldPath}` 格式,仅针对**特定字段**自定义: ::component-example --- collapse: true props: class: px-4 name: auto-form-slots-field-example --- :: ::note 特定字段插槽的优先级 **高于** 通用插槽。如果同时定义了 `field-label` 和 `field-label:username` ,后者会生效。 :: ## 插槽参数 ### 通用参数 所有字段插槽都接收以下参数: | Name | Type | Description | | ---------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | | `state` | `FormState`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 表单数据 | | `path` | `string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段路径 (如 `'username'` 或 `'profile.name'`) | | `value` | `any`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 当前字段值 | | `setValue` | `SetValueFn`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 设置字段值的函数 | | `errors` | `unknown[]`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段错误列表 | | `loading` | `boolean`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 表单加载状态 | ### 特定插槽参数 不同插槽类型有各自的专属参数: | Slot Type | Parameter | Type | Description | | ------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | `field-label` | `label` | `string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段标签文本 | | `field-hint` | `hint` | `string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 提示文本 | | `field-description` | `description` | `string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 描述文本 | | `field-help` | `help` | `string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 帮助文本 | | `field-error` | `error` | `string | boolean`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 错误信息 | ::tip{title="field-default 插槽"} `field-default` 插槽用于自定义**基础类型字段**(string、number、boolean 等)的输入控件。 对于对象和数组字段,请使用 [内容插槽](https://nuxt.mhaibaraai.cn/docs/auto-form/slots/content) 中的 `field-content`。 :: # 内容级插槽 ## 可用插槽 ::warning 内容插槽( `field-content` , `field-before` , `field-after` ) **仅适用于对象字段和数组字段** ,不适用于基础类型字段(如 string, number, boolean 等)。 :: ::tip 基础类型字段请使用 `field-default` 插槽来自定义输入控件。 :: | Name | Description | | ---------------------- | ------------------------- | | `field-before:{path}` | 在字段渲染之前插入自定义内容,不影响字段本身的渲染 | | `field-after:{path}` | 在字段渲染之后插入自定义内容,不影响字段本身的渲染 | | `field-content:{path}` | 完全替换字段的默认渲染逻辑 | ## 使用方式 ### `field-content` 完全替换**对象或数组字段**的默认渲染,适用于需要完全自定义字段内容的场景: ::component-example{collapse name="auto-form-slots-custom-controls-example"} :: ### `field-before` 在字段渲染之前插入内容,常用于添加分隔线、标题或说明信息: ::note 如果字段通过 `type` 覆盖了默认控件(如数组字段使用 `inputTags` ), `field-before` 插槽将不会生效。 :: ::component-example --- collapse: true props: class: px-4 name: auto-form-slots-field-before-example --- :: ### `field-after` 在字段渲染之后插入内容,常用于添加提示信息、统计数据或验证反馈: ::component-example --- collapse: true props: class: px-4 name: auto-form-slots-field-after-example --- :: ## 示例 ### 数组字段自定义 使用 `field-content` 插槽完全自定义数组字段的渲染逻辑: ::component-example --- collapse: true props: class: px-4 w-full name: auto-form-slots-array-content-example --- :: ### 嵌套对象自定义 使用 `field-content` 插槽自定义嵌套对象字段的渲染逻辑: ::component-example --- collapse: true props: class: px-4 name: auto-form-slots-nested-content-example --- :: ## 插槽参数 内容插槽接收以下参数: | Name | Type | Description | | ---------- | -------------------------------------------------------------------------------------------------------------------------------- | --------------- | | `state` | `FormState`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 表单数据 | | `path` | `string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段路径 | | `value` | `any`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 当前字段值 | | `setValue` | `SetValueFn`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 设置字段值的函数,支持相对路径 | | `errors` | `unknown[]`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字段错误列表 | | `loading` | `boolean`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 表单加载状态 | ::tip{title="setValue 函数"} 在内容插槽中, `setValue` 支持相对路径更新子字段,详见 [setValue 函数详解](https://nuxt.mhaibaraai.cn/docs/auto-form/slots#setvalue-%E5%87%BD%E6%95%B0%E8%AF%A6%E8%A7%A3) 。 :: # 可折叠字段 ## 基础用法 ::tip 折叠功能基于 Nuxt UI 的 `UCollapsible` 组件实现,支持动画效果和响应式配置。 :: ::component-example --- props: class: px-4 name: auto-form-advanced-collapsible-example --- :: ## 配置选项 ### `AutoFormNestedCollapsible` `collapsible` 属性接受一个配置对象,支持以下选项: | 属性 | 类型 | 默认值 | 说明 | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------- | | `enabled` | `boolean`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | `true` | 是否启用折叠功能 | | `defaultOpen` | `boolean`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | | 默认是否展开 | | `open` | `boolean`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | | 控制展开/收起状态(受控模式) | | `disabled` | `boolean`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | | 禁用折叠功能(始终展开) | | `unmountOnHide` | `boolean`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | `true` | 隐藏时卸载内容 | | `as` | `string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | `'div'` | 渲染的元素类型 | | `class` | `ClassNameValue`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | | CSS 类名 | | `ui` | `{ root?: ClassNameValue; content?: ClassNameValue; }`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | | UI 样式配置 | ## 样式自定义 ### 全局样式 通过 `app.config.ts` 自定义折叠面板的全局样式: ```ts [app.config.ts] export default defineAppConfig({ ui: { collapsible: { slots: { root: 'border border-gray-200 rounded-lg mb-4', content: 'p-4 space-y-4' } } } }) ``` # 条件渲染 ## 基本概念 ::note AutoForm 提供了两种条件渲染方式: - **`hidden`** - 隐藏字段(字段仍存在于 DOM 中) - **`if`** - 条件渲染(不满足条件时字段不会创建) :: ## 基础用法 根据账户类型显示不同的字段集: ::component-example --- props: class: px-4 name: auto-form-advanced-conditional-example --- :: # 自定义控件 ## 概述 `AutoForm` 允许你注册自定义 Vue 组件作为表单字段的渲染控件。这使得你可以扩展 `AutoForm` 的功能,使用任何 UI 组件库或自定义组件来渲染特定类型的字段。 自定义控件适用于以下场景: - 集成第三方组件库(如富文本编辑器、颜色选择器等) - 实现特定业务逻辑的输入控件 - 复用现有的表单组件 - 为特定字段类型提供统一的渲染方式 ## 基本用法 通过 `useAutoForm` 注册自定义控件: ```ts import { RichTextEditor } from '#components' const { afz, controls } = useAutoForm({ // 注册控件类型为 'richtext' richtext: { component: RichTextEditor, controlProps: { class: 'w-full' } } }) ``` 然后在 schema 中使用自定义控件: ```ts const schema = afz.object({ content: afz .string({ type: 'richtext', // 指定使用自定义控件 controlProps: { readonly: false } }) .meta({ label: '文章内容', description: '使用富文本编辑器编写文章内容' }) }) ``` 最后将 `controls` 传递给 `AutoForm` 组件: ```vue ``` ## 控件组件要求 自定义控件组件需要满足以下要求: 1. **接受 v-model**:组件必须支持 `v-model` 绑定 ```vue ``` 2. **接收 controlProps**:组件应该能够接收并处理传入的 props ```vue ``` ## 示例 ::component-example --- collapse: true props: class: p-4 w-full name: auto-form-custom-control-rich-text-example --- :: ## 注意事项 ::note **类型推断** 使用自定义控件时,Zod schema 的类型推断仍然基于底层的数据类型(如 `string()` 、 `number()` 等),而不是控件类型。 :: ::tip **Props 合并** `useAutoForm` 中的 `controlProps` 和字段级别的 `controlProps` 会进行浅合并,字段级别的配置优先级更高。 :: ::warning **组件导入** 确保自定义控件组件已正确导入。在 Nuxt 项目中,可以使用 `#components` 自动导入,或手动导入组件。 :: # 全局元数据 ## 基础用法 通过 `global-meta` 统一设置字段的大小、必填样式和验证延迟: ::component-example --- props: class: px-4 name: auto-form-advanced-global-meta-example --- :: # 布局系统 ## 基础用法 ### 简单容器布局 使用 `afz.layout` 创建一个基础的容器布局,通过 `class` 属性控制样式: ::component-example --- collapse: true props: class: px-4 name: auto-form-layout-simple-example --- :: ::note 布局字段的 key(如 `$layout` )以 `$` 开头是惯例写法,但不是必需的。你可以使用任何字段名,只要其值为 `afz.layout()` 即可。 :: ### 网格布局 使用 CSS Grid 创建多列布局,通过字段级别的 `class` 控制跨列: ::component-example --- collapse: true props: class: px-4 name: auto-form-layout-grid-example --- :: ### 响应式多列布局 利用 Tailwind 的响应式前缀,创建自适应不同屏幕尺寸的布局: ::component-example --- collapse: true props: class: px-4 name: auto-form-layout-responsive-example --- :: ::tip 使用 Tailwind 的响应式前缀( `md:` 、 `lg:` )可以轻松实现跨设备的自适应布局。字段可以通过 `col-span-full` 占据整行。 :: ## 高级布局容器 ### 手风琴布局 使用 Nuxt UI 的 `UAccordion` 组件,将字段组织在可折叠的面板中: ::component-example --- collapse: true props: class: px-4 name: auto-form-layout-accordion-example --- :: ::tip `fieldSlots` 用于将不同字段分配到布局组件的不同插槽中。插槽名称需要与 `items` 中定义的 `slot` 对应。 :: ### 标签页布局 使用 `UTabs` 组件创建分页式表单,支持动态响应式配置: ::component-example --- collapse: true props: class: px-4 name: auto-form-layout-tabs-example --- :: ::tip 标签页布局展示了如何根据表单状态动态调整布局结构。切换用户类型时,标签页标题和字段分布会自动更新。 :: ::warning 在使用条件渲染( `if` )时,建议将字段标记为 `.optional()` ,避免验证错误。 :: ## 布局配置详解 ### `AutoFormLayoutConfig` `afz.layout` 接受一个配置对象,包含以下属性: | 属性 | 类型 | 说明 | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | `component` | `Component | string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 布局容器组件(默认:`'div'`) | | `class` | `string | (context) => string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | CSS 类名,支持响应式函数 | | `props` | `object | (context) => object`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 组件属性,支持响应式函数 | | `slots` | `Record VNode> | (context) => ...`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 组件插槽内容 | | `fields` | `Record`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 包含的字段定义 | | `fieldSlot` | `string | (context) => string`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 将**所有**字段渲染到指定插槽 | | `fieldSlots` | `Record | (context) => ...`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 将**不同**字段分配到不同插槽 | ### 响应式配置函数 `class`、`props`、`slots`、`fieldSlot` 和 `fieldSlots` 都支持响应式函数,接收一个上下文对象: ```ts interface LayoutContext { state: FormState // 表单当前状态 path: string // 布局字段的路径 value: any // 布局字段的值(通常为空) setValue: (value) => void // 设置值的函数 } ``` **示例**: ```ts afz.layout({ class: ({ state }) => state?.mode === 'advanced' ? 'grid grid-cols-3 gap-4' : 'space-y-4', props: ({ state }) => ({ disabled: state?.readOnly === true }) }) ``` ### `fieldSlot` vs `fieldSlots` - **`fieldSlot`** - 将所有字段渲染到同一个插槽,适用于简单场景: ```ts afz.layout({ component: UAccordion, fieldSlot: 'content', // 所有字段都渲染到 'content' 插槽 fields: { /* ... */ } }) ``` - **`fieldSlots`** - 精确控制每个字段的插槽位置,适用于分组场景: ```ts afz.layout({ component: UAccordion, fieldSlots: { name: 'panel-1', email: 'panel-1', bio: 'panel-2' }, fields: { /* ... */ } }) ``` ## 嵌套布局 布局字段可以无限嵌套,构建复杂的层级结构: ::component-example --- collapse: true props: class: px-4 name: auto-form-layout-nested-example --- :: ::note 嵌套布局的深度没有限制,但过深的嵌套可能影响可读性。建议保持在 2-3 层。 :: ## 数据提取机制 布局字段在数据验证时会被**自动展开**为其内部的实际数据字段: ```ts const schema = afz.object({ $layout: afz.layout({ fields: { name: afz.string(), email: afz.email() } }) }) // 表单数据结构(不包含 $layout) const formData = { name: 'John', email: 'john@example.com' } ``` 这个机制由 `extractPureSchema` 函数实现,它会递归遍历 schema,提取所有布局字段中的实际数据字段,生成一个“纯净”的验证 schema。 ::tip 这意味着你可以自由调整布局结构,而不影响现有的数据结构或验证逻辑。 :: # useAutoForm ## Usage 使用自动导入的 `useAutoForm` composable 创建类型化的 Zod Schema,为 [`AutoForm`](https://nuxt.mhaibaraai.cn/docs/auto-form) 组件提供验证规则和控件配置。 ```vue ``` - `useAutoForm` 返回增强的 Zod 工厂对象 `afz`,支持为 Schema 附加 AutoForm 元数据。 - 元数据会自动在 Zod 的链式调用中持久化(如 `.optional()`, `.nullable()`, `.default()` 等)。 ::note 使用 `afz` 工厂创建的 Schema 会自动拦截 Zod 的克隆方法,确保元数据在链式调用时不会丢失。 :: ## 自定义控件注册 当使用自定义控件时,需要通过 `defineControl` 辅助函数定义: ```ts const { afz, defineControl } = useAutoForm({ myInput: defineControl({ component: MyCustomInput, controlProps: { class: 'w-full' } }) }) // 现在可以在 Schema 中使用 const schema = afz.string({ type: 'myInput', controlProps: { variant: 'outline' } }) ``` ## 控件映射 ### 默认 | Schema 类型 | 默认控件 | 说明 | | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----- | | `string` | `UInput`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 文本输入框 | | `number` | `UInputNumber`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 数字输入框 | | `boolean` | `UCheckbox`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 复选框 | | `enum` | `USelect`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 下拉选择 | | `file` | `UFileUpload`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 文件上传 | | `calendarDate` | `DatePicker`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 日期选择器 | ### 扩展控件类型 通过 `type` 元数据可以使用以下扩展控件: | type 类型 | 控件 | 说明 | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------- | | `switch` | `USwitch`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 开关 | | `textarea` | `UTextarea`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 多行文本输入框 | | `slider` | `USlider`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 滑块 | | `pinInput` | `UPinInput`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 验证码输入框 | | `inputTags` | `UInputTags`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 标签输入 | | `selectMenu` | `USelectMenu`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 选择菜单 | | `inputMenu` | `UInputMenu`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 输入菜单 | | `checkboxGroup` | `UCheckboxGroup`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 复选框组 | | `radioGroup` | `URadioGroup`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 单选框组 | | `withClear` | `UInput`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 带清除按钮的输入框 | | `withPasswordToggle` | `UInput`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 密码显示切换 | | `withCopy` | `UInput`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 带复制按钮 | | `withCharacterLimit` | `UInput`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 字符计数限制 | | `colorChooser` | `UColorPicker`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 颜色选择器 | | `starRating` | `StarRating`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 星级评分 | | `slideVerify` | `SlideVerify`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | 滑动验证 | ## API ### useAutoForm() `useAutoForm(controls?: AutoFormControls): UseAutoFormReturn`{.shiki,shiki-themes,material-theme-lighter,material-theme,material-theme-palenight lang="ts-type"} 创建 AutoForm 工厂和控件管理器。 #### Parameters ::field-group :::field{name="controls" type="AutoFormControls"} 自定义控件映射对象,用于扩展或覆盖默认控件。 ::::collapsible ```ts const { afz } = useAutoForm({ myCustomInput: defineControl({ component: CustomInputComponent, controlProps: { class: 'w-full' } }) }) ``` :::: ::: :: #### Returns ::field-group :::field{name="afz" type="TypedZodFactory"} 类型化的 Zod 工厂对象,用于创建带元数据的 Schema。 ::: :::field{name="defineControl" type="Function"} 定义控件的辅助函数。 ::: :::field{name="DEFAULT_CONTROLS" type="Object"} 默认控件映射对象。 ::: :::field{name="controls" type="Object"} 传入的自定义控件映射。 ::: :::field{name="getAutoFormMetadata" type="Function"} 从 Schema 中提取自定义元数据的工具函数。 ::: :: ### afz 工厂方法 #### 基础类型 ::field-group :::field{name="afz.string()" type="(meta?: string | AutoFormMeta) => ZodString"} 创建字符串类型 Schema,默认映射到 `UInput` 组件。 ::: :::field{name="afz.number()" type="(meta?: string | AutoFormMeta) => ZodNumber"} 创建数字类型 Schema,默认映射到 `UInputNumber` 组件。 ::: :::field --- name: afz.boolean() type: "(meta?: string | AutoFormMeta) => ZodBoolean" --- 创建布尔类型 Schema,默认映射到 `UCheckbox` 组件。 ::: :::field{name="afz.file()" type="(meta?: string | AutoFormMeta) => ZodFile"} 创建文件类型 Schema,默认映射到 `UFileUpload` 组件。 ::: :::field --- name: afz.calendarDate() type: "(meta?: string | AutoFormMeta) => ZodType" --- 创建日期类型 Schema,默认映射到 `DatePicker` 组件。 ::: :: #### Zod v4 专用验证 ::field-group :::field{name="afz.email()" type="(meta?: string | AutoFormMeta) => ZodEmail"} 创建 Email 验证 Schema。推荐使用此方法代替 `afz.string().email()` (已在 Zod v4 中弃用)。 ::: :::field{name="afz.url()" type="(meta?: string | AutoFormMeta) => ZodUrl"} 创建 URL 验证 Schema。 ::: :::field{name="afz.uuid()" type="(meta?: string | AutoFormMeta) => ZodUuid"} 创建 UUID 验证 Schema。 ::: :: #### 集合类型 ::field-group :::field --- name: afz.array() type: "(schema: ZodType, overwrite?: AutoFormMeta) => ZodArray" --- 创建数组类型 Schema。 ::: :::field --- name: afz.tuple() type: "(schemas: [ZodType, ...ZodType[]], overwrite?: AutoFormMeta) => ZodTuple" --- 创建元组类型 Schema。 ::: :::field --- name: afz.enum() type: "(values: any, overwrite?: AutoFormMeta) => ZodEnum" --- 创建枚举类型 Schema,默认映射到 `USelect` 组件。 ::: :: #### 对象类型 ::field-group :::field --- name: afz.object() type: "(shape: ZodRawShape, meta?: AutoFormMeta) => ZodObject" --- 创建标准对象 Schema。支持柯里化写法以获得更好的类型推断。 ::::collapsible ```ts // 推荐:柯里化写法(更好的类型推断) const schema = afz.object()({ field: afz.string() }) // 也可以:直接传参 const schema = afz.object({ field: afz.string() }) ``` :::: ::: :::field --- name: afz.looseObject() type: "(shape: ZodRawShape, meta?: AutoFormMeta) => ZodObject" --- 创建宽松对象 Schema(允许额外字段)。 ::: :::field --- name: afz.strictObject() type: "(shape: ZodRawShape, meta?: AutoFormMeta) => ZodObject" --- 创建严格对象 Schema(不允许额外字段)。 ::: :: #### 布局系统 ::field-group :::field --- name: afz.layout() type: "(config: AutoFormLayoutConfig) => ZodType" --- 创建布局字段,用于在表单中插入布局组件(如 `UFormGrid`, `UCard` 等)。 ::::collapsible ```ts const schema = afz.object({ $layout: afz.layout({ component: 'UFormGrid', props: { columns: 2 } }), firstName: afz.string(), lastName: afz.string() }) ``` :::: ::: :: ### 元数据选项 元数据可以是字符串(仅错误消息)或对象: ::field-group :::field{name="type" type="string"} 控件类型,如 `'textarea'` , `'switch'` , `'slider'` 等。 ::: :::field{name="label" type="string"} 字段标签文本。 ::: :::field{name="description" type="string"} 字段描述文本。 ::: :::field{name="placeholder" type="string"} 输入框占位符。 ::: :::field{name="controlProps" type="object"} 传递给控件组件的 props。 ::: :::field{name="error" type="string"} 自定义错误消息。 ::::collapsible ```ts // 简化写法:仅错误消息 afz.string('这个字段是必填的') // 完整写法:带控件配置 afz.string({ type: 'textarea', label: '个人简介', placeholder: '请输入个人简介', controlProps: { rows: 4 } }) ``` :::: ::: :: ## Changelog :commit-changelog{prefix="/composables" suffix="ts"} # useApiFetch ## Usage 使用自动导入的 `useApiFetch` composable 进行 API 请求,基于 Nuxt [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch){rel=""nofollow""} 封装,提供自动认证、数据解包、业务状态码检查和统一的错误处理。 ```vue ``` - `useApiFetch` 继承 Nuxt [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch){rel=""nofollow""} 的所有功能,同时提供额外的 API 集成特性。 - 自动从 session 获取 token 并添加到请求头(通过 `$api` 实例)。 - 自动检查业务状态码并抛出错误。 - 内置 Toast 提示,支持自定义配置。 ::note `useApiFetch` 在服务端和客户端均可执行。如需仅在客户端执行请求,请使用 [`useClientApiFetch`](https://nuxt.mhaibaraai.cn/docs/composables/use-client-api-fetch)。 完整的 `useFetch` 选项参考: [Nuxt useFetch 文档](https://nuxt.com/docs/api/composables/use-fetch){rel=""nofollow""} :: ::tip --- icon: i-lucide-settings to: https://nuxt.mhaibaraai.cn/docs/getting-started/configuration#api --- 了解 API 系统的全局配置选项,包括端点、认证、Toast 等 :: ## 数据解包 `useApiFetch` 会自动解包 API 响应的 `data` 字段: ```ts // API 响应格式 { code: 200, message: 'Success', data: { id: 1, name: 'test' } // 这部分会被自动解包 } // 使用时直接声明 data 字段的类型 const { data } = await useApiFetch('/user') // data.value = { id: 1, name: 'test' } ``` 如果响应格式不同,可以使用 `skipBusinessCheck` 跳过解包,或使用 `transform` 自定义转换逻辑。 ## 业务状态码检查 默认情况下,`useApiFetch` 会检查业务状态码(通过模块配置的 `response` 选项): ```ts // 默认配置 { response: { codeKey: 'code', successCodes: [200, 0] } } ``` 当业务状态码不匹配时,会自动抛出错误并显示 Toast: ```ts // 业务状态码错误 { code: 400, message: '用户名已存在' } // 自动抛出错误,显示 Toast: "用户名已存在" ``` 可以通过 `skipBusinessCheck: true` 禁用此功能: ```ts const { data } = await useApiFetch('/users', { skipBusinessCheck: true }) ``` ## Toast 提示 内置 Toast 提示,支持请求级配置: ```ts // 禁用所有 Toast const { data } = await useApiFetch('/users', { toast: false }) // 仅显示错误提示 const { data } = await useApiFetch('/users', { toast: { success: false } }) // 使用快捷文本 const { data } = await useApiFetch('/users', { toast: { successMessage: '创建成功!', errorMessage: '创建失败,请重试' } }) // 使用完整的 Toast 属性 const { data } = await useApiFetch('/users', { toast: { success: { title: '创建成功', description: '用户已添加到系统', color: 'primary', icon: 'i-lucide-circle-check' }, error: { title: '创建失败', description: '请检查输入信息', color: 'danger', timeout: 5000 } } }) ``` ## 自定义 Hooks 支持自定义请求钩子,与内置钩子合并执行: ```ts const { data } = await useApiFetch('/users', { onRequest({ request, options }) { console.log('发送请求:', request) }, onRequestError({ request, error }) { console.error('请求错误:', error) }, onResponse({ response }) { console.log('收到响应:', response._data) }, onResponseError({ response }) { console.error('响应错误:', response.status) } }) ``` ::tip 用户钩子会在内置钩子之后执行,不会覆盖内置的认证和 Toast 逻辑。 :: ## API ### useApiFetch() `useApiFetch(url: string | (() => string), options?: UseApiFetchOptions): UseApiFetchReturn`{.shiki,shiki-themes,material-theme-lighter,material-theme,material-theme-palenight lang="ts-type"} 创建 API 请求。 #### Parameters ::field-group :::field --- required: true name: url type: string | (() => string) --- 请求 URL 或返回 URL 的函数。支持响应式 URL。 ::: :::field{name="options" type="UseApiFetchOptions"} 请求配置选项。支持所有 Nuxt `useFetch` 选项,以及额外的 API 集成选项。 ::::collapsible **API 集成选项** :::::field-group ::::::field{name="endpoint" type="string"} 使用指定的端点配置。默认使用 `$api` 的默认端点。 :::::: ::::::field{name="toast" type="RequestToastOptions | false"} Toast 提示配置。设为 `false` 禁用 Toast。 :::::::collapsible ::::::::field-group :::::::::field{name="success" type="Partial | false"} 成功提示配置。可以是完整的 Toast 属性对象,或 `false` 禁用成功提示。 ::::::::: :::::::::field{name="error" type="Partial | false"} 错误提示配置。可以是完整的 Toast 属性对象,或 `false` 禁用错误提示。 ::::::::: :::::::::field{name="successMessage" type="string"} 成功提示的快捷文本。等价于 `success: { title: '...' }` 。 ::::::::: :::::::::field{name="errorMessage" type="string"} 错误提示的快捷文本。等价于 `error: { title: '...' }` 。 ::::::::: :::::::: ::::::: :::::: ::::::field{name="skipBusinessCheck" type="boolean"} 跳过业务状态码检查。默认 `false` 。 :::::: ::::: **请求选项** (继承自 `useFetch`) :::::field-group ::::::field --- name: method type: "'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE'" --- HTTP 请求方法。默认 `'GET'` 。 :::::: ::::::field{name="query" type="Record"} URL 查询参数,会自动序列化到 URL。 :::::: ::::::field{name="params" type="Record"} `query` 的别名。 :::::: ::::::field{name="body" type="RequestInit['body'] | Record"} 请求体。对于 POST/PUT/PATCH 请求,会自动序列化为 JSON。 :::::: ::::::field --- name: headers type: "Record | [key: string, value: string][] | Headers" --- 请求头。 :::::: ::::::field{name="baseURL" type="string"} 基础 URL。通常通过 `endpoint` 选项配置,无需手动设置。 :::::: ::::: **响应处理** :::::field-group ::::::field{name="transform" type="(input: ResT) => DataT | Promise"} 转换响应数据的函数。接收解包后的 `data` 字段,返回最终数据。 :::::: ::::::field{name="pick" type="string[]"} 仅选择响应数据的指定字段。 :::::: ::::::field{name="default" type="() => DataT"} 设置默认值的工厂函数。 :::::: ::::: **执行选项** :::::field-group ::::::field{name="immediate" type="boolean"} 是否立即执行请求。设为 `false` 时需手动调用 `execute()` 。默认 `true` 。 :::::: ::::::field{name="lazy" type="boolean"} 是否使用 lazy 模式。lazy 模式下不会阻塞导航,初始 `pending` 为 `false` 。默认 `false` 。 :::::: ::::::field{name="server" type="boolean"} 是否在服务端执行请求。默认 `true` 。 :::::: ::::::field{name="watch" type="MultiWatchSources"} 监听的响应式源,变化时自动重新请求。可以是 ref、reactive 对象或数组。 :::::: ::::::field{name="deep" type="boolean"} 是否深度监听 `watch` 的对象。默认 `true` 。 :::::: ::::: **缓存与去重** :::::field-group ::::::field{name="key" type="string"} 自定义缓存键。用于多个请求之间共享数据或去重。 :::::: ::::::field{name="dedupe" type="'cancel' | 'defer'"} 重复请求的处理策略。默认 `cancel` - `'cancel'` - 取消待处理的请求 - `'defer'` - 不发起新请求 :::::: ::::::field{name="getCachedData" type="(key: string) => DataT"} 从缓存中获取数据的函数。返回值将用作初始 `data` 。 :::::: ::::: **请求钩子** :::::field-group ::::::field --- name: onRequest type: "(context: FetchContext) => Promise | void" --- 请求发送前的钩子。与内置钩子合并执行。 :::::::collapsible ```ts onRequest({ request, options }) { console.log('发送请求:', request.url) } ``` ::::::: :::::: ::::::field --- name: onRequestError type: "(context: FetchContext & { error: Error }) => Promise | void" --- 请求错误时的钩子。与内置钩子合并执行。 :::::::collapsible ```ts onRequestError({ request, error }) { console.error('请求错误:', error) } ``` ::::::: :::::: ::::::field --- name: onResponse type: "(context: FetchContext & { response: Response }) => Promise | void" --- 收到响应时的钩子。与内置钩子合并执行。 :::::::collapsible ```ts onResponse({ response }) { console.log('收到响应:', response.status) } ``` ::::::: :::::: ::::::field --- name: onResponseError type: "(context: FetchContext & { response: Response }) => Promise | void" --- 响应错误时的钩子(4xx/5xx)。与内置钩子合并执行。 :::::::collapsible ```ts onResponseError({ response }) { console.error('响应错误:', response.status) } ``` ::::::: :::::: ::::: :::: ::: :: #### Type Parameters ::field-group :::field{name="ResT" type="type"} API 响应中 `data` 字段的原始类型。 ::: :::field{name="DataT" type="type"} `transform` 转换后的最终类型。默认等于 `ResT` 。 ::: :: #### Returns 返回 [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch#return-values){rel=""nofollow""} 的响应对象,包含以下属性: ::field-group :::field{name="data" type="Ref"} 响应数据(已解包和转换)。初始值为 `null` ,请求成功后更新为实际数据。 ::: :::field{name="error" type="Ref"} 错误对象。包含网络错误、业务状态码错误等。 ::: :::field{name="status" type="Ref<'idle' | 'pending' | 'success' | 'error'>"} 请求状态。 - `'idle'` - 尚未开始(仅 `immediate: false` 时) - `'pending'` - 请求进行中 - `'success'` - 请求成功 - `'error'` - 请求失败 ::: :::field{name="pending" type="Ref"} 是否正在请求中。等价于 `status.value === 'pending'` 。 ::: :::field{name="refresh" type="(opts?: { dedupe?: boolean }) => Promise"} 刷新数据,重新执行请求。别名: `execute`。 ::::collapsible ```ts // 刷新数据 await refresh() // 跳过去重检查,强制刷新 await refresh({ dedupe: false }) ``` :::: ::: :::field{name="execute" type="(opts?: { dedupe?: boolean }) => Promise"} `refresh` 的别名。手动执行请求(常用于 `immediate: false` 时)。 ::: :::field{name="clear" type="() => void"} 清空状态。将 `data` 设为 `null` 、 `error` 设为 `null` 、 `status` 设为 `'idle'` 。 ::: :: ## Changelog :commit-changelog{prefix="/composables" suffix="ts"} # useClientApiFetch ## Usage 使用自动导入的 `useClientApiFetch` composable 进行仅客户端的 API 请求。这是 [`useApiFetch`](https://nuxt.mhaibaraai.cn/docs/composables/use-api-fetch) 的简化版本,预设了 `server: false` 和 `lazy: true`。 ```vue ``` - `useClientApiFetch` 继承 [`useApiFetch`](https://nuxt.mhaibaraai.cn/docs/composables/use-api-fetch) 的所有功能。 - 默认不自动执行,需要手动调用 `execute()` 触发请求。 - 仅在客户端执行,不参与服务端渲染。 ::warning 由于 `server: false` \+ `lazy: true` 的特性,数据获取会等到客户端 hydration 完成后才执行。即使在 ` ``` - `useApiAuth` 基于 `nuxt-auth-utils` 提供的 Session 管理功能。 - 支持自动获取用户信息、自定义 token 提取和 session 构建。 - 登录后 token 会自动添加到后续 API 请求的请求头。 ::note `useApiAuth` 依赖 `nuxt-auth-utils` 模块。确保已安装并正确配置该模块。 :: ::tip --- icon: i-lucide-settings to: https://nuxt.mhaibaraai.cn/docs/getting-started/configuration#auth --- 了解认证相关的全局配置选项 :: ## 登录流程 `login()` 方法执行以下步骤: 1. 调用登录接口,传递凭证 2. 从响应中提取 token(通过 `tokenExtractor`) 3. 如果提供 `userInfoPath`,使用 token 调用用户信息接口 4. 构建 session 数据(通过 `sessionBuilder`) 5. 设置 session 到服务端 6. 刷新客户端 session 状态 ```ts // 完整流程示例 await login({ // 1. 登录接口 loginPath: '/auth/login', credentials: { username: 'admin', password: '123456' }, // 2. 用户信息接口(可选) userInfoPath: '/auth/me', // 3. 自定义 token 提取 tokenExtractor: (res) => res.data?.accessToken, // 4. 自定义 session 构建 sessionBuilder: (user, token) => ({ user: { id: user.id, name: user.name }, secure: { token, permissions: user.permissions } }) }) ``` ## Token 提取 默认 `tokenExtractor` 会依次尝试以下路径: 1. `data.token` 2. `data.accessToken` 3. `token` ::tip 如果 API 响应格式不同,可以自定义提取逻辑: :: ```ts // 自定义 token 路径 await login({ loginPath: '/auth/login', credentials: { username: 'admin', password: '123456' }, tokenExtractor: (res) => res.data?.jwt // 从 data.jwt 提取 }) // 复杂嵌套结构 await login({ loginPath: '/auth/login', credentials: { username: 'admin', password: '123456' }, tokenExtractor: (res) => res.data?.session?.accessToken }) ``` ## Session 构建 默认 `sessionBuilder` 构建以下 session 结构: ```ts { user: User, // 用户信息 token: string, // 访问令牌 loggedInAt: string // 登录时间(ISO 格式) } ``` ::tip 可以自定义 session 结构,例如分离敏感数据: :: ```ts await login({ loginPath: '/auth/login', credentials: { username: 'admin', password: '123456' }, sessionBuilder: (user, token) => ({ user: { id: user.id, name: user.name, avatar: user.avatar }, secure: { token, refreshToken: user.refreshToken, permissions: user.permissions } }) }) ``` ::note `nuxt-auth-utils` 会自动加密 `secure` 字段,保护敏感数据。 :: ## Session 配置 通过 `sessionConfig` 选项可以自定义 Session 的行为,包括过期时间、Cookie 配置等。 ### 默认配置 ```ts [nuxt.config.ts] { name: useSiteConfig().name, password: process.env.NUXT_SESSION_PASSWORD || '', cookie: { sameSite: 'lax' } } ``` ::warning 生产环境务必设置 `NUXT_SESSION_PASSWORD` 环境变量,用于加密 Session 数据。建议使用至少 32 位的随机字符串。 :: ::tip --- icon: i-lucide-link to: https://github.com/atinux/nuxt-auth-utils?tab=readme-ov-file#configuration --- 了解更多 Session 配置选项,请参考 nuxt-auth-utils 文档 :: ## 用户信息获取 有两种方式获取用户信息: ### 方式 1: 从登录响应获取 ```ts // 登录响应包含用户信息 await login({ loginPath: '/auth/login', credentials: { username: 'admin', password: '123456' } }) // 用户信息从登录响应的 data 字段提取 ``` 登录响应格式示例: ```json { "code": 200, "message": "登录成功", "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "id": 1, "name": "Admin", "email": "admin@example.com" } } ``` ### 方式 2: 调用独立的用户信息接口 ```ts // 使用 userInfoPath 自动调用用户信息接口 await login({ loginPath: '/auth/login', credentials: { username: 'admin', password: '123456' }, userInfoPath: '/auth/me' // 使用 token 调用此接口 }) ``` 此方式会自动: 1. 从登录响应提取 token 2. 使用 token 调用 `/auth/me` 3. 将用户信息接口的响应作为用户数据 ## API ### useApiAuth() `useApiAuth(): UseApiAuthReturn`{.shiki,shiki-themes,material-theme-lighter,material-theme,material-theme-palenight lang="ts-type"} 创建认证管理器。 #### Returns 返回包含以下方法和属性的认证管理器对象: ::field-group :::field --- name: login() type: "(options: LoginOptions) => Promise" --- 执行登录流程。 ::::collapsible **Parameters** :::::field-group ::::::field --- required: true name: loginPath type: string --- 登录接口路径。 :::::: ::::::field --- required: true name: credentials type: Record --- 登录凭证(如用户名、密码)。 :::::: ::::::field{name="userInfoPath" type="string"} 用户信息接口路径。如果提供,登录后会使用 token 调用此接口获取用户信息。 :::::: ::::::field --- name: tokenExtractor type: "(response: ApiResponse) => string | null | undefined" --- 自定义 token 提取函数。默认从 `data.token` 、 `data.accessToken` 或 `token` 字段提取。 :::::: ::::::field --- name: sessionBuilder type: "(user: User, token: string) => UserSession" --- 自定义 session 构建函数。默认返回 `{ user, token, loggedInAt }` 。 :::::: ::::::field{name="sessionConfig" type="Partial"} Session 配置选项,用于自定义 Session 的行为。 - [查看 nuxt-auth-utils 文档](https://github.com/atinux/nuxt-auth-utils?tab=readme-ov-file#configuration){rel=""nofollow""} - 默认配置:`{ name: useSiteConfig().name, password: process.env.NUXT_SESSION_PASSWORD, cookie: { sameSite: 'lax' } }` :::::: ::::::field{name="endpoint" type="string"} 使用指定的端点配置。 :::::: ::::: **Returns** 返回 `Promise`,包含: - `user: User` - 用户信息 - `token: string` - 访问令牌 :::: ::: :::field{name="loggedIn" type="Ref"} 是否已登录(响应式)。 ::: :::field{name="user" type="Ref"} 当前用户信息(响应式)。 ::: :::field{name="session" type="Ref"} 当前 session 数据(响应式)。 ::: :::field{name="fetch()" type="() => Promise"} 刷新 session 状态。 ::: :::field{name="clear()" type="() => Promise"} 清除 session(登出)。 ::: :: ## Changelog :commit-changelog{prefix="/composables" suffix="ts"} # useUploadWithProgress ## Usage 使用自动导入的 `useUploadWithProgress` composable 进行文件上传,基于原生 `XMLHttpRequest` 实现,支持实时进度监控和取消上传。 ```vue ``` - `useUploadWithProgress` 基于原生 `XMLHttpRequest` 实现,支持上传进度监听。 - 支持自动认证(从 session 获取 token)。 - 自动检查业务状态码。 - 内置 Toast 提示。 ::note 适用于需要展示上传进度的场景,如图片上传、文件管理等。对于无需进度的小文件,可以直接使用 `$api.$fetch` 。 :: ::tip --- icon: i-lucide-settings to: https://nuxt.mhaibaraai.cn/docs/getting-started/configuration#api --- 了解 API 系统的全局配置选项 :: ## 单文件 vs 多文件上传 `upload()` 方法支持单个文件和多个文件: ```ts // 单文件上传 await upload('/api/upload', file, { fieldName: 'file' }) // FormData: file= // 多文件上传 await upload('/api/upload', [file1, file2, file3], { fieldName: 'files' }) // FormData: files=, files=, files= ``` ## 额外表单字段 可以附加额外的表单字段: ```ts await upload('/api/upload', file, { fieldName: 'avatar', fields: { userId: '123', category: 'profile', description: '用户头像' } }) // FormData: // avatar= // userId=123 // category=profile // description=用户头像 ``` ## 业务状态码检查 `useUploadWithProgress` 会自动检查业务状态码: ```ts // 成功响应 { code: 200, message: '上传成功', data: { url: 'https://cdn.example.com/file.jpg' } } // 触发 onSuccess 回调,显示成功 Toast // 失败响应 { code: 400, message: '文件类型不支持' } // 触发 onError 回调,显示错误 Toast ``` 业务状态码规则由模块配置的 `response` 选项控制。 ## 取消上传 使用 `abort()` 方法取消正在进行的上传: ```ts const { uploading, upload, abort } = useUploadWithProgress() // 开始上传 upload('/api/upload', largeFile) // 用户点击取消 const handleCancel = () => { if (uploading.value) { abort() // 中止上传 } } ``` 取消上传后: - `uploading` 变为 `false` - `progress` 重置为 `0` - `error` 设为 `new Error('上传已取消')` - 不会显示 Toast 提示 ## 认证集成 `useUploadWithProgress` 会自动从 session 获取 token 并添加到请求头: ```ts // 自动添加 Authorization 头 await upload('/api/upload', file) // 请求头: Authorization: Bearer ``` 如需额外的请求头: ```ts await upload('/api/upload', file, { headers: { 'X-Custom-Header': 'value' } }) ``` ## API ### useUploadWithProgress() `useUploadWithProgress(): { progress, uploading, data, error, upload, abort }`{.shiki,shiki-themes,material-theme-lighter,material-theme,material-theme-palenight lang="ts-type"} 创建上传管理器。 #### Type Parameters ::field-group :::field{name="T" type="type"} 服务器响应 `data` 字段的类型。 ::: :: #### Returns 返回包含以下方法和属性的上传管理器对象: ::field-group :::field{name="progress" type="Ref"} 上传进度,范围 0-100。 ::: :::field{name="uploading" type="Ref"} 是否正在上传中。 ::: :::field{name="data" type="Ref | null>"} 服务器响应数据。 ::: :::field{name="error" type="Ref"} 错误信息(如果上传失败)。 ::: :::field --- name: upload() type: "(url: string, files: File | File[], options?: UploadWithProgressOptions) => Promise<{ data: ApiResponse | null, error: Error | null }>" --- 执行上传。 ::::collapsible **Parameters** :::::field-group ::::::field --- required: true name: url type: string --- 上传文件的 URL。 :::::: ::::::field --- required: true name: files type: File | File[] --- 要上传的文件或文件数组。 :::::: ::::::field{name="options" type="UploadWithProgressOptions"} 上传配置选项。 :::::::collapsible ::::::::field-group :::::::::field{name="fieldName" type="string"} 文件字段名。默认 `'file'` 。 ::::::::: :::::::::field{name="fields" type="Record"} 额外的表单字段。 ::::::::: :::::::::field{name="headers" type="Record"} 额外的请求头。 ::::::::: :::::::::field{name="toast" type="RequestToastOptions | false"} Toast 提示配置。设为 `false` 禁用 Toast。 ::::::::: :::::::::field{name="endpoint" type="string"} 使用指定的端点配置。 ::::::::: :::::::::field{name="onSuccess" type="(response: ApiResponse) => void"} 上传成功回调,接收服务器响应。 ::::::::: :::::::::field{name="onError" type="(error: Error) => void"} 上传失败回调,接收错误对象。 ::::::::: :::::::: ::::::: :::::: ::::: **Returns** 返回 `Promise<{ data: ApiResponse | null, error: Error | null }>`: - `data` - 服务器响应数据(成功时) - `error` - 错误信息(失败时) :::: ::: :::field{name="abort()" type="() => void"} 中止当前上传,重置进度和状态。 ::: :: ## Changelog :commit-changelog{prefix="/composables" suffix="ts"} # useDownloadWithProgress ## Usage 使用自动导入的 `useDownloadWithProgress` composable 进行文件下载,基于原生 `fetch` 和 `ReadableStream` 实现,支持实时进度监控和取消下载。 ```vue ``` - `useDownloadWithProgress` 基于原生 API 实现,不依赖第三方库。 - 支持自动认证(从 session 获取 token)。 - 自动提取文件名(从响应头或 URL)。 - 内置 Toast 提示。 ::note 适用于需要展示下载进度的场景,如大文件导出、批量下载等。对于无需进度的小文件,可以直接使用 `$api.$fetch` 。 :: ::tip --- icon: i-lucide-settings to: https://nuxt.mhaibaraai.cn/docs/getting-started/configuration#api --- 了解 API 系统的全局配置选项 :: ## 文件名提取 `useDownloadWithProgress` 会自动提取文件名,优先级如下: 1. 用户提供的 `filename` 选项 2. 响应头 `Content-Disposition` 中的文件名 3. URL 最后一部分(fallback) ```ts // 1. 自定义文件名 await download('/api/export', { filename: 'custom-report.pdf' }) // 保存为: custom-report.pdf // 2. 从响应头提取 // 响应头: Content-Disposition: attachment; filename="report-2024.pdf" await download('/api/export') // 保存为: report-2024.pdf // 3. 从 URL 提取 await download('/api/export/monthly-report.pdf') // 保存为: monthly-report.pdf ``` ## 进度计算 进度基于响应头的 `Content-Length`: ```ts // 如果响应包含 Content-Length // 进度 = (已下载字节 / 总字节) * 100 // 如果响应不包含 Content-Length // 进度保持为 0,但下载仍然正常进行 ``` 某些服务器可能不返回 `Content-Length`,此时无法计算准确进度,但下载功能不受影响。 ## 取消下载 使用 `abort()` 方法取消正在进行的下载: ```ts const { downloading, download, abort } = useDownloadWithProgress() // 开始下载 download('/api/export/large-file.zip') // 用户点击取消 const handleCancel = () => { if (downloading.value) { abort() // 中止下载 } } ``` 取消下载后: - `downloading` 变为 `false` - `progress` 重置为 `0` - 不会触发 `onError` 回调 - 不会显示 Toast 提示 ## 认证集成 `useDownloadWithProgress` 会自动从 session 获取 token 并添加到请求头: ```ts // 自动添加 Authorization 头 await download('/api/export/private-report.pdf') // 请求头: Authorization: Bearer ``` 如需额外的请求头: ```ts await download('/api/export', { headers: { 'X-Custom-Header': 'value' } }) ``` ## API ### useDownloadWithProgress() `useDownloadWithProgress(): { progress, downloading, error, download, abort }`{.shiki,shiki-themes,material-theme-lighter,material-theme,material-theme-palenight lang="ts-type"} 创建下载管理器。 #### Returns 返回包含以下方法和属性的下载管理器对象: ::field-group :::field{name="progress" type="Ref"} 下载进度,范围 0-100。 ::: :::field{name="downloading" type="Ref"} 是否正在下载中。 ::: :::field{name="error" type="Ref"} 错误信息(如果下载失败)。 ::: :::field --- name: download() type: "(url: string, options?: DownloadWithProgressOptions) => Promise<{ success: boolean, error: Error | null }>" --- 执行下载。 ::::collapsible **Parameters** :::::field-group ::::::field --- required: true name: url type: string --- 下载文件的 URL。 :::::: ::::::field{name="options" type="DownloadWithProgressOptions"} 下载配置选项。 :::::::collapsible ::::::::field-group :::::::::field{name="filename" type="string"} 自定义文件名。不提供时从响应头的 `Content-Disposition` 或 URL 中提取。 ::::::::: :::::::::field{name="headers" type="Record"} 额外的请求头。 ::::::::: :::::::::field{name="toast" type="RequestToastOptions | false"} Toast 提示配置。设为 `false` 禁用 Toast。 ::::::::: :::::::::field{name="endpoint" type="string"} 使用指定的端点配置。 ::::::::: :::::::::field{name="onSuccess" type="(filename: string) => void"} 下载成功回调,接收最终的文件名。 ::::::::: :::::::::field{name="onError" type="(error: Error) => void"} 下载失败回调,接收错误对象。 ::::::::: :::::::: ::::::: :::::: ::::: **Returns** 返回 `Promise<{ success: boolean, error: Error | null }>`: - `success` - 下载是否成功 - `error` - 错误信息(成功时为 `null`) :::: ::: :::field{name="abort()" type="() => void"} 中止当前下载,重置进度和状态。 ::: :: ## Changelog :commit-changelog{prefix="/composables" suffix="ts"} # useDateFormatter ## Usage 使用自动导入的 `useDateFormatter` composable 进行日期格式化、转换和操作,基于 `@internationalized/date` 库实现国际化日期处理。 ```vue ``` - `useDateFormatter` 基于 `Intl.DateTimeFormat` 提供本地化日期格式化。 - 支持单个日期、日期范围、日期数组以及嵌套对象的批量转换。 ::note 所有格式化和转换方法都内置了错误处理,当传入 `null` 或 `undefined` 时会返回空字符串或 `null` ,避免抛出异常。 :: ## 时区处理 默认情况下,`useDateFormatter` 使用本地时区。如需指定时区,使用 `timeZone` 参数: ```ts const formatter = useDateFormatter({ timeZone: 'Asia/Shanghai' }) // 或使用其他时区 const formatterNY = useDateFormatter({ timeZone: 'America/New_York' }) ``` 时区标识符遵循 IANA 时区数据库规范。常用时区包括: - `'Asia/Shanghai'` - 中国标准时间 - `'America/New_York'` - 美国东部时间 - `'Europe/London'` - 英国时间 - `'Asia/Tokyo'` - 日本标准时间 ## 类型安全 `useDateFormatter` 提供了类型守卫函数用于运行时类型检查: ```ts const formatter = useDateFormatter() // 检查是否为 DateValue if (formatter.isDateValue(someValue)) { // TypeScript 自动推断 someValue 为 DateValue 类型 const formatted = formatter.format(someValue) } // 检查是否为 DateRange if (formatter.isDateRange(someValue)) { // TypeScript 自动推断 someValue 为 DateRange 类型 const { start, end } = someValue } ``` ## 与 AutoForm 集成 在 AutoForm 中使用日期字段时,建议使用 `convertToISO()` 进行批量转换后提交: ```vue ``` ::note 这种方式可以: - 自动处理所有嵌套的日期字段 - 保持对象结构不变 - 避免手动遍历转换 :: ## API ### useDateFormatter() `useDateFormatter(options?: DateFormatterOptions): DateFormatter`{.shiki,shiki-themes,material-theme-lighter,material-theme,material-theme-palenight lang="ts-type"} 创建日期格式化器实例。 #### Parameters ::field-group :::field{name="options" type="DateFormatterOptions"} 日期格式化配置选项。 ::::collapsible :::::field-group ::::::field{name="locale" type="string"} 语言区域,默认 `'zh-CN'` 。 :::::: ::::::field{name="formatOptions" type="Intl.DateTimeFormatOptions"} 日期格式化选项,遵循 [Intl.DateTimeFormat](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat){rel=""nofollow""} 规范。默认为 `{ dateStyle: 'medium' }` 。 :::::: ::::::field{name="timeZone" type="string"} 时区标识符(如 `'Asia/Shanghai'` , `'America/New_York'` ),默认使用本地时区。详见 [@internationalized/date 时区文档](https://react-spectrum.adobe.com/internationalized/date/index.html#timezones){rel=""nofollow""} 。 :::::: ::::: :::: ::: :: #### Returns 返回包含以下方法和属性的日期格式化器对象。 ### 格式化方法 ::field-group :::field{name="format()" type="(date: DateValue | undefined | null) => string"} 格式化单个日期为本地化字符串。 ::::collapsible ```ts formatter.format(date) // "2025年11月29日" ``` :::: ::: :::field --- name: formatRange() type: "(start: DateValue | undefined | null, end: DateValue | undefined | null, separator?: string) => string" --- 格式化日期范围,默认分隔符为 `' - '`。 ::::collapsible ```ts formatter.formatRange(startDate, endDate) // "2025年11月1日 - 2025年11月30日" formatter.formatRange(startDate, endDate, ' 至 ') // "2025年11月1日 至 2025年11月30日" ``` :::: ::: :::field --- name: formatArray() type: "(dates: DateValue[] | undefined | null, separator?: string) => string" --- 格式化日期数组,默认分隔符为 `', '`。 ::::collapsible ```ts formatter.formatArray([date1, date2, date3]) // "2025年11月1日, 2025年11月15日, 2025年11月29日" ``` :::: ::: :: ### 转换方法 ::field-group :::field{name="toISO()" type="(date: DateValue | undefined | null) => string"} 转换为 ISO 8601 字符串。 ::::collapsible ```ts formatter.toISO(date) // "2025-11-29" ``` :::: ::: :::field --- name: toDate() type: "(date: DateValue | undefined | null) => Date | null" --- 转换为 JavaScript Date 对象。 ::: :::field --- name: toTimestamp() type: "(date: DateValue | undefined | null) => number | null" --- 转换为时间戳(毫秒)。 ::::collapsible ```ts formatter.toTimestamp(date) // 1732838400000 ``` :::: ::: :::field --- name: toUnixTimestamp() type: "(date: DateValue | undefined | null) => number | null" --- 转换为 Unix 时间戳(秒)。 ::::collapsible ```ts formatter.toUnixTimestamp(date) // 1732838400 ``` :::: ::: :::field{name="parse()" type="(value: string) => DateValue | null"} 解析 ISO 8601 日期字符串。自动识别 `CalendarDate`、`CalendarDateTime` 或 `ZonedDateTime`。 ::::collapsible ```ts formatter.parse('2025-11-29') // CalendarDate formatter.parse('2025-11-29T10:30:00') // CalendarDateTime formatter.parse('2025-11-29T10:30:00[Asia/Shanghai]') // ZonedDateTime ``` :::: ::: :: ### 工具方法 - 获取日期 ::field-group :::field{name="getToday()" type="() => CalendarDate"} 获取今天的日期。 ::: :::field{name="getNow()" type="() => ZonedDateTime"} 获取当前日期时间。 ::: :::field{name="getStartOfWeek()" type="(date: DateValue) => DateValue"} 获取一周的开始日期(基于当前语言区域)。 ::: :::field{name="getEndOfWeek()" type="(date: DateValue) => DateValue"} 获取一周的结束日期(基于当前语言区域)。 ::: :::field{name="getStartOfMonth()" type="(date: DateValue) => DateValue"} 获取一月的开始日期。 ::: :::field{name="getEndOfMonth()" type="(date: DateValue) => DateValue"} 获取一月的结束日期。 ::: :::field{name="getStartOfYear()" type="(date: DateValue) => DateValue"} 获取一年的开始日期。 ::: :::field{name="getEndOfYear()" type="(date: DateValue) => DateValue"} 获取一年的结束日期。 ::: :: ### 工具方法 - 查询 ::field-group :::field{name="getDayOfWeek()" type="(date: DateValue) => number"} 获取星期几数字(0-6,0 为本地化的一周第一天)。 ::: :::field --- name: getDayOfWeekName() type: "(date: DateValue, style?: 'narrow' | 'short' | 'long') => string" --- 获取本地化的星期几名称。 ::::collapsible **Parameters** :::::field-group ::::::field --- required: true name: date type: DateValue --- 日期对象。 :::::: ::::::field{name="style" type="'narrow' | 'short' | 'long'"} 格式样式。 默认 `long` - `'narrow'` - 最短格式,如「一」 - `'short'` - 短格式,如「周一」 - `'long'` - 完整格式,如「星期一」 :::::: ::::: ```ts const date = formatter.getToday() formatter.getDayOfWeekName(date, 'narrow') // "一" formatter.getDayOfWeekName(date, 'short') // "周一" formatter.getDayOfWeekName(date, 'long') // "星期一" formatter.getDayOfWeekName(date) // "星期一" (默认) ``` :::: ::: :::field{name="getWeeksInMonth()" type="(date: DateValue) => number"} 获取本月的周数。 ::: :::field{name="isWeekday()" type="(date: DateValue) => boolean"} 判断是否为工作日。 ::: :::field{name="isWeekend()" type="(date: DateValue) => boolean"} 判断是否为周末。 ::: :::field{name="isSameDay()" type="(a: DateValue, b: DateValue) => boolean"} 判断是否为同一天。 ::: :::field{name="isSameMonth()" type="(a: DateValue, b: DateValue) => boolean"} 判断是否为同一月。 ::: :::field{name="isSameYear()" type="(a: DateValue, b: DateValue) => boolean"} 判断是否为同一年。 ::: :::field{name="isToday()" type="(date: DateValue) => boolean"} 判断是否为今天。 ::: :::field{name="isDateValue()" type="(value: unknown) => value is DateValue"} 类型守卫,判断是否为 `DateValue` 类型。 ::: :::field{name="isDateRange()" type="(value: unknown) => value is DateRange"} 类型守卫,判断是否为 `DateRange` 类型。 ::: :: ### 批量转换方法 ::field-group :::field --- name: convertData() type: "(data: T, converter: (value: DateValue) => any) => T" --- 通用数据转换函数,自动处理单个日期、日期范围、日期数组以及嵌套对象。 ::::collapsible ```ts const converted = formatter.convertData(complexData, formatter.toISO) ``` :::: ::: :::field{name="convertToISO()" type="(data: T) => T"} 批量转换为 ISO 字符串。支持单个日期、日期范围、数组和嵌套对象。 ::::collapsible ```ts // 单个日期 formatter.convertToISO(date) // "2025-11-29" // 日期范围 formatter.convertToISO({ start: date1, end: date2 }) // { start: "2025-11-01", end: "2025-11-30" } // 日期数组 formatter.convertToISO([date1, date2]) // ["2025-11-01", "2025-11-15"] // 嵌套对象 formatter.convertToISO({ createdAt: date1, range: { start: date2, end: date3 } }) // { // createdAt: "2025-11-01", // range: { start: "2025-11-20", end: "2025-11-29" } // } ``` :::: ::: :::field{name="convertToFormatted()" type="(data: T) => T"} 批量转换为格式化字符串。 ::: :::field{name="convertToDate()" type="(data: T) => T"} 批量转换为 Date 对象。 ::: :: ### 配置属性 ::field-group :::field{name="locale" type="string"} 当前使用的语言区域。 ::: :::field{name="timeZone" type="string"} 当前使用的时区。 ::: :: ## Changelog :commit-changelog{prefix="/composables" suffix="ts"} # Auto Form ::note{to="https://nuxt.mhaibaraai.cn/docs/auto-form"} 阅读文档了解更多 Auto Form 详情。 :: ::sandbox --- branch: main dir: examples/auto-form file: app.vue repo: mhaibaraai/movk-examples --- ::