allinssl/frontend/packages/vue/naive-ui
chudong eb6172436c 【新增】在翻译文件中添加多吉云AccessKey和SecretKey的相关翻译,更新阿里云ESA的配置接口,优化CA管理功能,增强表单验证逻辑,提升用户体验。 2025-06-20 10:07:56 +08:00
..
docs 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
src 【新增】在翻译文件中添加多吉云AccessKey和SecretKey的相关翻译,更新阿里云ESA的配置接口,优化CA管理功能,增强表单验证逻辑,提升用户体验。 2025-06-20 10:07:56 +08:00
types 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
README.md 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
eslint.config.js 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
index.html 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
package.json 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
postcss.config.js 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
prettier.config.js 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
tailwind.config.js 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
tsconfig.json 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
tsconfig.node.json 【同步】前端项目源码 2025-05-10 11:53:11 +08:00
vite.config.ts 【同步】前端项目源码 2025-05-10 11:53:11 +08:00

README.md

Dynamic Components

基于 Vue 3 和 Naive UI 的动态表单和表格组件库,提供灵活的配置式 UI 开发方案。

目录

安装

# 使用 npm
npm install dynamic-components

# 使用 yarn
yarn add dynamic-components

# 使用 pnpm
pnpm add dynamic-components

快速开始

基础依赖

本组件库基于 Vue 3 和 Naive UI 构建,请确保您的项目中已安装以下依赖:

{
	"dependencies": {
		"@vicons/tabler": "^0.13.0",
		"@vueuse/core": "^10.7.0",
		"naive-ui": "^2.37.3",
		"vue": "^3.4.0"
	}
}

基本用法

<template>
	<div>
		<component :is="form.FormComponent" />
		<n-button @click="handleSubmit">提交</n-button>
	</div>
</template>

<script setup>
	import { useForm } from '@baota/naive-ui/hooks'
	import { NButton } from 'naive-ui'

	// 定义表单配置
	const formConfig = [
		{
			type: 'formItem',
			label: '用户名',
			children: [
				{
					type: 'input',
					field: 'username',
					placeholder: '请输入用户名',
				},
			],
		},
		{
			type: 'formItem',
			label: '密码',
			children: [
				{
					type: 'input',
					field: 'password',
					type: 'password',
					placeholder: '请输入密码',
				},
			],
		},
	]

	// 创建表单实例
	const form = useForm({
		config: formConfig,
		defaultValues: {
			username: '',
			password: '',
		},
	})

	// 提交表单
	const handleSubmit = async () => {
		const valid = await form.validate()
		if (valid) {
			console.log('表单数据:', form.formData.value)
		}
	}
</script>

核心功能

  • 配置式表单:通过 JSON 配置快速构建复杂表单
  • 动态表格:支持自定义列、排序、筛选等功能
  • 表单设计器:可视化拖拽设计表单
  • 标签页管理:简化多标签页应用开发

组件和 Hooks

useForm

useForm 是一个强大的表单 Hook用于创建动态表单。

基本用法

import { useForm } from '@baota/naive-ui/hooks'

const form = useForm({
	config: [
		{
			type: 'formItem',
			label: '姓名',
			children: [
				{
					type: 'input',
					field: 'name',
				},
			],
		},
	],
	defaultValues: {
		name: '',
	},
	requestFn: async (data) => {
		// 提交表单数据的请求函数
		return await api.submitForm(data)
	},
})

参数说明

参数 类型 必填 说明
config FormConfig 表单配置
defaultValues Record<string, any> 表单默认值
requestFn (data: any) => Promise 表单提交请求函数
immediate boolean 是否立即执行请求,默认为 false

返回值

属性 类型 说明
loading Ref 加载状态
formData Ref<Record<string, any>> 表单数据
formRef Ref<FormInst | null> 表单实例引用
submit () => Promise 提交方法
reset () => void 重置方法
validate () => Promise 验证方法
FormComponent () => JSX.Element 表单渲染组件

支持的表单元素

