欢迎光临
我们一直在努力

使用vite+vue3+ElementPlus+pinia搭建中后台应用-前端-1

使用vite+vue3+ElementPlus+pina搭建中后台应用

  • 新建项目
  • 引入ElementPlus
    • 安装
    • 使用
    • 引入样式
      • 自定义主题
    • 布局
    • 引入Pinia
      • 安装
      • hooks-useWindow
        • 工具库-获取浏览器信息和窗口大小
      • store存储菜单状态
      • 处理菜单折叠
      • 在header处理折叠
        • 引入 @element-plus/icons-vue
    • 引入vue-router
    • 网络请求

进度
2025-10-30

在这里插入图片描述

仓库地址 https://github.com/codehskdog/system-manager

新建项目

使用vite新建一个vue3+ts项目 我的vite版本是7.17, vue为3.5.22 npm init vite@latest 在这里插入图片描述 使用你喜欢的包管理工具安装依赖。 安装完成后执行 npm run dev

引入ElementPlus

安装

选择你喜欢的包管理器安装即可 npm/yarn/pnpm pnpm install element-plus

使用

这里使用按需引入。

需要安装pnpm install -D unplugin-vue-components unplugin-auto-import

修改vite.config.ts

import vue from '@vitejs/plugin-vue';
import { defineConfig } from 'vite';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
});

在App.vue中,根组件为

<script setup lang="ts"></script>

<template>
<el-config-provider>
<!– … –>
</el-config-provider>
</template>

引入样式

尝试下 在这里插入图片描述 在这里插入图片描述 没有引入样式就正常显示了。why?自动导入插件帮我们做了

自定义主题

新建文件 src/styles/index.scss 这里可以写自己项目的样式。 然后在main.ts引入 比如我们这样写。

body {
width: 100vw;
height: 100vh;
margin: 0;
}

#app {
width: 100%;
height: 100%;
}

新建文件 src/styles/element/var.scss 写一些自定义的主题 控制台可能会报错,说你却少sass依赖,根据它的提示安装就好。 在这里插入图片描述 在这里重写一些定义

@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': #003261,
),
'success': (
'base': #21ba45,
),
'warning': (
'base': #f2711c,
),
'danger': (
'base': #db2828,
),
'error': (
'base': #db2828,
),
'info': (
'base': #42b8dd,
),
),
$button-padding-horizontal: (
'default': 80px,
)
);

配置vite.config.ts 配置别名 '@': path.resolve(__dirname, 'src'),

import vue from '@vitejs/plugin-vue';
import { defineConfig } from 'vite';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import path from 'path';

export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [
ElementPlusResolver({
importStyle: 'sass',
}),
],
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/element/var.scss" as *;`,
},
},
},
});

主要是配置css预处理选项,然后因为是按需导入,需要配置自动导入的为scss。 在这里插入图片描述

布局

新建一个目录 src/layout 新建一个文件scr/layout/index.vue layout目录下新建两个目录 src/layout/pc src/layout/mobile 分别新建index.vue 在这里插入图片描述

将App.vue中的 在这里插入图片描述 移动到src/layout/index.vue 并且在App.vue引入src/layout/index.vue 在这里插入图片描述

此刻可能会提示找不到定义什么的。 修改tsconfig.app.json 增加baseUrl和paths。 在这里插入图片描述

这里先不管 mobile/index.vue,先完善pc/index.vue

<template>
<el-container>
<el-aside width="200px"> <Menu /></el-aside>
<el-container>
<el-header><Header /></el-header>
<el-main><Page /></el-main>
</el-container>
</el-container>
</template>

<script setup>
import Menu from './Menu.vue';
import Header from './Header.vue';
import Page from './Page.vue';
</script>

<style></style>

在layout/index.vue引入使用 在这里插入图片描述 在pc目录下新增几个组件,并在pc/index.vue引入使用 在这里插入图片描述 修改下 el-header的默认padding和高度 这里你可以看看element-plus的定义找到header的 在这里插入图片描述 在这里插入图片描述 对于aside的宽度,我们查看下元素,发现–el-aside-width未定义

