Robot Admin NaiveUI 集成详解
🎨 基于 NaiveUI 的组件系统
高质量、高性能的用户界面组件框架,提供全面的主题定制能力
NaiveUI 是 Robot Admin 中使用的主要 UI 组件框架,用于提供高质量、高性能的用户界面,并具备全面的主题定制能力。本指南将详细介绍 NaiveUI 如何集成到项目中,如何使用其组件,以及如何利用其强大的主题系统。
🎯 集成架构概述

Naive UI 是一个支持 TypeScript 的 Vue 3 组件库,提供了一整套可定制主题的 UI 组件。Robot Admin 通过插件架构集成 NaiveUI,并实现了一个复杂的主题系统,支持亮色和暗色模式。
核心集成特性
特性 | 说明 | 价值 |
---|---|---|
提供者架构 | 全局配置和服务注入 | 统一的组件配置 |
主题系统 | 支持亮色/暗色/系统主题 | 灵活的视觉定制 |
TypeScript | 完整的类型支持 | 更好的开发体验 |
响应式设计 | 自适应不同屏幕尺寸 | 优秀的用户体验 |
💡 集成优势
NaiveUI 通过组件提供者和插件系统的组合进行集成,实现了统一的主题管理和全局服务访问。
🔧 基础集成设置
提供者配置
NaiveUI 集成的核心在 App.vue
中,将整个应用程序包裹在 NaiveUI 提供者中:
vue
<template>
<NConfigProvider
:theme="themeStore.currentTheme"
:theme-overrides="themeStore.themeOverrides"
:locale="zhCN"
:date-locale="dateZhCN"
class="global-config-provider"
>
<NLoadingBarProvider>
<NDialogProvider>
<NNotificationProvider>
<NMessageProvider>
<RouterView />
</NMessageProvider>
</NNotificationProvider>
</NDialogProvider>
</NLoadingBarProvider>
</NConfigProvider>
</template>
<script setup lang="ts">
import { useThemeStore } from "@/stores/theme";
import { zhCN, dateZhCN } from "naive-ui";
const themeStore = useThemeStore();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
这种嵌套的提供者结构实现了:
- NConfigProvider: 主题配置和全局设置
- NLoadingBarProvider: 加载条功能
- NDialogProvider: 对话框系统
- NNotificationProvider: 通知系统
- NMessageProvider: 消息系统
插件注册
应用程序在启动过程中通过插件系统初始化 NaiveUI:
typescript
import { setupNaiveUI } from "@/plugins/naive-ui-plugin";
async function bootstrap() {
const app = createApp(App);
// 设置 NaiveUI
setupNaiveUI(app);
app.mount("#app");
}
bootstrap();
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
typescript
import type { App } from "vue";
import { createDiscreteApi } from "naive-ui";
const { notification, message, dialog } = createDiscreteApi([
"message",
"dialog",
"notification",
]);
export function setupNaiveUI(app: App) {
// 全局提供通知服务
app.provide("notification", notification);
app.provide("message", message);
app.provide("dialog", dialog);
}
// 导出服务供组件外使用
export { notification, message, dialog };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
🎨 组件使用方式
基本组件使用
在 Vue 组件中直接使用任何 NaiveUI 组件:
vue
<template>
<div class="component-demo">
<!-- 按钮组件 -->
<n-space>
<n-button type="primary">主按钮</n-button>
<n-button type="success">成功按钮</n-button>
<n-button type="warning">警告按钮</n-button>
<n-button type="error">错误按钮</n-button>
</n-space>
<!-- 输入组件 -->
<n-input v-model:value="inputValue" placeholder="请输入内容" clearable />
<!-- 日期选择器 -->
<n-date-picker v-model:value="date" type="date" placeholder="选择日期" />
<!-- 数据表格 -->
<n-data-table
:columns="columns"
:data="tableData"
:pagination="pagination"
/>
</div>
</template>
<script setup lang="ts">
import { ref, h } from "vue";
import { NButton } from "naive-ui";
// 响应式数据
const inputValue = ref("");
const date = ref<number | null>(null);
// 表格配置
const columns = [
{ title: "姓名", key: "name" },
{ title: "年龄", key: "age" },
{
title: "操作",
key: "actions",
render: (row: any) =>
h(NButton, { size: "small", onClick: () => handleEdit(row) }, "编辑"),
},
];
const tableData = ref([
{ name: "张三", age: 25 },
{ name: "李四", age: 30 },
]);
const pagination = {
pageSize: 10,
showSizePicker: true,
pageSizes: [10, 20, 50],
};
const handleEdit = (row: any) => {
console.log("编辑:", row);
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
全局服务使用
项目提供了全局 API,可以在 Vue 组件外部使用:
typescript
import { notification, message, dialog } from "@/plugins/naive-ui-plugin";
// 通知 API
export const showNotification = {
success: (title: string, content?: string) => {
notification.success({
title,
content,
duration: 3000,
});
},
error: (title: string, content?: string) => {
notification.error({
title,
content,
duration: 5000,
});
},
warning: (title: string, content?: string) => {
notification.warning({
title,
content,
duration: 4000,
});
},
};
// 消息 API
export const showMessage = {
success: (content: string) => message.success(content),
error: (content: string) => message.error(content),
warning: (content: string) => message.warning(content),
info: (content: string) => message.info(content),
};
// 对话框 API
export const showDialog = {
confirm: (title: string, content: string, onConfirm: () => void) => {
dialog.warning({
title,
content,
positiveText: "确定",
negativeText: "取消",
onPositiveClick: onConfirm,
});
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
typescript
import { showNotification, showMessage, showDialog } from "@/utils/ui";
// 在任何地方使用
const handleSuccess = () => {
showNotification.success("操作成功", "数据已保存");
showMessage.success("保存成功");
};
const handleDelete = () => {
showDialog.confirm("确认删除", "此操作不可恢复,确定要删除吗?", () => {
// 执行删除操作
console.log("执行删除");
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
🎨 主题指南
Robot Admin 基于 NaiveUI 的主题能力实现了一个全面的主题系统,支持亮色、暗色和系统主题。
主题配置定义
主题配置定义在 config/theme.ts
中:
typescript
import type { GlobalThemeOverrides } from "naive-ui";
// 主题常量
export const themeConstants = {
primaryColor: "#2080f0",
primaryColorHover: "#4098fc",
primaryColorPressed: "#1c7cd8",
successColor: "#18a058",
warningColor: "#f0a020",
errorColor: "#d03050",
infoColor: "#2080f0",
};
// 全局主题类型扩展
export interface CustomThemeOverrides extends GlobalThemeOverrides {
common?: {
primaryColor?: string;
primaryColorHover?: string;
primaryColorPressed?: string;
successColor?: string;
warningColor?: string;
errorColor?: string;
infoColor?: string;
};
Menu?: {
itemTextColor?: string;
itemIconColor?: string;
arrowColor?: string;
};
Button?: {
textColor?: string;
textColorHover?: string;
};
}
// 亮色主题配置
export const lightThemeOverrides: CustomThemeOverrides = {
common: {
...themeConstants,
bodyColor: "#ffffff",
cardColor: "#ffffff",
borderColor: "#e0e0e6",
},
Menu: {
color: "#ffffff",
itemTextColor: "#333639",
itemIconColor: "#666666",
arrowColor: "#666666",
},
};
// 暗色主题配置
export const darkThemeOverrides: CustomThemeOverrides = {
common: {
...themeConstants,
bodyColor: "#101014",
cardColor: "#18181c",
borderColor: "#2d2d30",
},
Menu: {
color: "#0d1425",
itemTextColor: "#e5e7eb",
itemIconColor: "#9ca3af",
arrowColor: "#9ca3af",
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
主题状态管理
主题通过主题存储进行管理:
typescript
import { defineStore } from "pinia";
import { ref, computed, nextTick } from "vue";
import { darkTheme } from "naive-ui";
import type { GlobalTheme, GlobalThemeOverrides } from "naive-ui";
export type ThemeMode = "light" | "dark" | "system";
export const useThemeStore = defineStore("theme", () => {
// 获取保存的主题模式
const savedMode = localStorage.getItem("theme-mode") as ThemeMode;
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
// 状态定义
const mode = ref<ThemeMode>(savedMode || "system");
const systemIsDark = ref(mediaQuery.matches);
const customOverrides = ref<GlobalThemeOverrides>({});
// 计算当前是否为暗色主题
const isDark = computed(() => {
return (
mode.value === "dark" || (mode.value === "system" && systemIsDark.value)
);
});
// 计算当前主题
const currentTheme = computed<GlobalTheme | null>(() => {
return isDark.value ? darkTheme : null;
});
// 计算主题覆盖
const themeOverrides = computed<GlobalThemeOverrides>(() => {
const baseOverrides = isDark.value
? darkThemeOverrides
: lightThemeOverrides;
return {
...baseOverrides,
...customOverrides.value,
};
});
// 设置主题模式
const setMode = async (newMode: ThemeMode) => {
mode.value = newMode;
localStorage.setItem("theme-mode", newMode);
await nextTick();
updateBodyClass();
};
// 更新主题覆盖
const updateThemeOverrides = (overrides: GlobalThemeOverrides) => {
customOverrides.value = { ...customOverrides.value, ...overrides };
localStorage.setItem(
"theme-overrides",
JSON.stringify(customOverrides.value)
);
};
// 重置主题覆盖
const resetThemeOverrides = () => {
customOverrides.value = {};
localStorage.removeItem("theme-overrides");
};
// 更新 body 类名
const updateBodyClass = () => {
if (isDark.value) {
document.body.classList.add("dark");
} else {
document.body.classList.remove("dark");
}
};
// 监听系统主题变化
mediaQuery.addEventListener("change", (e) => {
systemIsDark.value = e.matches;
});
// 初始化
updateBodyClass();
return {
mode,
isDark,
currentTheme,
themeOverrides,
setMode,
updateThemeOverrides,
resetThemeOverrides,
};
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
主题使用示例
vue
<template>
<div class="theme-controls">
<n-space>
<!-- 主题模式切换 -->
<n-select
v-model:value="themeStore.mode"
:options="themeOptions"
@update:value="handleThemeChange"
/>
<!-- 主色调设置 -->
<n-color-picker
v-model:value="primaryColor"
@update:value="handleColorChange"
/>
<!-- 重置主题 -->
<n-button @click="handleReset"> 重置主题 </n-button>
</n-space>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { useThemeStore } from "@/stores/theme";
const themeStore = useThemeStore();
const themeOptions = [
{ label: "浅色", value: "light" },
{ label: "深色", value: "dark" },
{ label: "跟随系统", value: "system" },
];
const primaryColor = ref("#2080f0");
const handleThemeChange = (value: string) => {
themeStore.setMode(value as any);
};
const handleColorChange = (color: string) => {
themeStore.updateThemeOverrides({
common: {
primaryColor: color,
primaryColorHover: adjustColor(color, 10),
primaryColorPressed: adjustColor(color, -10),
},
});
};
const handleReset = () => {
themeStore.resetThemeOverrides();
primaryColor.value = "#2080f0";
};
// 颜色调整工具函数
const adjustColor = (color: string, amount: number): string => {
// 简化的颜色调整逻辑
return color;
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
📋 最佳实践指南
开发建议
实践 | 说明 | 重要性 |
---|---|---|
主题常量 | 使用主题系统中的颜色常量 | 🟡 重要 |
类型安全 | 充分利用 TypeScript 类型支持 | 🔴 关键 |
响应式设计 | 使用 NaiveUI 的响应式组件 | 🟡 重要 |
平滑过渡 | 利用内置的主题切换过渡效果 | 🟡 重要 |
组件使用规范
vue
<template>
<div>
<!-- ✅ 推荐的组件使用方式 -->
<n-card title="用户信息" :bordered="false">
<n-form :model="formData" :rules="rules" ref="formRef">
<n-form-item label="用户名" path="username">
<n-input
v-model:value="formData.username"
placeholder="请输入用户名"
clearable
/>
</n-form-item>
<n-form-item label="角色" path="role">
<n-select
v-model:value="formData.role"
:options="roleOptions"
placeholder="请选择角色"
/>
</n-form-item>
<n-form-item>
<n-space>
<n-button type="primary" @click="handleSubmit"> 提交 </n-button>
<n-button @click="handleReset"> 重置 </n-button>
</n-space>
</n-form-item>
</n-form>
</n-card>
</div>
</template>
<script setup lang="ts">
import type { FormInst, FormRules } from "naive-ui";
import { showMessage } from "@/utils/ui";
// 表单引用
const formRef = ref<FormInst | null>(null);
// 表单数据
const formData = ref({
username: "",
role: null,
});
// 表单规则
const rules: FormRules = {
username: {
required: true,
message: "请输入用户名",
trigger: "blur",
},
role: {
required: true,
message: "请选择角色",
trigger: "change",
},
};
// 角色选项
const roleOptions = [
{ label: "管理员", value: "admin" },
{ label: "用户", value: "user" },
];
// 提交处理
const handleSubmit = () => {
formRef.value?.validate((errors) => {
if (!errors) {
showMessage.success("提交成功");
}
});
};
// 重置处理
const handleReset = () => {
formRef.value?.restoreValidation();
Object.assign(formData.value, {
username: "",
role: null,
});
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
与其他系统集成
UnoCSS 集成
在样式化组件时的优先级:
- NaiveUI 内置属性: 组件特定样式
- 主题覆盖: 全局样式更改
- UnoCSS 实用类: 微调和布局
vue
<template>
<!-- 使用 NaiveUI 属性 + UnoCSS 类 -->
<n-card :bordered="false" class="mb-4 shadow-sm">
<n-button type="primary" size="large" class="w-full">
完整宽度按钮
</n-button>
</n-card>
</template>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
🎯 开发建议
NaiveUI 为 Robot Admin 的 UI 提供了坚实的基础,通过全局提供者、插件系统和主题存储的集成架构,实现了一个灵活且可维护的 UI 系统。利用其强大的主题系统和丰富的组件库,可以快速构建美观且功能完善的用户界面。
有关使用特定 NaiveUI 组件的更多详细信息,请参考官方 NaiveUI 文档。