类型 说明 对应 Naive UI 组件
input 输入框 NInput
inputNumber 数字输入框 NInputNumber
inputGroup 输入框组 NInputGroup
select 选择器 NSelect
radio 单选框组 NRadioGroup
radioButton 单选按钮 NRadioButton
checkbox 复选框组 NCheckboxGroup
switch 开关 NSwitch
datepicker 日期选择器 NDatePicker
timepicker 时间选择器 NTimePicker
colorPicker 颜色选择器 NColorPicker
slider 滑块 NSlider
rate 评分 NRate
transfer 穿梭框 NTransfer
mention 提及 NMention
dynamicInput 动态输入 NDynamicInput
dynamicTags 动态标签 NDynamicTags
autoComplete 自动完成 NAutoComplete
cascader 级联选择 NCascader
treeSelect 树选择 NTreeSelect
upload 上传 NUpload
uploadDragger 拖拽上传 NUploadDragger
slot 插槽 -
render 自定义渲染 -

栅格布局

const formConfig = [
	{
		type: 'grid',
		cols: 24,
		xGap: 12,
		children: [
			{
				type: 'formItemGi',
				label: '姓名',
				span: 12,
				children: [
					{
						type: 'input',
						field: 'firstName',
					},
				],
			},
			{
				type: 'formItemGi',
				label: '姓氏',
				span: 12,
				children: [
					{
						type: 'input',
						field: 'lastName',
					},
				],
			},
		],
	},
]

自定义插槽

<template>
	<component :is="form.FormComponent">{{
    customSlot,
	}}</component>
</template>

<script setup>
	import { useForm } from '@baota/naive-ui/hooks'

	const formConfig = [
		{
			type: 'formItem',
			label: '自定义内容',
			children: [
				{
					type: 'slot',
					slot: 'customSlot',
				},
			],
		},
	]

	const formSlots = {
		customSlot: (formData, formRef) => <div>这是自定义插槽内容</div>,
	}

	const form = useForm({
		config: formConfig,
	})
</script>

useTable

useTable 是一个用于创建动态表格的 Hook。

基本用法

import { useTable } from '@baota/naive-ui/hooks'
import { h } from 'vue'

// 定义表格列
const columns = [
	{
		title: 'ID',
		key: 'id',
	},
	{
		title: '姓名',
		key: 'name',
	},
	{
		title: '操作',
		key: 'actions',
		render: (row) =>
			h('div', [
				h('button', { onClick: () => handleEdit(row) }, '编辑'),
				h('button', { onClick: () => handleDelete(row) }, '删除'),
			]),
	},
]

// 创建表格实例
const table = useTable({
	columns,
	requestFn: async (params) => {
		// 获取表格数据的请求函数
		const res = await api.getList(params)
		return {
			list: res.data.list,
		}
	},
	defaultParams: {
		page: 1,
		pageSize: 10,
	},
})

参数说明

参数 类型 必填 说明
columns DataTableColumns 表格列配置
requestFn (params: TableRequestParams) => Promise 数据请求函数
defaultParams Ref 默认请求参数
immediate boolean 是否立即执行请求,默认为 true

返回值

属性 类型 说明
loading Ref 加载状态
data Ref<T[]> 表格数据
params TableRequestParams 查询参数
refresh () => Promise 刷新方法
reset () => Promise 重置方法
tableRef Ref 表格引用
TableComponent () => JSX.Element 表格渲染组件

useTabs

useTabs 是一个用于管理标签页的 Hook特别适合基于路由的多标签页应用。

基本用法

<template>
	<component :is="tabs.TabsComponent" />
</template>

<script setup>
	import { useTabs } from '@baota/naive-ui/hooks'

	const tabs = useTabs({
		defaultToFirst: true,
	})
</script>

参数说明

参数 类型 必填 说明
defaultToFirst boolean 是否在初始化时自动选中第一个标签,默认为 true

返回值

属性 类型 说明
activeKey string 当前激活的标签值
childRoutes RouteRecordRaw[] 子路由列表
handleTabChange (key: string) => void 切换标签页方法
TabsComponent () => JSX.Element 标签页渲染组件

