vite-vue3脚手架(参考帝莎编程-后台管理系统开发)
一 创建项目vite-vue3-app
项目原型: http://shopadmin.dishawang.com/#/
vite: https://vitejs.cn/vite3-cn/
# 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/
npm install element-plus
// 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
npm i -D vite-plugin-windicss windicss
// vite.config.js
import WindiCSS from 'vite-plugin-windicss'
export default {
plugins: [
WindiCSS(),
],
}
// main.js
import 'virtual:windi.css'
四 引入VueRouter
npm install vue-router@4
// 新建 router文件夹,在router文件夹下新建index.js
// 1. 定义路由组件.
// 也可以从其他文件导入
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }
// 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` 的缩写
})
// main.js
// 5. 创建并挂载根实例
const app = Vue.createApp({})
//确保 _use_ 路由实例使
//整个应用支持路由。
app.use(router)
app.mount('#app')
// 现在,应用已经启动了!
五 路由配置和404页面捕获
// vite.config.js 配置路径别名
import path from 'path'
export default defineConfig({
resolve:{
alias:{
"~":path.resolve(__dirname,"src")
}
},
plugins: [vue(),WindCSS()]
})
<!-- 在src下新建pages文件夹, 新建Index.vue -->
<template>
index
</template>
<script>
export default {
}
</script>
<style>
</style>
//修改routers/index.js
import Index from '~/pages/Index.vue'
const routes=[{
path:"/",
component:Index
}]
<!-- 修改App.vue -->
<template>
<router-view></router-view>
</template>
依次添加About.vue,404.vue。其中404.vue在ElementPlus的result结果里面可以选一种使用。
六 登录页面
<template>
<el-row class="min-h-screen bg-indigo-500">
<el-col :lg="16" ms="8" class="flex items-center justify-center">
<div>
<div class="font-bold text-5xl text-light-50 mb-4">欢迎光临</div>
<div class="text-gray-200 text-sm">
此站点是(vue3+vite实战商城后台开发)
</div>
</div>
</el-col>
<el-col
:lg="8" ms="8"
class="bg-light-50 flex items-center justify-center flex-col"
>
<h2 class="font-bold text-3xl text-gray-800">欢迎回来</h2>
<div
class="flex items-center justify-center my-5 text-gray-300 space-x-2"
>
<span class="h-[1px] w-16 bg-gray-200"></span>
<span>账号密码登录</span>
<span class="h-[1px] w-16 bg-gray-200"></span>
</div>
<div>
<el-form :model="form" class="w-[250px]">
<el-form-item label="用户名">
<el-input v-model="form.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="密码">
<el-input v-model="form.password" placeholder="请输入密码" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit" class="w-[250px]" round color="#626aef">登录</el-button>
</el-form-item>
</el-form>
</div>
</el-col>
</el-row>
</template>
<script setup>
import { reactive } from "vue";
const form = reactive({
username: "",
password: "",
});
const onSubmit = () => {
console.log("submit!");
};
</script>
<style>
</style>
七 登录页响应式处理
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 响应式栅格数或者栅格属性对象
八 全局引入图标
npm install @element-plus/icons-vue
# 参考:
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
# 部分引入
<template #prefix>
<el-icon class="el-input__icon"><User /></el-icon>
</template>
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进行重构
<template>
<el-row class="login-container">
<el-col :lg="16" ms="8" class="left">
<div>
<div>欢迎光临</div>
<div>此站点是(vue3+vite实战商城后台开发)</div>
</div>
</el-col>
<el-col :lg="8" ms="8" class="right">
<h2 class="title">欢迎回来</h2>
<div>
<span class="line"></span>
<span>账号密码登录</span>
<span class="line"></span>
</div>
<div>
<el-form :model="form" class="w-[250px]">
<el-form-item label="用户名">
<el-input v-model="form.username" placeholder="请输入用户名">
<template #prefix>
<el-icon class="el-input__icon"><User /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="密 码">
<el-input
v-model="form.password"
placeholder="请输入密码"
class="ml-2"
>
<template #prefix>
<el-icon class="el-input__icon"><Lock /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit"
class="w-[250px]"
round
color="#626aef"
>登录</el-button
>
</el-form-item>
</el-form>
</div>
</el-col>
</el-row>
</template>
<script setup>
import { reactive } from "vue";
const form = reactive({
username: "",
password: "",
});
const onSubmit = () => {
console.log("submit!");
};
</script>
<style scoped>
.login-container {
@apply min-h-screen bg-indigo-500;
}
.login-container .left {
@apply flex items-center justify-center;
}
.login-container .right {
@apply bg-light-50 flex items-center justify-center flex-col;
}
.left > div > div:first-child {
@apply font-bold text-5xl text-light-50 mb-4;
}
.left > div > div:last-child {
@apply text-gray-200 text-sm;
}
.right .title {
@apply font-bold text-3xl text-gray-800;
}
.right > div {
@apply flex items-center justify-center my-5 text-gray-300 space-x-2;
}
.right .line {
@apply h-[1px] w-16 bg-gray-200;
}
</style>
十 setup语法糖和组合式api
ref-->基本类型
reactive -->引用类型
十一 登录表单验证处理
# 参考:https://element-plus.gitee.io/zh-CN/component/form.html#%E8%A1%A8%E5%8D%95%E6%A0%A1%E9%AA%8C
十二 引入axios请求库和登录接口交互
接口文档:http://dishaxy.dishait.cn/shopadminapi
http://ceshi13.dishait.cn
# 安装axios
npm install axios
// 在src目录下新建axios.js
import axios from 'axios'
// 创建实例时配置默认值
const service = axios.create({
baseURL: '/api'
});
export default service;
// 修改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/, '')
},
}
},
// 在src目录下新建api目录,新增manager.js
import service from '~/axios'
export default function login(username, password) {
return service.post("/admin/login", {
username,
password
})
}
// 修改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
<!--在Index.vue中进行cookie测试-->
<template>
<div>
后台首页
<el-button @click="set">设置</el-button>
<el-button @click="get">读取</el-button>
<el-button @click="remove">删除</el-button>
</div>
</template>
<script setup>
import { useCookies } from "@vueuse/integrations/useCookies";
const cookies = useCookies();
console.log(cookies);
function set() {
cookies.set("token", "123456");
}
function get() {
console.log(cookies.get("token"));
}
function remove() {
cookies.remove("token");
}
</script>
<style>
</style>
十四 请求拦截器和响应拦截器
// 修改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);
});
十五 常用工具库封装
// 封装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)
}
import { getToken } from "~/composables/auth";
const token = getToken()
//存储token
setToken(res.token);
// 封装提示工具
// 在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状态管理用户信息
# 参考:https://vuex.vuejs.org/zh/
npm install vuex@next --save
// 在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
// 修改main.js
import store from "./store";
app.use(store)
// 修改Login.vue
import { useStore } from "vuex";
const store = useStore();
//获取用户信息
getInfo().then((res2) => {
store.commit("set_userInfo", res2);
console.log(res2);
});
十七 全局路由拦截实现登录判断
参考: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()
})
十八 登录功能完善
// 用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()
})
//回车登录
<el-form-item label="密 码" prop="password">
<el-input
type="password"
v-model="form.password"
placeholder="请输入密码"
class="ml-2"
show-password
@keyup.enter.native="onSubmit"
>
<template #prefix>
<el-icon class="el-input__icon"><Lock /></el-icon>
</template>
</el-input>
</el-form-item>

浙公网安备 33010602011771号