vue3+axios+elementPlus+vue-router完成单页面demo

项目用VUE3+vuex+axios+elementPlus完成了基础的单页面程序,暂时没有登录,权限等模块。

  • npm 6.0以上
  • node 20.0以上
  • vue cli 3.0以上

先上干货git地址:

demo地址:vue3

创建项目

(这个网上有很多例子,就不细说了)

项目目录结构

项目目录结构如下图所示(这个图是项目搭建完成后的图,项目刚创建完的话,有些文件是没有的(vue.config,api.lib,config这些文件刚创建的项目应该都没有)):
在这里插入图片描述

安装各个插件(如果在创建项目时选择了安装这些插件,直接看怎么配置即可)

安装elementPlus

npm install element-plus --save

全局引入elementPlus配置

在main.js中

import ElementPlus from 'element-plus';
import 'element-plus/lib/theme-chalk/index.css';

const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

参考链接:elementPlus官网

安装axios和vue-axios

vue-axios是一个vue整合axios的工具

npm install --save axios vue-axios

//在main.js中
.use(VueAxios, axios)

参考链接:http://www.axios-js.com/zh-cn/docs/vue-axios.html

安装预编译css插件Less

npm install --save less less-loader

main.js如下

import { createApp } from 'vue';
import App from './App.vue';
import router from './router/index';
import store from './store';
import ElementPlus from 'element-plus';             //引入elementPLus
import 'element-plus/lib/theme-chalk/index.css';    //elementPlus的css
import axios from 'axios';                          //axios请求插件
import VueAxios from 'vue-axios';                   //可以让vue直接使用(use)axios的小插件
import config from '@/config';        				//配置后端请求地址的文件

//创建app
const app = createApp(App);
app.config.globalProperties = config;				//后端请求地址
app.use(store)
    .use(router)
    .use(ElementPlus)
    .use(VueAxios, axios)
    .mount('#app');
export default app

后端请求地址config.js

其实这个js有用的只有baseUrl


export default {
    /**
     * @description 配置显示在浏览器标签的title
     */
    // title: '测试',

  /**
   * @description api请求基础路径
   */
  baseUrl: {
     dev: 'http://localhost:8080/', // 我的主机

     pro: '/'//线上服务器地址
  },
  /**
   * @description 默认打开的首页的路由name值,默认为home
   */
  // homeName: 'home',
}

配置公共css、less的全局引用,配置@指向

配置后可以全局使用特定文件夹里面的css、less。
vue.config.js里面(这里是整个文件的js):

const path = require('path');
const resolve = dir => {
    return path.join(__dirname, dir)
};
/**
 *默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,
 *例如 https://www.my-app.com/。
 *如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。
 *如果你的应用被部署在 https://www.my-app.com/my-app/,
 *则设置 publicPath 为 /my-app/。
 *这个值也可以被设置为空字符串 ('') 或是相对路径 ('./'),这样所有的资源都会被链接为相对路径,
 *这样打出来的包可以被部署在任意路径,也可以用在类似 Cordova hybrid 应用的文件系统中。
 */
const PUBLIC_PATH = process.env.NODE_ENV === 'production' ? '/' : '/';
/**
 * 引入全局的less文件
 * @param {*} rule
 */
const addStyleResource = (rule) => {
    rule.use('style-resource')
        .loader('style-resources-loader')
        .options({
            patterns: [
                // 需要全局导入的less,这个地方一点要写./src才行,否则会报错...
                path.resolve(__dirname, './src/assets/css/publicCss.less')
            ]
        });
};
module.exports = {
    // 选项...
    publicPath:PUBLIC_PATH,
    chainWebpack: config => {
        const types = ['vue-modules', 'vue', 'normal-modules', 'normal'];
        types.forEach(type => addStyleResource(config.module.rule('less').oneOf(type))); //轮询加载全局样式
        config.resolve.alias
            //配置@指向src
            .set('@', resolve('src')) // key,value自行定义,比如.set('@@', resolve('src/components'))
            //配置_c指向src
            .set('_c', resolve('src/components'))

    },
    devServer: {
        overlay: {
            warnings: true,
            errors: true
        }
    },
    css: {
        loaderOptions: {
            less: {
                javascriptEnabled: true
            }
        }
    }
};

设置请求拦截和响应拦截

我的文件名是axios.js

import axios from "axios";
import app from '@/main';

// 退出登录
const logout = () => {
    app.config.globalProperties.$router.push({name: 'login'});
    clearAllSessionStorage()
};