FormDesigner

FormDesigner 是一个可视化表单设计器组件,支持拖拽设计表单。

基本用法

<template>
	<FormDesigner />
</template>

<script setup>
	import { FormDesigner } from '@baota/naive-ui/hooks'
</script>

功能特点

  • 支持拖拽添加表单组件
  • 支持编辑组件属性
  • 支持导入/导出表单配置
  • 实时预览表单效果

FormBuilder

FormBuilder 是一个简化版的表单构建器组件,适合快速创建简单表单。

基本用法

<template>
	<FormBuilder />
</template>

<script setup>
	import { FormBuilder } from '@baota/naive-ui/hooks'
</script>

功能特点

  • 拖拽式表单构建
  • 支持导出表单配置
  • 简单直观的操作界面

类型定义

组件库提供了完整的 TypeScript 类型定义,可以在开发时获得良好的类型提示。

import type {
	FormConfig,
	FormItemConfig,
	FormElement,
	UseFormOptions,
	FormInstance,
	TableRequestParams,
	TableResponse,
	UseTableOptions,
	TableInstance,
} from '@baota/naive-ui/hooks'

示例

完整的表单示例

<template>
	<div>
		<component :is="form.FormComponent" />
		<n-button type="primary" @click="handleSubmit" :loading="form.loading.value"> 提交 </n-button>
	</div>
</template>

<script setup>
	import { useForm } from '@baota/naive-ui/hooks'
	import { NButton } from 'naive-ui'
	import { ref } from 'vue'

	// 表单配置
	const formConfig = [
		{
			type: 'grid',
			cols: 24,
			xGap: 12,
			children: [
				{
					type: 'formItemGi',
					label: '姓名',
					span: 12,
					required: true,
					children: [
						{
							type: 'input',
							field: 'name',
							placeholder: '请输入姓名',
						},
					],
				},
				{
					type: 'formItemGi',
					label: '年龄',
					span: 12,
					children: [
						{
							type: 'inputNumber',
							field: 'age',
							placeholder: '请输入年龄',
							min: 0,
							max: 120,
						},
					],
				},
			],
		},
		{
			type: 'formItem',
			label: '性别',
			required: true,
			children: [
				{
					type: 'radio',
					field: 'gender',
					options: [
						{ label: '男', value: 'male' },
						{ label: '女', value: 'female' },
						{ label: '其他', value: 'other' },
					],
				},
			],
		},
		{
			type: 'formItem',
			label: '兴趣爱好',
			children: [
				{
					type: 'checkbox',
					field: 'hobbies',
					options: [
						{ label: '阅读', value: 'reading' },
						{ label: '音乐', value: 'music' },
						{ label: '运动', value: 'sports' },
						{ label: '旅行', value: 'travel' },
						{ label: '编程', value: 'coding' },
					],
				},
			],
		},
		{
			type: 'formItem',
			label: '出生日期',
			children: [
				{
					type: 'datepicker',
					field: 'birthday',
					type: 'date',
				},
			],
		},
		{
			type: 'formItem',
			label: '个人简介',
			children: [
				{
					type: 'input',
					field: 'bio',
					type: 'textarea',
					placeholder: '请输入个人简介',
					maxLength: 500,
					showCount: true,
				},
			],
		},
	]

	// 默认值
	const defaultValues = ref({
		name: '',
		age: 18,
		gender: 'male',
		hobbies: [],
		birthday: null,
		bio: '',
	})

	// 创建表单实例
	const form = useForm({
		config: formConfig,
		defaultValues,
		requestFn: async (data) => {
			// 模拟提交请求
			return new Promise((resolve) => {
				setTimeout(() => {
					console.log('提交的数据:', data)
					resolve({ success: true })
				}, 1000)
			})
		},
	})

	// 提交表单
	const handleSubmit = async () => {
		const result = await form.submit()
		if (result?.success) {
			// 提交成功后的处理
			console.log('提交成功')
		}
	}