在这里插入图片描述 我们可以自己定义 在这里插入图片描述 说白了,他这个定义也就是注入到root,我们同样可以。包括定义$header-bg-color的颜色。 header.vue

<template>
<div class="header"></div>
</template>

<script setup lang="ts"></script>

<style scoped lang="scss">
.header {
width: 100%;
height: 100%;
background-color: $header-bg-color;
}
</style>

当然我这里使用的是scss,你也可以使用一些原子化的样式。

Menu.vue

<template>
<div class="menu"></div>
</template>

<script setup lang="ts"></script>

<style style lang="scss">
.menu {
width: 100%;
height: 100vh;
background-color: $menu-bg-color;
}
</style>

在这里插入图片描述 然后Page.vue 此时我们的内容存放位置el-main 在这里插入图片描述 所以Page暂时比较简单,先把router-view写进来,虽然还没开始使用vue-router page.vue

<template>
<div class="content">
<router-view></router-view>
</div>
</template>

<script setup lang="ts"></script>

<style lang="scss" coped>
.content {
width: 100%;
height: 100%;
}
</style>

到此布局简单完成。

布局就像我们一个人,无论你穿什么样子的衣服,你总是有头,身体,四肢对吧,然后我们就可以戴帽子,穿衣服,做搭配?layout/index.vue可以理解为试衣间,给我们提供数据等等。

引入Pinia

https://pinia.vuejs.org/core-concepts/

安装

pnpm add pinia

新增目录 src/stores并在此目录下增加index.ts

负责处理Pinia的注册。

import { createPinia } from 'pinia';

const pinia = createPinia();

export default pinia;

在main.ts中引入

import { createApp } from 'vue';
import App from './App.vue';
import '@/styles/index.scss';
import pinia from '@/stores/index';

const app = createApp(App);

app.use(pinia).mount('#app');

hooks-useWindow

工具库-获取浏览器信息和窗口大小

修改tsconfig的配置 在这里插入图片描述

新增目录 src/utils 负责存放公共工具方法。并新增index.ts文件 在这里插入图片描述

新增enums.ts文件

// 屏幕尺寸枚举
export enum ScreenSize {
XS = 'xs', // <768px
SM = 'sm', // 768px~991px
MD = 'md', // 992px~1199px
XL = 'xl', // 1200px~1919px
FULL = 'full', // >=1920px
}

// 浏览器类型枚举
export enum BrowserType {
CHROME = 'chrome',
SAFARI = 'safari',
FIREFOX = 'firefox',
OPERA = 'opera',
MSIE = 'msie',
OTHER = 'other',
}

// 设备标签枚举
export enum DeviceTag {
PC = 'pc',
MOBILE = 'mobile',
PAD = 'pad',
ANDROID_PAD = 'androidPad',
}

// 浏览器前缀枚举
export enum BrowserPrefix {
WEBKIT = 'webkit',
MS = 'ms',
MOZ = 'Moz',
O = 'O',
}

utils/index.ts

import { ScreenSize, BrowserType, DeviceTag, BrowserPrefix } from './enums'; // 导入枚举