// 清空sessionStorage
const clearAllSessionStorage = () => {
    sessionStorage.removeItem('token')
}

// 响应拦截
const respFilter = res => {
    // elementPlus的信息提示
    let message = app.config.globalProperties.$message;
    let msg = {"content": res.data.message, "duration": 5};
    let code = res.data.code;
    let method = res.config.method;
    if (method) {
        method = method.toLowerCase();
    }
    if ((method !== 'options') && code === 'timeout') {
        logout()
    } else if (code === 'Unauthorized') {
        message.error(msg);
    } else if (code === 'system_error') {
        message.error(msg);
    }
    if (res.data.success === false) {
        message.error(msg);
    }
};

class HttpRequest {
    constructor(baseUrl = baseURL) {
        this.baseUrl = baseUrl;
        this.queue = {}
    }

    getInsideConfig() {
        return {
            baseURL: this.baseUrl,
            headers: {
                token: localStorage.getItem('token') || '',
            }
        }
    }

    interceptors(instance) {
        // 请求拦截
        instance.interceptors.request.use(config => {
            return config
        }, error => {
            return Promise.reject(error)
        });
        // 响应拦截
        instance.interceptors.response.use(
            res => {
                respFilter(res);
                const {data, status, headers} = res;
                return {data, status, headers}
            },
            error => {
                return Promise.reject(error)
            })
    }

    request(options) {
        const instance = axios.create();
        options = Object.assign(this.getInsideConfig(), options);
        this.interceptors(instance, options.url);
        return instance(options)
    }
}

export default HttpRequest

使用拦截器

文件名request.js

import HttpRequest from './axios';
import config from '@/config';
const baseUrl = process.env.NODE_ENV === 'development' ? config.baseUrl.dev : config.baseUrl.pro;
const axios = new HttpRequest(baseUrl);
export default axios

vuex的分模块使用

src/store/index.js

import { createStore } from 'vuex'
import user from './module/user'

export default createStore({
  state: {

  },
  mutations: {

  },
  actions: {

  },
  modules: {
      user,
  }
})

src/store/module/user.js

import {login} from "@/api/user";

export default {
    state:{

    },
    mutations:{

    },
    actions:{
         userLogin({commit},data){
            return new Promise((resolve, reject) => {
                login(data).then(res => resolve(res)).catch(error => reject(error))
            })
        }
    }
}

src/api/user.js

import axios from '@/lib/request'

export const login = (data) =>{
    return axios.request({
        url: 'login',
        data: data,
        method: 'post'
    })
}

发起一次请求

以登陆为例

  1. 在vue组件中:
<template>
    <el-button @click="login">login</el-button>
</template>

<script>
    import {reactive} from 'vue'
    import {useStore} from 'vuex';
    export default {
        name: 'Home',
        setup() {
            let store = useStore();
            const data = reactive({
                userName: '张三',
            });
            const login = () => {
                console.log(store);
                store.dispatch('userLogin', {name: data.userName, sex: '男'}).then(res => {
                    console.log(res);
                })
            };
            return {
                login,
            }
        }
    }
</script>

  1. src/store/module/user.js代码在上面
  2. src/api/user.js代码在上面

利用vue-router 和elementPlus的Container、NavMenu完成单页面

src/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
import Routes from '@/router/routers';
// 导入routers的路由
const routes = Routes;
// 创建初始化router
const router = createRouter({
    /**
     * hash模式:createWebHashHistory,
     * history模式:createWebHistory
     */
  history: createWebHistory(),
  routes:routes,
});

//配置导航守卫
router.beforeEach((to,from,next) => {
    //如果访问的是登录页,直接放行
    if(to.path === '/login') return next();
    // 获取token
    // const userInfo = sessionStorage.getItem('token');
    const userInfo = '565432';
    // 判断token是否存在,如果不存在返回登录页,存在就允许跳转
    if(!userInfo) return next('/login');
    next()
});

export default router

src/router/routers/index.js