</script>

完整的表格示例

<template>
	<div>
		<n-card title="用户列表">
			<!-- 搜索表单 -->
			<component :is="searchForm.FormComponent" />
			<div class="mb-4">
				<n-button type="primary" @click="handleSearch">搜索</n-button>
				<n-button @click="searchForm.reset" class="ml-2">重置</n-button>
			</div>

			<!-- 表格 -->
			<component :is="table.TableComponent" />
		</n-card>
	</div>
</template>

<script setup>
	import { useForm, useTable } from '@baota/naive-ui/hooks'
	import { NButton, NCard, useMessage } from 'naive-ui'
	import { h, ref } from 'vue'

	const message = useMessage()

	// 搜索表单配置
	const searchFormConfig = [
		{
			type: 'grid',
			cols: 24,
			xGap: 12,
			children: [
				{
					type: 'formItemGi',
					label: '用户名',
					span: 8,
					children: [
						{
							type: 'input',
							field: 'username',
							placeholder: '请输入用户名',
						},
					],
				},
				{
					type: 'formItemGi',
					label: '状态',
					span: 8,
					children: [
						{
							type: 'select',
							field: 'status',
							placeholder: '请选择状态',
							options: [
								{ label: '全部', value: '' },
								{ label: '启用', value: 'active' },
								{ label: '禁用', value: 'inactive' },
							],
						},
					],
				},
				{
					type: 'formItemGi',
					label: '注册时间',
					span: 8,
					children: [
						{
							type: 'datepicker',
							field: 'registerDate',
							type: 'daterange',
							clearable: true,
						},
					],
				},
			],
		},
	]

	// 搜索表单实例
	const searchForm = useForm({
		config: searchFormConfig,
		defaultValues: {
			username: '',
			status: '',
			registerDate: null,
		},
	})

	// 表格列配置
	const columns = [
		{
			title: 'ID',
			key: 'id',
			sorter: true,
		},
		{
			title: '用户名',
			key: 'username',
		},
		{
			title: '邮箱',
			key: 'email',
		},
		{
			title: '状态',
			key: 'status',
			render: (row) => {
				const statusMap = {
					active: { text: '启用', type: 'success' },
					inactive: { text: '禁用', type: 'error' },
				}
				const status = statusMap[row.status] || { text: '未知', type: 'default' }
				return h('n-tag', { type: status.type }, { default: () => status.text })
			},
		},
		{
			title: '注册时间',
			key: 'registerDate',
			sorter: true,
		},
		{
			title: '操作',
			key: 'actions',
			render: (row) =>
				h('div', [
					h(
						'n-button',
						{
							size: 'small',
							type: 'primary',
							onClick: () => handleEdit(row),
						},
						{ default: () => '编辑' },
					),
					h(
						'n-button',
						{
							size: 'small',
							type: 'error',
							style: 'margin-left: 8px',
							onClick: () => handleDelete(row),
						},
						{ default: () => '删除' },
					),
				]),
		},
	]

	// 模拟请求函数
	const mockRequestFn = async (params) => {
		// 模拟网络请求
		return new Promise((resolve) => {
			setTimeout(() => {
				// 模拟数据
				const mockData = [
					{ id: 1, username: 'user1', email: 'user1@example.com', status: 'active', registerDate: '2023-01-15' },
					{ id: 2, username: 'user2', email: 'user2@example.com', status: 'inactive', registerDate: '2023-02-20' },
					{ id: 3, username: 'user3', email: 'user3@example.com', status: 'active', registerDate: '2023-03-10' },
				]

				// 简单过滤
				let filteredData = [...mockData]
				if (params.username) {
					filteredData = filteredData.filter((item) =>
						item.username.toLowerCase().includes(params.username.toLowerCase()),
					)
				}
				if (params.status) {
					filteredData = filteredData.filter((item) => item.status === params.status)
				}

				resolve({
					list: filteredData,
					total: filteredData.length,
				})
			}, 500)
		})
	}

	// 表格默认参数
	const defaultParams = ref({
		page: 1,
		pageSize: 10,
	})

	// 创建表格实例
	const table = useTable({
		columns,
		requestFn: mockRequestFn,
		defaultParams,
		pagination: {
			pageSize: 10,
		},
		bordered: true,
	})

	// 搜索处理
	const handleSearch = async () => {
		// 合并搜索表单数据到表格参数
		Object.assign(table.params, searchForm.formData.value)
		await table.refresh()
	}

	// 编辑处理
	const handleEdit = (row) => {
		message.info(`编辑用户: ${row.username}`)
	}

	// 删除处理
	const handleDelete = (row) => {
		message.info(`删除用户: ${row.username}`)
	}