export function getBrowser() {
const docEl = document.documentElement;
const ua = navigator.userAgent.toLowerCase();
const { platform } = navigator;

const { clientWidth: width, clientHeight: height } = docEl;

// 1. 检测浏览器类型
let browserType: BrowserType = BrowserType.OTHER;
if (ua.includes('firefox')) {
browserType = BrowserType.FIREFOX;
} else if (ua.includes('chrome')) {
browserType = BrowserType.CHROME;
} else if (ua.includes('safari')) {
browserType = BrowserType.SAFARI;
} else if (ua.includes('opera') || ua.includes('opr')) {
browserType = BrowserType.OPERA;
} else if (ua.includes('msie') || ua.includes('trident')) {
browserType = BrowserType.MSIE;
}

// 2. 检测设备类型
const isTouchDevice =
'ontouchstart' in window || ua.includes('touch') || ua.includes('mobile');

let deviceTag: DeviceTag = DeviceTag.PC;
if (isTouchDevice) {
if (ua.includes('ipad')) {
deviceTag = DeviceTag.PAD;
} else if (ua.includes('mobile')) {
deviceTag = DeviceTag.MOBILE;
} else if (ua.includes('android')) {
deviceTag = DeviceTag.ANDROID_PAD;
}
}

// 3. 确定浏览器前缀(使用枚举映射)
const prefixMap: Record<BrowserType, BrowserPrefix> = {
[BrowserType.CHROME]: BrowserPrefix.WEBKIT,
[BrowserType.SAFARI]: BrowserPrefix.WEBKIT,
[BrowserType.FIREFOX]: BrowserPrefix.MOZ,
[BrowserType.OPERA]: BrowserPrefix.O,
[BrowserType.MSIE]: BrowserPrefix.MS,
[BrowserType.OTHER]: BrowserPrefix.WEBKIT,
};
const browserPrefix = prefixMap[browserType];

// 4. 检测操作系统平台
const osPlatform = ua.includes('android')
? 'android'
: platform.toLowerCase();

// 5. 屏幕尺寸分级(使用 ScreenSize 枚举)
let screenSize: ScreenSize = ScreenSize.FULL;
if (width < 768) {
screenSize = ScreenSize.XS;
} else if (width < 992) {
screenSize = ScreenSize.SM;
} else if (width < 1200) {
screenSize = ScreenSize.MD;
} else if (width < 1920) {
screenSize = ScreenSize.XL;
}

// 6. 衍生辅助判断
const isIOS = /\\(i[^;]+;( u;)? cpu.+mac os x/.test(ua);
const isPC = deviceTag === DeviceTag.PC;
const isMobile = !isPC;
const isMini = screenSize === ScreenSize.XS || isMobile;

return {
width,
height,
type: browserType,
prefix: browserPrefix,
plat: osPlatform,
tag: deviceTag,
screen: screenSize,
isMobile,
isIOS,
isPC,
isMini,
};
}

useWindow.ts

import { onMounted, onUnmounted, ref } from 'vue';
import { getBrowser } from '@/utils/index'; // 导入浏览器信息检测函数
import {
ScreenSize,
BrowserType,
DeviceTag,
BrowserPrefix,
} from '@/utils/enums'; // 导入类型

export type BrowserInfo = {
width: number;
height: number;
type: BrowserType;
prefix: BrowserPrefix;
plat: string;
tag: DeviceTag;
screen: ScreenSize;
isMobile: boolean;
isIOS: boolean;
isPC: boolean;
isMini: boolean;
};
export const useWindow = () => {
// 用ref存储响应式浏览器信息
const browserInfo = ref<BrowserInfo>({} as BrowserInfo);
const needCollapse = ref(false);

// 更新浏览器信息的方法
const updateBrowserInfo = () => {
browserInfo.value = getBrowser();
const need = [ScreenSize.XS, ScreenSize.SM, ScreenSize.MD];
if (need.includes(browserInfo.value.screen)) {
needCollapse.value = true;
} else {
needCollapse.value = false;
}
};

// 初始化信息
updateBrowserInfo();
// 监听窗口变化(无节流,实时触发)
window.addEventListener('resize', updateBrowserInfo);

onUnmounted(() => {
// 移除监听,避免内存泄漏
window.removeEventListener('resize', updateBrowserInfo);
});

return {
browserInfo,
needCollapse,
// 手动更新方法
updateBrowserInfo,
};
};

store存储菜单状态

在这里插入图片描述

import { useWindow } from '@/hooks/useWindow';
import { defineStore } from 'pinia';
import { ref, watch } from 'vue';

export const useGlobalStore = defineStore('global', () => {
const { browserInfo, needCollapse } = useWindow();
watch(
() => browserInfo.value.screen,
(v) => {
menuCollapsed.value = needCollapse.value;
}
);
const menuCollapsed = ref(needCollapse.value);
const collapsedMenu = () => {
menuCollapsed.value = true;
};
const expandMenu = () => {
menuCollapsed.value = false;
};

return { menuCollapsed, collapsedMenu, expandMenu, browserInfo };
});

处理菜单折叠

这个时候可以先把el-aside的宽度设置为auto 在这里插入图片描述

在Menu.vue

<template>
<el-menu default-active="2" :collapse-transition="false" :collapse="menuCollapsed" class="menu">
<el-menu-item index="2">
<template #title>Navigator Two</template>
</el-menu-item>
<el-menu-item index="3" disabled>
<template #title>Navigator Three</template>
</el-menu-item>
<el-menu-item index="4">
<template #title>Navigator Four</template>
</el-menu-item>
</el-menu>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { useGlobalStore } from '@/stores/modules/global';
import { storeToRefs } from 'pinia';
const store = useGlobalStore();
const { menuCollapsed } = storeToRefs(store);
const width = computed(() => {
return menuCollapsed.value ? '80px' : '200px';
});
</script>

<style style lang="scss" scoped>
.menu {
width: 100%;
height: 100vh;
width: v-bind(width);
}
</style>

在header处理折叠

引入 @element-plus/icons-vue

pnpm install @element-plus/icons-vue

在这里插入图片描述 使用Expand和Fold 先去掉 背景色,加个border区分。

<template>
<div class="header">
<el-icon size="20" class="icon">
<component
:is="menuCollapsed ? Expand : Fold"
@click="changeMenuCollapsed"
/>

</el-icon>
</div>
</template>

<script setup lang="ts">
import { useGlobalStore } from '@/stores/modules/global';
import { Expand, Fold } from '@element-plus/icons-vue';

const { menuCollapsed, changeMenuCollapsed } = useGlobalStore();
</script>

<style scoped lang="scss">
.header {
display: flex;
align-items: center;
box-sizing: border-box;
padding: 0 10px;
width: 100%;
height: 100%;
border-bottom: 1px solid #e3e3e3;
// background-color: $header-bg-color;
.icon
{
cursor: pointer;
}
}
</style>

引入vue-router

安装pnpm add vue-router@4 新建目录src/router 新建目录src/views 在views目录下新建两个vue文件,随便输入点内容 在这里插入图片描述

<template>
<div>Home</div>
</template>

<script></script>

<style></style>

在src/router下新增index.ts

import { createWebHashHistory, createRouter } from 'vue-router';

const routes = [
{ path: '/', component: () => import('@/views/Home.vue') },
{ path: '/about', component: () => import('@/views/About.vue') },
];

const router = createRouter({
history: createWebHashHistory(),
routes,
});

export default router;

在main.ts中引入 在这里插入图片描述

有了router后再改下Menu

<template>
<el-menu
default-active="/"
:collapse="menuCollapsed"
:collapse-transition="false"
class="menu"
router
>

<el-menu-item index="/">
<el-icon>
<HomeFilled />
</el-icon>
<template #title>首页</template>
</el-menu-item>

<el-menu-item index="/about">
<el-icon>
<Setting />
</el-icon>
<template #title>About</template>
</el-menu-item>
</el-menu>
</template>

<script setup lang="ts">
import { HomeFilled, Setting } from '@element-plus/icons-vue';
import { computed, ref, watch } from 'vue';
import { useGlobalStore } from '@/stores/modules/global';
import { storeToRefs } from 'pinia';
const store = useGlobalStore();
const { menuCollapsed } = storeToRefs(store);
const width = computed(() => {
return menuCollapsed.value ? '80px' : '200px';
});
</script>

<style style lang="scss" scoped>
.menu {
width: 100%;
height: 100vh;
width: v-bind(width);
}
</style>

到此就有了一个大概的框架。

在这里插入图片描述

网络请求

一般我们的页面渲染可以理解为Render(data) 我们负责写Render,而后端提供data。 网络请求接入。 一般使用axios,或者可以尝试下umi的一个中间件umi-request。 umi-request文档地址 安装 pnpm install –save umi-request 在这里插入图片描述 我们也用创建实例的方式 在这里插入图片描述 新增文件src/api/index.ts 在这里插入图片描述 PS:2025-10-30暂停前端开发,先搭建后端

赞(0)
未经允许不得转载:171主机测评 » 使用vite+vue3+ElementPlus+pinia搭建中后台应用-前端-1
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址