import main from '@/components/index.vue'
import view from '@/components/menus/menu.vue'
/**
 *
 * 路由配置规则:
 * 一级菜单的component必须为 main||() => import('@/components/index.vue')
 * {
 *  path:'',路径
 *  name:'',路由名称,生成menu时menu显示的name
 *  breadcrumb: boolean值,true是可以点击面包屑跳转到对应页面,false||不写||null||unfinde不跳转
 *  ----------  ps:一般父级菜单项都不跳转
 *  redirect:'', 路由转发,可以转发到另外一个页面。
 *  -------------例如:访问 / 会转发到home再转发到 /home/index,/home/index才是需要显示的首页
 *  isShow:'', 是否显示为菜单,为TRUE时为菜单,false不是菜单不会在导航栏显示
 *  icon:'',图标,显示在名称前面的图标
 *  children: [], 子路由,menu中的子menu 没有时可为空数组或者不填,
 *  -----------  当一个菜单有三级或者以上的时候需要在他的父级使用:
 *  -----------  component:view||component: () => import('@/components/menus/menu.vue'),这个属性才能显示出多子级菜单
 * }
 *
 */
const Routes = [
    {
        path: '/',
        redirect: '/home',
        isShow: false
    },
    {
        path: '/login',
        name: '登录',
        isShow: false,
        component: () => import('@/components/login/login.vue'),
    },
    {
        path: '/home',
        name: '首页',
        isShow: false,
        component:main,
        breadcrumb:true,
        redirect:'/home/index',
        children:[
            {
                path: '/home/index',
                isShow: false,
                icon: 'el-icon-s-home',
                component: () => import('@/views/home.vue'),
            },
        ]
    },
    {
        path: '/system',
        name: '系统管理',
        isShow: true,
        icon: 'el-icon-s-home',
        breadcrumb:false,
        component:main,
        children:[
            {
                path: 'user',
                name: '用户管理',
                isShow: true,
                icon: 'el-icon-user',
                component:view,
                children:[
                    {
                        path: '/system/user/home',
                        name: '添加用户',
                        isShow: true,
                        icon: 'el-icon-user',
                        breadcrumb:true,
                        component: () => import('@/views/system/Home.vue')
                    },
                    {
                        path: '/system/user/user',
                        name: '用户列表',
                        isShow: true,
                        icon: 'el-icon-user',
                        breadcrumb:true,
                        component: () => import('@/views/system/user.vue')
                    }
                ]
            },
            {
                path: '/system/doc',
                name: '文档',
                isShow: true,
                breadcrumb:true,
                icon: 'el-icon-s-order',
                component: () => import('@/views/system/About.vue'),
            },
        ]
    },
    {
        path: '/chart',
        name: '图表',
        isShow: true,
        breadcrumb:true,
        icon: 'el-icon-s-order',
        component:main,
        children:[
            {
                path: '/chart/echarts',
                name: '柱状图',
                isShow: true,
                breadcrumb:true,
                icon: 'el-icon-s-order',
                component:() => import('@/views/system/echarts.vue')
            }
        ]
    },
];
export default Routes;

app.vue中添加

<router-view></router-view>

src/components/index.vue文件(页面布局文件)

<template>
  <div>
    <el-container>
<!--        左侧导航栏-->
      <el-aside width="200px">
<!--          左侧导航栏组件-->
          <Sidebar></Sidebar>
      </el-aside>
      <el-container>
<!--          头部-->
        <el-header style="background-color: blue">
<!--            头部组件-->
          <Header></Header>
        </el-header>
<!--          主体-->
        <el-main>
<!--            路由 占位符-->
          <Menu></Menu>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
    import Header from '@/components/Header/index.vue'
    import Sidebar from '@/components/Sidebar/index.vue'
    import Menu from '@/components/menus/menu.vue'
    export default {
        setup() {

        },
        components: {
            Sidebar,
            Header,
            Menu
        }
    };
</script>
<style lang="less" scoped>
</style>

左侧导航采用递归展示菜单
src/components/Sidebar/index.vue

<template>
<!--    导航栏的顶部-->
  <el-affix class="el-affix-Cursor" :z-index="1200" style="" @click="gotoHome">
    <div>
      <span>vue3练习</span>
    </div>
  </el-affix>
  <el-menu
    :default-active="route.path"
    class="el-menu-vertical-demo"
    active-text-color="#ffd04b"
    background-color="#545c64"
    text-color="#fff"
    :unique-opened="true"
    router>
<!--      导航组件-->
      <Sidebar :routerList="routers"></Sidebar>
  </el-menu>
</template>

<script>
    import Sidebar from '@/components/Sidebar/sidebar.vue'
    import {reactive, onMounted, toRefs} from 'vue'
    import {useRouter,useRoute} from 'vue-router'
    export default {
        setup() {
            const routers = useRouter();
            const data = reactive({
                routers:routers.options.routes,
                route: useRoute(),
            });
            const gotoHome = () => {
                routers.push({
                    path:'/home'
                });
            };
            const handleOpen = (key, keyPath) => {
                console.log(key, keyPath);
            };
            onMounted(() => {

                },
            );
            return {
                handleOpen,
                gotoHome,
                ...toRefs(data)
            };
        },
        components:{
            Sidebar
        }
    };
