# 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
index
```
```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
欢迎光临
此站点是(vue3+vite实战商城后台开发)
欢迎回来
账号密码登录
登录
```
## 七 登录页响应式处理
```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
欢迎光临
此站点是(vue3+vite实战商城后台开发)
欢迎回来
账号密码登录
登录
```
## 十 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
//回车登录
```