1.创建相关应用模版请参考链接:去中心化的前端构建工具 — Vite
2.完成创建后就可以在github中查看到新增的Vite仓库

git clone + 项目地址
cd Vite
git checkout feature/1.0.0
npm install
npm run dev
这里打开浏览器3000端口,并出现默认页面。
npm i vue-router@next -S

import { createRouter, createWebHashHistory } from 'vue-router';const router = createRouter({history: createWebHashHistory(),routes: [{ path: '/', component: () => import('views/home.vue') }]});export default router
import router from "@/router";createApp(App).use(router).mount("#app");
npm i vuex@next -S

import {createStore} from 'vuex';export default createStore({state: {couter: 0}});
import store from "@/store";createApp(App).use(store).mount("#app");
npm i sass -D
styles 目录保存各种样式

index.scss 作为出口组织这些样式,同时编写一些全局样式

最后在main.js导入
npm i element3 -S
import element3 from "element3";import "element3/lib/theme-chalk/index.css";createApp(App).use(element3)
import "element3/lib/theme-chalk/button.css";import { ElButton } from "element3"createApp(App).use(ElButton)
抽取成插件会更好 plugins/element3.js
// 完整引入import element3 from "element3";import "element3/lib/theme-chalk/index.css";// 按需引入// import { ElButton } from "element3";// import "element3/lib/theme-chalk/button.css";export default function (app) {// 完整引入app.use(element3)// 按需引入// app.use(ElButton);}
<el-button>my button</el-button>
我们应用需要一个基本布局页,类似下图,将来每个页面以布局页为父页面即可:

<template><div class="app-wrapper"><!-- 侧边栏 --><div class="sidebar-container"></div><!-- 内容容器 --><div class="main-container"><!-- 顶部导航栏 --><navbar /><!-- 内容区 --><app-main /></div></div></template><script setup>import AppMain from "./components/AppMain.vue";import Navbar from "./components/Navbar.vue";</script><style lang="scss" scoped>@import "../styles/mixin.scss";.app-wrapper {@include clearfix;position: relative;height: 100%;width: 100%;}</style>
{path: "/",component: Layout,children: [{path: "",component: () => import('views/home.vue'),name: "Home",meta: { title: "首页", icon: "el-icon-s-home" },},],},
根据路由表动态生成侧边导航菜单。

首先创建侧边栏组件,递归输出routes中的配置为多级菜单,layout/Sidebar/index.vue
<template><el-scrollbar wrap-class="scrollbar-wrapper"><el-menu:default-active="activeMenu":background-color="variables.menuBg":text-color="variables.menuText":unique-opened="false":active-text-color="variables.menuActiveText"mode="vertical"><sidebar-itemv-for="route in routes":key="route.path":item="route":base-path="route.path"/></el-menu></el-scrollbar></template><script setup>import SidebarItem from "./SidebarItem.vue";import { computed } from "vue";import { useRoute } from "vue-router";import { routes } from "@/router";import variables from "styles/variables.module.scss";const activeMenu = computed(() => {const route = useRoute();const { meta, path } = route;if (meta.activeMenu) {return meta.activeMenu;}return path;});</script>
添加相关样式:
○ styles/variables.module.scss
○ styles/sidebar.scss
○ styles/index.scss中引入
创建SidebarItem.vue组件,解析当前路由是导航链接还是父菜单:

