Pinia
# 安装 pinia pinia-plugin-persistedstate
pnpm add pinia pinia-plugin-persistedstate
创建:src/store/index.ts
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia();
// 注册数据持久化插件
pinia.use(piniaPluginPersistedstate);
export default pinia
配置:main.ts
import { createApp } from 'vue';
// 引入状态管理
import pinia from '@/store';
import App from './App.vue';
const app = createApp(App);
// 挂载状态管理
app.use(pinia);
app.mount('#app');
Vue I18n
# 安装 Vue I18n
pnpm add vue-i18n
创建:src/locales/en-us/index.ts
export default {};
创建:src/locales/zh-cn/index.ts
export default {};
创建:src/types/global.ts
// 语言选项类型
type LangKey = 'zhCn' | 'enUs';
export type { LangKey };
创建:src/types/index.ts
export * from './global';
创建:src/locales/index.ts
import { createI18n } from 'vue-i18n';
import type { LangKey } from '@/types';
import enUs from './en-us/index';
import zhCn from './zh-cn/index';
// 语言选项
const LANG_OPT: Record<LangKey, string> = {
enUs: 'English',
zhCn: '简体中文',
};
// 语言包
const langPkg: Record<LangKey, any> = {
enUs,
zhCn,
};
// 创建 i18n 实例
const i18n = createI18n({
// 组合 API
legacy: false,
// 默认语言为中文
locale: 'zhCn',
// 当语言文件中缺少翻译时,回退到英文
fallbackLocale: 'enUs',
// 启用全局注入,允许在组件中使用 `$t` 等方法
globalInjection: true,
// 语言包配置
messages: langPkg,
});
export default i18n;
export { LANG_OPT };
配置:main.ts
import { createApp } from 'vue';
// 引入国际化
import i18n from '@/locales';
import App from './App.vue';
const app = createApp(App);
// 挂载国际化
app.use(i18n);
app.mount('#app');
Vue Router
pnpm add nprogress
pnpm add -D @types/nprogress
# 安装 Vue Router
pnpm add vue-router
创建:src/layout/index.vue
<script setup lang="ts">
defineOptions({ name: 'Layout' });
</script>
<template>
<div class="container">Layout</div>
</template>
创建:src/views/home/index.vue
<script setup lang="ts">
defineOptions({ name: 'Home' });
</script>
<template>
<div class="container">Home</div>
</template>
创建:src/router/routes.ts
import type { RouteRecordRaw } from 'vue-router';
// 静态路由
const staticRoutes: RouteRecordRaw[] = [
{
path: '/',
name: 'Layout',
component: () => import('@/layout/index.vue'),
meta: {
hidden: false,
},
redirect: '/home',
children: [
{
path: '/home',
name: 'Home',
component: () => import('@/views/home/index.vue'),
meta: {
hidden: false,
},
},
],
},
];
// 动态路由
const dynamicRoutes: RouteRecordRaw[] = [];
export default [...staticRoutes, ...dynamicRoutes];
export { staticRoutes, dynamicRoutes };
创建:src/router/guards.ts
import nprogress from 'nprogress';
import type { Router } from 'vue-router';
// 引入进度条样式
import 'nprogress/nprogress.css';
/**
* 注册全局路由守卫
* @param router - 路由实例
*/
export default function setupRouterGuards(router: Router) {
// 全局前置守卫
router.beforeEach((_to, _, next) => {
// 开启进度条
nprogress.start();
next();
});
// 全局后置守卫
router.afterEach(() => {
// 关闭进度条
nprogress.done();
});
}
创建:src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import setupRouterGuards from './guards';
import { routes } from './routes';
const router = createRouter({
/*
* 使用 HTML5 History 模式,支持浏览器的前进、后退操作
* BASE_URL 从 Vite 配置的环境变量中读取,通常是应用的根路径
*/
history: createWebHistory(import.meta.env.BASE_URL),
// 路由配置
routes,
// 滚动行为
scrollBehavior(to, _from, savedPosition) {
if (savedPosition) {
// 如果存在保存的位置(如浏览器前进/后退时),恢复到保存的位置
return savedPosition;
}
if (to.hash) {
// 如果目标路由包含锚点,滚动到锚点位置
return { el: to.hash, behavior: 'smooth' };
}
// 默认滚动到顶部
return { top: 0 };
},
});
// 注册全局路由守卫
setupRouterGuards(router);
export default router;
export * from './routes';
配置:main.ts
import { createApp } from 'vue';
// 引入路由
import router from '@/router';
import App from './App.vue';
const app = createApp(App);
// 挂载路由
app.use(router);
app.mount('#app');
配置:App.vue
<script setup lang="ts">
defineOptions({
name: 'App',
});
</script>
<template>
<router-view />
</template>
Element Plus
pnpm add element-plus
配置:main.ts
// 引入 element-plus 样式
import 'element-plus/dist/index.css';
// 引入 element-plus 暗黑模式样式
import 'element-plus/theme-chalk/dark/css-vars.css';
# 按需导入
pnpm add -D unplugin-vue-components unplugin-auto-import unplugin-icons
# https://icones.js.org
pnpm add -D @iconify/json
配置:vite.config.ts
import AutoImport from 'unplugin-auto-import/vite';
import IconsResolver from 'unplugin-icons/resolver';
import Icons from 'unplugin-icons/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import Components from 'unplugin-vue-components/vite';
import { defineConfig } from 'vite';
// https://vite.dev/config/
export default defineConfig({
plugins: [
AutoImport({
resolvers: [ElementPlusResolver(), IconsResolver()],
}),
Components({
resolvers: [ElementPlusResolver(), IconsResolver()],
}),
Icons({
compiler: 'vue3'
}),
],
});
Axios
# 安装 Axios
pnpm add axios
创建:src/utils/request.ts
import axios, { type AxiosInstance, type InternalAxiosRequestConfig } from 'axios';
const request: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
request.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
return config;
},
(error) => Promise.reject(error),
);
request.interceptors.response.use(
(response) => {
const { code, msg } = response.data;
if (code !== 200) {
return Promise.reject(new Error(msg || 'Unknown'));
}
return response.data;
},
(error) => Promise.reject(error),
);
// 响应接口统一格式
interface ApiResponse<T = any> {
code: number;
msg: string;
data: T;
}
// 封装常用方法
const httpGet = <T>(url: string, params?: Record<string, any>): Promise<ApiResponse<T>> =>
request.get<any, ApiResponse<T>>(url, { params });
const httpPost = <T>(url: string, data?: Record<string, any>): Promise<ApiResponse<T>> =>
request.post<any, ApiResponse<T>>(url, data);
const httpPut = <T>(url: string, data?: Record<string, any>): Promise<ApiResponse<T>> =>
request.put<any, ApiResponse<T>>(url, data);
const httpPatch = <T>(url: string, data?: Record<string, any>): Promise<ApiResponse<T>> =>
request.patch<any, ApiResponse<T>>(url, data);
const httpDelete = <T>(url: string, params?: Record<string, any>): Promise<ApiResponse<T>> =>
request.delete<any, ApiResponse<T>>(url, { params });
// 可选:支持传入完整的 config(更灵活)
const httpRequest = <T>(config: InternalAxiosRequestConfig): Promise<ApiResponse<T>> => request(config);
export { httpGet, httpPost, httpPut, httpPatch, httpDelete, httpRequest };
配置:src/utils/index.ts
export * from './request';
接下来就可以开始编写我们的管理页面啦!!!
【博灵A3】这是一个掌握天气预报的报警灯