• 使用vue3搭建后台系统的过程记录


    首先使用npm 或者yarn创建一个vue项目

    1. // 使用npm创建一个基于vite构建的vue项目
    2. npm create vite@latest
    3. // 使用yarn创建一个基于vite构建的vue项目
    4. yarn create vite@latest

    在创建的构成中选择        vue        vue-ts

    创建完之后将项目拖到编译器打开

    一、配置vite

    在vite.config.ts文件中配置项目的服务数据,配置如下:

    1. // 此处配置项目服务参数
    2. server: {
    3. host: "0.0.0.0", // 项目运行地址,此处代表localhost
    4. port: 8888, // 项目运行端口
    5. open: true, //编译之后是否自动打开页面
    6. hmr: true, // 是否开启热加载
    7. },

    之后server下方接着配置src的别名@,配置如下

    1. // 配置src的别名@
    2. resolve: {
    3. alias: {
    4. "@": resolve(__dirname, "./src"),
    5. },
    6. },

    此外还需在ts的配置文件tsconfig.json中加入以下配置:

    1. "baseUrl": "./", // 配置路径解析的起点
    2. "paths": { // 配置src别名
    3. "@/*": ["src/*"] // 当我们输入@/时会被映射成src/
    4. }

    二、router路由

    1、安装router路由

    1. npm install vue-router@latest
    2. yarn add vue-router@latest

    2、配置router路由

    在src下新建router文件夹,同时创建index.ts并配置如下

    1. import { createRouter, createWebHistory, RouteRecordRaw} from 'vue-router';
    2. import Layout from '@/components/HelloWorld.vue'
    3. // 定义路由,此处为Array数组,数据类型为RouteRecordRaw
    4. const routes: Array<RouteRecordRaw> = [
    5. {
    6. path: '/home',
    7. name: 'home',
    8. component: Layout
    9. }
    10. ]
    11. // 创建路由
    12. const router = createRouter({
    13. history: createWebHistory(),
    14. routes // 将定义的路由传入
    15. })
    16. // 将创建的router路由暴露,使其在其他地方可以被引用
    17. export default router

    3、注册router路由

    在main.ts中先通过        import router from '@/router/index'         引入路由,然后使用use函数注册路由,具体如下:

    1. import { createApp } from 'vue'
    2. import './style.css'
    3. import App from './App.vue'
    4. // 此处引入定义的路由
    5. import router from '@/router/index'
    6. // createApp(App).mount('#app')
    7. // 此处将链式创建拆解,从中注册路由
    8. const app = createApp(App);
    9. // 注册路由
    10. app.use(router)
    11. app.mount('#app')

    4、使用router路由

    注册完成之后,在程序入口App.vue中通过 使用路由,具体如下:

    三、安装element plus等其他依赖

    1. # 选择一个你喜欢的包管理器
    2. // 安装element-plus
    3. npm install element-plus --save
    4. yarn add element-plus
    5. pnpm install element-plus
    6. // 安装element-plus的图标库组件
    7. npm install @element-plus/icons-vue
    8. yarn add @element-plus/icons-vue
    9. pnpm install @element-plus/icons-vue

    1、注册element plus并配置图标

    和router一样都是在main.ts中注册,配置如下:

    1. import { createApp } from "vue";
    2. import "./style.css";
    3. import App from "./App.vue";
    4. // 次数引入定义的路由
    5. import router from "@/router/index";
    6. // 引入element-plus
    7. import ElementPlus from "element-plus";
    8. import "element-plus/dist/index.css";
    9. // 引入element-plus的图标库
    10. import * as ElementPlusIconsVue from "@element-plus/icons-vue";
    11. // createApp(App).mount('#app')
    12. // 此处将链式创建拆解,从中注册路由
    13. const app = createApp(App);
    14. // 注册路由、element-plus等
    15. app.use(router).use(ElementPlus);
    16. // 将所有配置挂载到index.html的id为app的容器上
    17. app.mount("#app");
    18. // 此处参考官网,意为将图标库中的每个图标都注册成组件
    19. for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    20. app.component(key, component);
    21. }

    具体可以参考element-plus官网图标配置

    四、pinia使用                                pinia官网

    1、安装pinia

    1. yarn add pinia
    2. # 或者使用 npm
    3. npm install pinia

    2、注册pinia

    1. // 从pinia中引入创建实例的函数
    2. import { createPinia } from 'pinia'
    3. // 使用createPinia函数创建一个pinia实例并注册
    4. app.use(createPinia())

    3、配置pinia

    在src下面新建store文件夹并新建index.ts文件,并配置如下:

    1. // 从pinia中引入defineStore函数来定义store
    2. import { defineStore } from "pinia";
    3. // 定义一个store并取名为useStore
    4. // defineStore第一个参数是应用程序中store的唯一标识,也就是在定义其他store时该标识不能相同
    5. // 此处可以类比为java中的实体类,useStore就是类名,state里的属性是成员属性,getters里的函数是getter方法,actions里的函数是setter方法
    6. export const useStore = defineStore("useStore", {
    7. // 定义state
    8. // 推荐使用 完整类型推断的箭头函数
    9. state: () => {
    10. return {
    11. // 所有这些属性都将自动推断其类型
    12. count: 0,
    13. name: "Eduardo",
    14. isAdmin: true,
    15. };
    16. },
    17. // 定义getters,里面定义一些对state值的取值操作
    18. // 指向箭头函数定义的时候所处的对象,而不是其所使用的时候所处的对象,默认指向父级的this
    19. // 普通函数中的this指向它的调用者,如果没有调用者则默认指向window
    20. getters: {
    21. doubleCount: (state) => state.count * 2,
    22. doubleCountOne(state) {
    23. return state.count * 2;
    24. },
    25. doublePlusOne(): number {
    26. return this.count * 2 + 1;
    27. },
    28. },
    29. // 定义actions,里面定义一些对state的赋值操作
    30. actions: {
    31. setCounter(count:number){
    32. this.count = count
    33. }
    34. }
    35. });
    36. // 1、只有一个参数的时候,参数可以不加小括号,没有参数或2个及以上参数的,必须加上小括号
    37. // 2、返回语句只有一条的时候可以不写{}和return,会自动加上return的,返回多条语句时必须加上{}和return
    38. // 3、箭头函数在返回对象的时候必须在对象外面加上小括号
    39. // 在vue中定义函数时,我们尽量都指明函数返回值类型以及参数的数据类型

    4、测试pinia

    1. <script setup lang="ts">
    2. import { useStore } from "@/store/index";
    3. import { storeToRefs } from "pinia"; // 解析store中的数据,如成员属性、方法
    4. // 创建了一个useStore实例对象
    5. const store = useStore();
    6. // 增加成员属性count的值,方式一、直接通过store.count++
    7. // 拿到成员属性count,但这样取值会失去响应性,也就是不能实时同步,当我们点击增加按钮后,虽然操作已经完成,count也增加了,但展示有延迟
    8. // 这个取值过程可能涉及解析数据,从而导致函数执行完后数据没有变化
    9. const count = store.count;
    10. const addCount = () => {
    11. store.count++;
    12. };
    13. // 通过pinia中的storeToRefs函数将store中的数据都进行解析
    14. const count1 = storeToRefs(store).count;
    15. const addCount1 = () => {
    16. store.count++;
    17. };
    18. // 方式二、通过调用store中的函数
    19. const addCount2 = () => {
    20. store.setCounter(++store.count)
    21. };
    22. script>
    23. <style scoped>
    24. .read-the-docs {
    25. color: #888;
    26. }
    27. style>

    五、layout布局

    在配置layout之前,我们还需要对一些标签做初始化的样式设置,比如:html、body等,具体如下

    在项目的index.html文件下添加样式设置

    1. DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8" />
    5. <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    7. <title>Vite + Vue + TStitle>
    8. head>
    9. <body>
    10. <div id="app">div>
    11. <script type="module" src="/src/main.ts">script>
    12. body>
    13. html>
    14. <style lang="less">
    15. html,body,#app {
    16. padding: 0px;
    17. margin: 0px;
    18. height: 100%;
    19. box-sizing: border-box;
    20. }
    21. #app {
    22. width: 100%;
    23. max-width: 100%;
    24. }
    25. style>

    之后在src下新建layout文件夹并新建index.vue文件,配置如下:

    整个el-container为layout布局的整体,其下又可以按照布局的不同划分出不同的区块,但总结起来可以划分为:1、侧边菜单栏;2、头部区;3、内容展示区;4、尾部区,我们根据自己的需要进行选择组合,这些划分出来的区块涉及到不同的配置和处理,因此,我们可以将这些大的区块从layout整体布局中抽离成组件,让代码拥有更好的可读性;此外,每个抽离的组件自己本身也可能存在需要拆分的问题。我们通过拆分,可以很好的将一个问题化繁为简,从而很轻松的解决。

    1. <script setup lang="ts">
    2. // vue3中组件引入后不需要使用conponents注册,可以直接使用
    3. import Header from '@/layout/header/Header.vue'
    4. import Menu from '@/layout/menu/Menu.vue'
    5. script>
    6. <style scoped lang="less">
    7. .container {
    8. height: 100%;
    9. .aside {
    10. background-color: antiquewhite;
    11. }
    12. .header {
    13. background-color: aquamarine;
    14. }
    15. .content {
    16. background-color: pink
    17. }
    18. }
    19. style>

    从layout布局抽离的菜单栏组件:

    1. <script setup lang="ts">
    2. import { ref, reactive } from "vue";
    3. import MenuItem from "@/layout/menu/item/MenuItem.vue";
    4. // 自定义的假的树形菜单数据
    5. // reactive函数用来处理响应式数据,处理的数据一般是复杂类型数据,如对象类型
    6. // ref函数也可以处理响应式数据,不过数据一般是基本数据类型
    7. const isCollapse = ref(false)
    8. const uniqueOpenedFlag = ref(true)
    9. const menuList = reactive([
    10. {
    11. path: "/system",
    12. name: "system",
    13. component: "Layout",
    14. meta: {
    15. title: "系统管理",
    16. icon: "Setting",
    17. roles: ["sys:manage"],
    18. },
    19. children: [
    20. {
    21. path: "/worker",
    22. name: "worker",
    23. component: "Layout",
    24. meta: {
    25. title: "员工管理",
    26. icon: "Setting",
    27. roles: ["sys:manage"],
    28. },
    29. },
    30. {
    31. path: "/happy",
    32. name: "happy",
    33. component: "Layout",
    34. meta: {
    35. title: "菜单管理",
    36. icon: "Setting",
    37. roles: ["sys:manage"],
    38. },
    39. },
    40. ],
    41. },
    42. {
    43. path: "/mail",
    44. name: "mail",
    45. component: "Layout",
    46. meta: {
    47. title: "商场管理",
    48. icon: "Setting",
    49. roles: ["sys:manage"],
    50. },
    51. children: [
    52. {
    53. path: "/worker11",
    54. name: "worker11",
    55. component: "Layout",
    56. meta: {
    57. title: "员工管理22",
    58. icon: "Setting",
    59. roles: ["sys:manage"],
    60. },
    61. },
    62. {
    63. path: "/happy22",
    64. name: "happy22",
    65. component: "Layout",
    66. meta: {
    67. title: "菜单管理22",
    68. icon: "Setting",
    69. roles: ["sys:manage"],
    70. },
    71. },
    72. ],
    73. },
    74. ]);
    75. script>
    76. <style lang="less" scoped>style>

    从菜单栏抽离的菜单项组件:

    1. <script setup lang="ts">
    2. import {
    3. Document,
    4. Menu as IconMenu,
    5. Location,
    6. Setting,
    7. } from "@element-plus/icons-vue";
    8. // 子组件接受父组件传递的数据
    9. // 本组件为子组件,接受父组件传过来的数据,此处定义menuList属性,接受父组件传递的menuList数据
    10. defineProps(["menuList"]);
    11. script>
    12. <style lang="less" scoped>style>

    六、菜单栏logo

    首先,将自己准备的logo图片放到src下的assets文件夹下,然后在layout的menu的logo文件夹下新建MenuLogo.vue文件,并配置如下:

    1. <script setup lang="ts">
    2. import { ref } from "vue";
    3. import Logo from "@/assets/logo.png";
    4. const title = ref("博客管理系统");
    5. script>
    6. <style lang="less" scoped>
    7. .logo {
    8. display: flex; // 弹性布局
    9. width: 100%;
    10. height: 60px;
    11. line-height: 60px;
    12. background-color: rgb(234, 255, 127);
    13. text-align: center;
    14. cursor: pointer; // 鼠标悬浮在元素上时,鼠标从箭头变成小手
    15. align-items: center;
    16. img {
    17. width: 36px;
    18. height: 36px;
    19. margin-left: 20px; // 元素的外边距
    20. margin-right: 12px;
    21. }
    22. .logo-title {
    23. font-weight: 800; // 800为加粗
    24. color: black;
    25. font-size: 20px;
    26. line-height: 60px; // 元素上下居中
    27. font-family: FangSong; // 字体类型
    28. }
    29. }
    30. style>

    最后在菜单栏组件中引入菜单logo组件并使用

    1. // 在script标签中引入
    2. import MenuLogo from "@/layout/menu/logo/MenuLogo.vue";
    3. // el-menu标签上方引入使用
    4. <menu-logo>menu-logo>

    效果如下:

    七、路由和页面联动

    在src的router的index.ts文件下添加如下路由配置并在views文件夹下创建对应的文件

    1. {
    2. path: "/",
    3. component: Layout, // 每个路由都需要通过component指定归属的布局组件
    4. redirect: "/index",
    5. name: "Root",
    6. children: [
    7. {
    8. path: "/index",
    9. name: "Index",
    10. component: () => import("@/views/index/index.vue"),
    11. meta: {
    12. title: "首页看板",
    13. icon: "icon-home",
    14. affix: true,
    15. noKeepAlive: true,
    16. },
    17. },
    18. ],
    19. },
    20. {
    21. path: "/comp",
    22. component: Layout,
    23. name: "Comp",
    24. meta: { title: "系统管理", icon: "icon-code" },
    25. children: [
    26. {
    27. path: "/element",
    28. name: "ElementComp",
    29. component: () => import("@/views/element/index.vue"),
    30. meta: {
    31. title: "菜单管理",
    32. icon: "icon-code",
    33. },
    34. },
    35. {
    36. path: "/iconPark",
    37. name: "IconPark",
    38. component: () => import("@/views/icon/index.vue"),
    39. meta: {
    40. title: "路由管理",
    41. icon: "icon-like",
    42. },
    43. },
    44. {
    45. path: "/chart",
    46. name: "Chart",
    47. component: () => import("@/views/echarts/index.vue"),
    48. meta: {
    49. title: "员工管理",
    50. icon: "icon-chart-line",
    51. },
    52. children: [
    53. {
    54. path: "/line",
    55. name: "Line",
    56. component: () => import("@/views/echarts/line.vue"),
    57. meta: {
    58. title: "商品管理",
    59. },
    60. },
    61. {
    62. path: "/bar",
    63. name: "Bar",
    64. component: () => import("@/views/echarts/bar.vue"),
    65. meta: {
    66. title: "手机管理",
    67. },
    68. },
    69. {
    70. path: "/otherChart",
    71. name: "OtherChart",
    72. component: () => import("@/views/echarts/other.vue"),
    73. meta: {
    74. title: "会员管理",
    75. },
    76. },
    77. ],
    78. },
    79. ],
    80. },
    81. {
    82. path: "/errorPage",
    83. name: "ErrorPage",
    84. component: Layout,
    85. meta: {
    86. title: "用户管理",
    87. icon: "icon-link-cloud-faild",
    88. },
    89. children: [
    90. {
    91. path: "/404Page",
    92. name: "404Page",
    93. component: () => import("@/views/errorPage/404.vue"),
    94. meta: {
    95. title: "角色管理",
    96. icon: "icon-link-cloud-faild",
    97. },
    98. },
    99. {
    100. path: "/401Page",
    101. name: "401Page",
    102. component: () => import("@/views/errorPage/401.vue"),
    103. meta: {
    104. title: "权限管理",
    105. icon: "icon-link-interrupt",
    106. },
    107. },
    108. ],
    109. },

    添加完路由配置之后,创建路由的对应文件并添加一些描述文字,此时虽然路由和对应的页面都已经创建完毕并关联在了一起,但路由并没有被引用,也就无法在正确的位置展示路由页面的数据,所以,我们需要将路由引用到layout布局的main区域,也就是数据展示区,确保当我们访问某个路由时,对应的路由页面能够在该区域展示。

    1、路由和页面联动的注意细节

    在菜单项组件中,我们给菜单项的index属性绑定了路由的path值,其用意是为了启用element-plus中提供的一种在激活导菜单时(当我们点击某个菜单项时,该菜单项就是被激活的菜单)以index作为path进行路由跳转,所以为了我使用这个功能,我们还需要在菜单栏组件的el-menu标签中添加 router 属性以开启该功能,同时再添加 default-active 属性来指明当前被激活的菜单。用例如下

    1. import { useRouter, useRoute } from "vue-router";
    2. // 获取当前点击的路由
    3. const route = useRoute();
    4. // 从路由中获取path
    5. const activeIndex = computed(() => {
    6. const { path } = route;
    7. return path;
    8. });

  • 相关阅读:
    RibbonMainWindow
    Jmeter连接数据库jdbc
    c++ 资源回收学习
    CCF中国开源大会专访|毛晓光:“联合”是开源走向“共赢”的必由之路
    无线耳机哪个音质好?无线入耳式蓝牙耳机音质排行榜
    多目标优化算法:基于非支配排序的霸王龙优化算法(NSTROA)MATLAB
    第九章 哈希表 AcWing 1532. 找硬币
    Web 前端汇总
    【MyBatis-Plus】快速精通Mybatis-plus框架—核心功能
    单机/分布式限流-漏桶/令牌桶/滑动窗口/redis/nginx/sentinel
  • 原文地址:https://blog.csdn.net/python15397/article/details/126439249