通过路由匹配数组可以动态生成面包屑。
面包屑组件,layouts/components/Breadcrumb.vue
<template><el-breadcrumb class="app-breadcrumb" separator="/"><el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path"><spanv-if="item.redirect === 'noRedirect' || index == levelList.length - 1"class="no-redirect">{{ item.meta.title }}</span><a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a></el-breadcrumb-item></el-breadcrumb></template><script setup>import { compile } from "path-to-regexp";import { reactive, ref, watch } from "vue";import { useRoute, useRouter } from "vue-router";const levelList = ref(null);const router = useRouter();const route = useRoute();const getBreadcrumb = () => {let matched = route.matched.filter((item) => item.meta && item.meta.title);const first = matched[0];if (first.path !== "/") {matched = [{ path: "/home", meta: { title: "首页" } }].concat(matched);}levelList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false);}const pathCompile = (path) => {var toPath = compile(path);return toPath(route.params);}const handleLink = (item) => {const { redirect, path } = item;if (redirect) {router.push(redirect);return;}router.push(pathCompile(path));}getBreadcrumb();watch(route, getBreadcrumb)</script><style lang="scss" scoped>.app-breadcrumb.el-breadcrumb {display: inline-block;font-size: 14px;line-height: 50px;margin-left: 8px;.no-redirect {color: #97a8be;cursor: text;}}</style>
统一封装数据请求服务,有利于解决一下问题:
准备工作:
npm i axios -S
添加配置文件:.env.development
VITE_BASE_API=/api
请求封装 utils/request.js
import axios from "axios";import { Message, Msgbox } from "element3";// 创建axios实例const service = axios.create({// 在请求地址前面加上baseURLbaseURL: import.meta.env.VITE_BASE_API,// 当发送跨域请求时携带cookie// withCredentials: true,timeout: 5000,});// 请求拦截service.interceptors.request.use((config) => {// 模拟指定请求令牌config.headers["X-Token"] = "my token";return config;},(error) => {// 请求错误的统一处理console.log(error); // for debugreturn Promise.reject(error);});// 响应拦截器service.interceptors.response.use(/*** 通过判断状态码统一处理响应,根据情况修改* 同时也可以通过HTTP状态码判断请求结果*/(response) => {const res = response.data;// 如果状态码不是20000则认为有错误if (res.code !== 20000) {Message.error({message: res.message || "Error",duration: 5 * 1000,});// 50008: 非法令牌; 50012: 其他客户端已登入; 50014: 令牌过期;if (res.code === 50008 || res.code === 50012 || res.code === 50014) {// 重新登录Msgbox.confirm("您已登出, 请重新登录", "确认", {confirmButtonText: "重新登录",cancelButtonText: "取消",type: "warning",}).then(() => {store.dispatch("user/resetToken").then(() => {location.reload();});});}return Promise.reject(new Error(res.message || "Error"));} else {return res;}},(error) => {console.log("err" + error); // for debugMessage({message: error.message,type: "error",duration: 5 * 1000,});return Promise.reject(error);});export default service;
使用el-table展示结构化数据,配合el-pagination做数据分页。

文件组织结构如下:list.vue展示列表,edit.vue和create.vue编辑或创建,内部复用detail.vue处理,model中负责数据业务处理。

list.vue中的数据展示
<el-table v-loading="loading" :data="list"><el-table-column label="ID" prop="id"></el-table-column><el-table-column label="账户名" prop="name"></el-table-column><el-table-column label="年龄" prop="age"></el-table-column></el-table>
list和loading数据的获取逻辑,可以使用compsition-api提取到userModel.js
export function useList() {// 列表数据const state = reactive({loading: true, // 加载状态list: [], // 列表数据});// 获取列表function getList() {state.loading = true;return request({url: "/getUsers",method: "get",}).then(({ data, total }) => {// 设置列表数据state.list = data;}).finally(() => {state.loading = false;});}// 首次获取数据getList();return { state, getList };}
list.vue中使用
import { useList } from "./model/userModel";
const { state, getList } = useList();
分页处理 list.vue
<pagination:total="total"v-model:page="listQuery.page"v-model:limit="listQuery.limit"@pagination="getList"></pagination>
数据也在userModel中处理
const state = reactive({total: 0, // 总条数listQuery: {// 分页查询参数page: 1, // 当前页码limit: 5, // 每页条数},});
request({url: "/getUsers",method: "get",params: state.listQuery, // 在查询中加入分页参数})
用户数据新增、编辑使用el-form处理
可用一个组件detail.vue来处理,区别仅在于初始化时是否获取信息回填到表单。
<el-form ref="form" :model="model" :rules="rules"><el-form-item prop="name" label="用户名"><el-input v-model="model.name"></el-input></el-form-item><el-form-item prop="age" label="用户年龄"><el-input v-model.number="model.age"></el-input></el-form-item><el-form-item><el-button @click="submitForm" type="primary">提交</el-button></el-form-item></el-form>
数据处理同样可以提取到userModel中处理。
export function useItem(isEdit, id) {const model = ref(Object.assign({}, defaultData));// 初始化时,根据isEdit判定是否需要获取详情onMounted(() => {if (isEdit && id) {// 获取详情request({url: "/getUser",method: "get",params: { id },}).then(({ data }) => {model.value = data;});}});return { model };}
git add .git commit -m '添加你的注释'git push
一键进行应用部署。在应用详情页面点击日常环境的「部署」按钮进行一键部署,部署状态变成绿色已部署以后可以点击访问部署网站查看效果。





一键创建Vite应用模版链接 :https://workbench.aliyun.com/application/front/create?fromConfig=27&fromRepo=sol_github_27
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号