</script>
<style lang="less" scoped>
  .el-affix{
    display: flex;
    justify-content: center;
    align-items: center;
    height: 60px;
    background-color: cadetblue;
    cursor: pointer;

  }
</style>

src/components/Sidebar/sidebar.vue

<template>
    <!--    递归显示导航菜单-->
    <div v-for="(item) in routerArray" :key="item.path">
        <!--只有一级菜单-->
        <el-menu-item v-if="!item.children && item.isShow" :index="item.path">
            <!--图标-->
            <i :class="item.icon"></i>
            <!--标题-->
            <span>{{item.name}}</span>
        </el-menu-item>
        <!--有多级菜单-->
        <el-submenu v-if="item.children && item.isShow" :index="item.path">
            <template #title>
                <i :class="item.icon"></i>
                <span> {{item.name}}</span>
            </template>
            <!--递归组件,把遍历的值传回子组件,完成递归调用-->
            <Sidebar :routerList="item.children"></Sidebar>
        </el-submenu>
    </div>
</template>

<script>
    import {reactive, onBeforeMount, toRefs} from 'vue'

    export default {
        name: "sidebar",
        props: {
            routerList: {
                type: Array,
            },
        },
        setup(props) {
            const data = reactive({
                routerArray: props.routerList
            });
            onBeforeMount(() => {

            });
            return {
                ...toRefs(data)
            }
        }
    }
</script>

<style lang="less" scoped>

</style>

到这项目基本就搭建完成了,缺少的主题内容和头部内容随便创建个组件就可以了(当然最主要的是它太长了QAQ),以后可能会拆分。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用Vue3 + TypeScript + Vite + Element Plus + Router + Axios搭建前端项目框架的步骤: 1. 首先,确保你已经安装了Node.js和npm。你可以在命令行中运行以下命令来检查它们的版本: ```shell node -v npm -v ``` 2. 创建一个新的项目文件夹,并在该文件夹中打开命令行。 3. 在命令行中运行以下命令来初始化一个新的Vite项目: ```shell npm init vite ``` 在初始化过程中,你需要选择Vue作为模板,选择TypeScript作为语言,并填写项目名称。 4. 进入项目文件夹,并安装所需的依赖: ```shell cd your-project-name npm install ``` 5. 安装Vue Router、Vuex和Axios: ```shell npm install vue-router@next vuex@next axios ``` 6. 在项目文件夹中创建一个新的文件夹,用于存放页面组件和路由配置文件。 7. 在src文件夹中创建一个新的文件夹,用于存放页面组件。 8. 在src文件夹中创建一个新的文件夹,用于存放路由配置文件。 9. 在src/router文件夹中创建一个新的文件,命名为index.ts,并在其中编写路由配置: ```typescript import { createRouter, createWebHistory } from 'vue-router'; import Home from '../views/Home.vue'; const routes = [ { path: '/', name: 'Home', component: Home, }, // 添加其他页面的路由配置 ]; const router = createRouter({ history: createWebHistory(), routes, }); export default router; ``` 10. 在src/main.ts文件中导入并使用Vue Router: ```typescript import { createApp } from 'vue'; import App from './App.vue'; import router from './router'; createApp(App).use(router).mount('#app'); ``` 11. 在src/views文件夹中创建一个新的文件,命名为Home.vue,并在其中编写一个简页面组件: ```vue <template> <div> <h1>Welcome to Home Page</h1> </div> </template> <script> export default { name: 'Home', }; </script> ``` 12.src/App.vue文件中添加一个路由出口,用于显示组件: ```vue <template> <div id="app"> <router-view></router-view> </div> </template> <script> export default { name: 'App', }; </script> ``` 13. 在src/main.ts文件中导入并使用Element Plus: ```typescript import { createApp } from 'vue'; import App from './App.vue'; import router from './router'; import ElementPlus from 'element-plus'; import 'element-plus/lib/theme-chalk/index.css'; createApp(App).use(router).use(ElementPlus).mount('#app'); ``` 14. 运行以下命令来启动开发服务器: ```shell npm run dev ``` 15. 打开浏览器,并访问http://localhost:3000,你将看到一个简页面,其中包含"Welcome to Home Page"的文本。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值