</script>

贡献

欢迎提交 Issue 或 Pull Request 来帮助改进这个组件库。

许可证

ISC

@baota/naive-ui

基于 Naive UI 的扩展库,提供一系列增强功能和实用工具。

主要功能

  • 统一的主题管理
  • 扩展的 hooks 工具集
  • 支持组件内和组件外使用的 API

安装

# npm
npm install @baota/naive-ui

# yarn
yarn add @baota/naive-ui

# pnpm
pnpm add @baota/naive-ui

Message 消息提示

组件内使用 useMessage

在 Vue 组件内,可以直接使用 useMessage hook

import { defineComponent } from 'vue'
import { useMessage } from '@baota/naive-ui/hooks'

export default defineComponent({
	setup() {
		const message = useMessage()

		// 基本使用
		const showMessage = () => {
			message.success('操作成功')
			message.error('操作失败')
			message.warning('警告提示')
			message.info('信息提示')
		}

		// 处理请求结果
		const handleApiResponse = (response) => {
			message.request(response) // 自动根据 status 判断显示成功或失败消息
		}

		return { showMessage, handleApiResponse }
	},
})

非组件环境使用 createAllApi

在非组件环境如工具函数、API 请求拦截器等)中,可以使用 createAllApi 创建全局可用的 API 实例:

// api/index.ts
import { createAllApi } from '@baota/naive-ui/hooks'

// 创建全局API实例
export const globalApi = createAllApi()

// 在任何地方使用
export function handleApiResponse(response) {
	// 自动根据 status 判断显示成功或失败消息
	globalApi.message.request(response)
}

// 也可以直接调用特定类型的消息
export function showSuccessMessage(content: string) {
	globalApi.message.success(content)
}

整合 createDiscreteApi 的完整方案

createAllApi 整合了 Naive UI 的 createDiscreteApi,包含了 message、notification、dialog 和 loadingBar 功能,并且扩展了 request 方法:

import { createAllApi } from '@baota/naive-ui/hooks'

// 创建全局API实例
const { message, notification, dialog, loadingBar } = createAllApi()

// 使用 message
message.success('操作成功')
message.error('操作失败')
message.request({ status: true, message: '请求成功' })

// 使用 dialog
dialog.info({
	title: '提示',
	content: '这是一个对话框',
})
dialog.request({ status: false, message: '操作失败' })

// 使用 notification
notification.success({
	title: '成功',
	content: '操作已完成',
})

// 使用 loadingBar
loadingBar.start()
// 操作完成后
loadingBar.finish()

自定义主题

可以通过传入配置来自定义主题:

import { createAllApi } from '@baota/naive-ui/hooks'
import { darkTheme } from 'naive-ui'

// 使用暗色主题
const api = createAllApi({
	configProviderProps: {
		theme: darkTheme,
	},
})

常见问题

为什么需要同时支持 useMessage 和 createAllApi

  • useMessage 适合在组件内使用,可以访问组件上下文
  • createAllApi 适用于非组件环境如工具函数、API 请求拦截器等

message.request 方法是什么?

这是我们扩展的便捷方法,用于统一处理 API 响应结果:

// API响应格式
interface ApiResponse {
	status: boolean
	message: string
}

// 自动根据status显示成功或失败消息
message.request(apiResponse)