# vite-vue-app **Repository Path**: Ferryup/vite-vue-app ## Basic Information - **Project Name**: vite-vue-app - **Description**: 基于Vue3 + ElementPlus + Vite实战开发商城后台管理系统 - **Primary Language**: HTML/CSS - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2022-11-14 - **Last Updated**: 2025-04-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 一 创建项目vite-vue3-app 项目原型: http://shopadmin.dishawang.com/#/ **vite:** https://vitejs.cn/vite3-cn/ ```bash # npm 7+, extra double-dash is needed: npm create vite@latest vite-vue3-app -- --template vue cd vite-vue3-app npm install npm run dev # 安装依赖 npm install element-plus npm install vue-router@4 npm install pinia npm install axios # npm install ant-design-vue # npm install @ant-design/icons-vue ``` ## 二 引入ElementPlus ElementPlus: https://element-plus.gitee.io/zh-CN/ ```bash npm install element-plus ``` ```bash // main.ts import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' const app = createApp(App) app.use(ElementPlus) app.mount('#app') ``` ## 三 集成 Windi CSS ```bash npm i -D vite-plugin-windicss windicss ``` ```bash // vite.config.js import WindiCSS from 'vite-plugin-windicss' export default { plugins: [ WindiCSS(), ], } ``` ```bash // main.js import 'virtual:windi.css' ``` ## 四 引入VueRouter ```bash npm install vue-router@4 ``` ```js // 新建 router文件夹,在router文件夹下新建index.js // 1. 定义路由组件. // 也可以从其他文件导入 const Home = { template: '
Home
' } const About = { template: '
About
' } // 2. 定义一些路由 // 每个路由都需要映射到一个组件。 // 我们后面再讨论嵌套路由。 const routes = [ { path: '/', component: Home }, { path: '/about', component: About }, ] // 3. 创建路由实例并传递 `routes` 配置 // 你可以在这里输入更多的配置,但我们在这里 // 暂时保持简单 const router = VueRouter.createRouter({ // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。 history: VueRouter.createWebHashHistory(), routes, // `routes: routes` 的缩写 }) ``` ```js // main.js // 5. 创建并挂载根实例 const app = Vue.createApp({}) //确保 _use_ 路由实例使 //整个应用支持路由。 app.use(router) app.mount('#app') // 现在,应用已经启动了! ``` ## 五 路由配置和404页面捕获 ```js // vite.config.js 配置路径别名 import path from 'path' export default defineConfig({ resolve:{ alias:{ "~":path.resolve(__dirname,"src") } }, plugins: [vue(),WindCSS()] }) ``` ```vue ``` ```javascript //修改routers/index.js import Index from '~/pages/Index.vue' const routes=[{ path:"/", component:Index }] ``` ```vue ``` 依次添加About.vue,404.vue。其中404.vue在ElementPlus的result结果里面可以选一种使用。 ## 六 登录页面 ```vue ``` ## 七 登录页响应式处理 ```js https://element-plus.gitee.io/zh-CN/component/layout.html#col-%E5%B1%9E%E6%80%A7 将左边的span="16"改成:lg="16" ms="8" 将右边的span="8"改成:lg="8" ms="8" lg:≥1200px 响应式栅格数或者栅格属性对象 md ≥992px 响应式栅格数或者栅格属性对象 ``` ## 八 全局引入图标 ```bash npm install @element-plus/icons-vue ``` ```bash # 参考: https://element-plus.gitee.io/zh-CN/component/input.html#%E5%B8%A6%E5%9B%BE%E6%A0%87%E7%9A%84%E8%BE%93%E5%85%A5%E6%A1%86 # 部分引入 import { User } from "@element-plus/icons-vue"; # 全局引入 修改main.ts import * as ElementPlusIconsVue from '@element-plus/icons-vue' const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } ``` ## 九 结合@apply实现样式抽离 对Login.vue页面中的css使用@apply进行重构 ```vue ``` ## 十 setup语法糖和组合式api ```bash ref-->基本类型 reactive -->引用类型 ``` ## 十一 登录表单验证处理 ```bash # 参考:https://element-plus.gitee.io/zh-CN/component/form.html#%E8%A1%A8%E5%8D%95%E6%A0%A1%E9%AA%8C ``` ## 十二 引入axios请求库和登录接口交互 ```bash 接口文档:http://dishaxy.dishait.cn/shopadminapi http://ceshi13.dishait.cn # 安装axios npm install axios ``` ```js // 在src目录下新建axios.js import axios from 'axios' // 创建实例时配置默认值 const service = axios.create({ baseURL: '/api' }); export default service; ``` ```javascript // 修改vue.config.js, 设置跨域 // 参考:https://vitejs.cn/vite3-cn/config/server-options.html server: { proxy: { '/api': { target: 'http://ceshi13.dishait.cn', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') }, } }, ``` ```javascript // 在src目录下新建api目录,新增manager.js import service from '~/axios' export default function login(username, password) { return service.post("/admin/login", { username, password }) } ``` ```javascript // 修改Login.vue import login from "~/api/manager"; import { useRouter } from "vue-router"; const router = useRouter(); const onSubmit = () => { formRef.value.validate((valid) => { if (!valid) { return false; } login(form.username, form.password) .then((res) => { console.log(res); //提示成功 ElNotification({ message: res.data.data.msg || "登录成功", type: "success", duration: 1000, }); //存储token //跳转到后台主页 router.push("/"); }) .catch((err) => { ElNotification({ message: err.response.data.msg || "请求失败", type: "error", duration: 3000, }); }); }); }; ``` ## 十三 引入cookie存储用户token ``` npm i @vueuse/integrations npm i universal-cookie ``` ```vue ``` ## 十四 请求拦截器和响应拦截器 ```javascript // 修改axios.js // 添加请求拦截器 service.interceptors.request.use(function (config) { // 在发送请求之前做些什么 const cookies = useCookies(); const token = cookies.get('admin-token') if (token) { config.headers["token"] = token } return config; }, function (error) { // 对请求错误做些什么 return Promise.reject(error); }); // 添加响应拦截器 service.interceptors.response.use(function (response) { // 2xx 范围内的状态码都会触发该函数。 // 对响应数据做点什么 return response.data.data; }, function (error) { // 超出 2xx 范围的状态码都会触发该函数。 // 对响应错误做点什么 ElNotification({ message: error.response.data.msg || "请求失败", type: "error", duration: 3000, }); return Promise.reject(error); }); ``` ## 十五 常用工具库封装 ```javascript // 封装cookie工具 // 在src目录下新建composables目录,新建auth.js import { useCookies } from "@vueuse/integrations/useCookies"; const TOKEN_KEY = "admin-token" const cookies = useCookies() //获取token export function getToken() { return cookies.get(TOKEN_KEY) } //设置token export function setToken(token) { return cookies.set(TOKEN_KEY,token) } //删除tokenexport function removeToken() { return cookies.remove(TOKEN_KEY) } ``` ```javascript import { getToken } from "~/composables/auth"; const token = getToken() //存储token setToken(res.token); ``` ```javascript // 封装提示工具 // 在src目录下新建composables目录,新建util.js import { ElNotification } from "element-plus"; // ElementPlus消息提示 export function toast(message,type='success',dangerouslyUseHTMLString=false){ //提示成功 ElNotification({ message, type, duration: 2000, }); } //提示成功 toast("登录成功"); //提示失败 toast(error.response.data.msg || "请求失败",'error') ``` ## 十六 引入vuex状态管理用户信息 ```bash # 参考:https://vuex.vuejs.org/zh/ npm install vuex@next --save ``` ```javascript // 在src目录下新建store目录,新建index.js import { createStore } from 'vuex' // 创建一个新的 store 实例 const store = createStore({ state() { return { // 用户信息 user: {} } }, mutations: { // 记录用户信息 set_userInfo(state, user) { state.user = user } } }) export default store ``` ```javascript // 修改main.js import store from "./store"; app.use(store) ``` ```javascript // 修改Login.vue import { useStore } from "vuex"; const store = useStore(); //获取用户信息 getInfo().then((res2) => { store.commit("set_userInfo", res2); console.log(res2); }); ``` ## 十七 全局路由拦截实现登录判断 ```javascript 参考:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD%AE%E5%AE%88%E5%8D%AB // 在src目录下新建permission.js import router from '~/router' import { getToken } from '~/composables/auth' import { toast } from '~/composables/util' //全局前置守卫 router.beforeEach((to, from, next) => { const token = getToken() //没有登录,强制跳转回登录页码 if (!token && to.path != '/login') { toast('请先登录', 'error') return next({ path: '/login' }) } //防止重复登录 if (token && to.path == '/login') { toast('请勿重复登录', 'error') return next({ path: from.path ? from.path : '/' }) } next() }) ``` ## 十八 登录功能完善 ```js // 用vuex保存用户信息 // 修改store/index.js import { getInfo } from "~/api/manager"; actions:{ //获取当前登录用户信息 getInfo({commit}){ return new Promise((resolve,reject)=>{ getInfo() .then(res=>{ commit('set_userInfo',res) resolve(res) }) .catch(err=>reject(err)) }) } } // 修改permission.js import router from '~/router' import { getToken } from '~/composables/auth' import { toast } from '~/composables/util' import store from '~/store' //全局前置守卫 router.beforeEach(async (to, from, next) => { const token = getToken() //没有登录,强制跳转回登录页码 if (!token && to.path != '/login') { toast('请先登录', 'error') return next({ path: '/login' }) } //防止重复登录 if (token && to.path == '/login') { toast('请勿重复登录', 'error') return next({ path: from.path ? from.path : '/' }) } //如果用户登录了,自动获取用户信息并存储在vuex中 if (token) { await store.dispatch('getInfo') } next() }) ``` ```vue //回车登录 ```