目录
一、Vue3环境和项目创建
1、Vue3安装和项目创建
2、Vue3基础和Element-plus
模版语法
生命周期和事件绑定
路由
element-plus
二、登录
1、Login.vue
2、Vuex基础
1) vuex语法结构
2)案例
3)Vuex的读取和更改状态
3、路由守卫和登录
4、登录状态存储和退出登录
三、axios封装和登录完善
1、axios安装
2、service.js封装
3、request.js封装登录接口
4、Login.vue登录代码完善
四、首页
1、index.vue
2、Login.vue登录跳转首页
3、router.js
4、Layout 布局和导航
五、账号管理
1、router.js
2、UserList.vue
3、request.js添加账号管理API
六、角色管理
1、列表、新建、编辑、删除 API
2、角色新增和编辑提交
3、判断弹窗是添加还是编辑
4、编辑角色编辑
5、清除角色表单
6、删除角色
七、商品管理
1、商品查询
2、商品状态转换
一、Vue3环境和项目创建
1、Vue3安装和项目创建
vue/cli初始化项目工程
vue create shop
去掉代码检查
2、Vue3基础和Element-plus
-
模版语法
v-text
{{}}
v-html
v-bind:属性名
v-for
v-if
v-show
-
生命周期和事件绑定
-
路由
history 和hash模式
懒加载
路由跳转 router-to / router-view
-
element-plus
elementIcon引入
npm install @element-plus/icons-vue
// 如果您正在使用CDN引入,请删除下面一行。
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
二、登录
1、Login.vue
<template>
<div class="login_wrap">
<div class="form_wrap">
<el-form ref="ruleFormRef" :model="loginData" label-width="100px" class="demo-ruleForm"
:size="formSize">
<el-form-item label="用户名" prop="username" :rules="[{ required: true, message: '请输入用户名', trigger: 'blur' }]">
<el-input v-model="loginData.username" />
</el-form-item>
<el-form-item label="密码" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
<el-input type="password" v-model="loginData.password" />
</el-form-item>
</el-form>
<div class="display:flex">
<el-button type="primary" class="login_btn">登录</el-button>
</div>
</div>
</div>
</template>
<script>
import { reactive, toRefs } from 'vue';
export default {
name: "login",
setup() {
const data = reactive({
loginData:{
username:"",
password:""
},
rules:[]
})
return {
...toRefs(data)
}
}
}
</script>
<style>
.login_wrap {
width: 100%;
height: 100vh;
background: #726386;
}
.form_wrap {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff;
padding: 30px 50px;
border-radius: 5px;
}
.login_btn{
display: block;
margin: 10px auto;
}
</style>
2、Vuex基础
1) vuex语法结构
2)案例
import { resolve } from 'core-js/fn/promise'
import { createStore } from 'vuex'
export default createStore({
//全局状态初始化
state: {
count:1,
},
//计算state
getters:{
},
//更新状态的方法,更新的唯一方法commit mutations
mutations: {
setCount(state,num){
state.count = num
}
},
//可以异步操作,可以返回promise,更改数据还是传递到mutations去更改
actions: {
setcountPromise(context,num){
return new Promise((resolve,reject)=>{
if(num>100){
reject("值不能大于100")
}else{
context.commit("setCount",num)
resolve
}
})
}
},
//数据比较多,分模块
modules: {
}
})
3)Vuex的读取和更改状态
不分模块
import { createStore } from 'vuex'
export default createStore({
//全局状态初始化
state: {
count:1,
},
//计算state
getters:{
countStatus(state){
return state.count>=1
}
},
//更新状态的方法,更新的唯一方法commit mutations
mutations: {
setCount(state,num){
state.count = num
}
},
//可以异步操作,可以返回promise,更改数据还是传递到mutations去更改
actions: {
setcountPromise(context,num){
return new Promise((resolve,reject)=>{
if(num>100){
reject("值不能大于100")
}else{
context.commit("setCount",num)
resolve
}
})
}
},
//数据比较多,分模块
modules: {
}
})
分模块读取状态
3、路由守卫和登录
router.js
router.beforeEach((to,from,next)=>{
const uInfo = store.state.uInfo.userinfo
if(!uInfo.username){
if(to.path==="/login"){
next()
return
}
//未登录
next("/login")
}else{
next()
}
})
login.vue
const handleLogin = ()=>{
store.commit('setUserInfo',data.loginData)
localStorage.setItem("loginData",JSON.stringify(data.loginData))
router.push({
path:"/user"
})
}
userinfo.state.js
export default{
state:{
userinfo:(localStorage.getItem("loginData")&&JSON.parse(localStorage.getItem("loginData")))||{}
},
mutations:{
setUserInfo(state,uInfo){
state.userinfo= uInfo
}
}
}
4、登录状态存储和退出登录
const loginOut = () => {
localStorage.removeItem("loginData")
store.commit("setUserInfo", {})
router.push(
{
path: "/login"
})
}
三、axios封装和登录完善
1、axios安装
npm install axios --save
2、service.js封装
import axios from "axios"
import { ElLoading, ElMessage } from "element-plus"
import store from "../store/index.js"
let loadingObj = null
const Service = axios.create({
timeout: 5000,
baseURL: 'http://localhost:8080',
headers: {
"Content-type": "application/json;charset=utf-8",
"Authorization":store.state.uInfo.userinfo.token
}
})
//请求拦截
Service.interceptors.request.use(config => {
loadingObj = ElLoading.service({
lock: true,
text: 'loading',
background: 'rgba(0,0,0,0.7)'
})
return config
})
//响应拦截
Service.interceptors.response.use(response => {
console.log(response)
loadingObj.close()
const data = response.data
if (!data.data) {
//请求出错
ElMessage.error(data.meta.msg || "服务器出错")
return data
}
return response.data
}, error => {
loadingObj.close()
ElMessage({
message: "服务器错误",
type: error,
duration: 2000
})
})
//post 请求
export const post = config => {
return Service({
...config,
method: "post",
data: config.data
})
}
//get 请求
export const get = config => {
return Service({
...config,
method: "get",
params: config.data
})
}
//put 请求
export const put = config => {
return Service({
...config,
method: "put",
data: config.data
})
}
//delet 请求
export const del= config => {
return Service({
...config,
method: "delete",
})
}
3、request.js封装登录接口
export const loginApi=data=>{
return post({
url:"/login",
data
})
}
4、Login.vue登录代码完善
const handleLogin = () => {
//请求后台接口
//默认用户:admin/123456
loginApi(data.loginData).then(res => {
if (res.data) {
store.commit('setUserInfo', data.loginData)
localStorage.setItem("loginData", JSON.stringify(data.loginData))
router.push({
path: "/login"
})
}
})
}
四、首页
1、index.vue
<template>
<div>
<h1>欢迎来到用户管理系统</h1>
</div>
</template>
<script>
export default {
name: "首页"
}
</script>
2、Login.vue登录跳转首页
const handleLogin = () => {
//请求后台接口
//默认用户:admin/123456
loginApi(data.loginData).then(res => {
console.log(res)
// if (res.data) {
store.commit('setUserInfo', data.loginData)
localStorage.setItem("loginData", JSON.stringify(data.loginData))
router.push({
path: "/"
})
// }
})
}
3、router.js
{
path: '/',
name: 'layout',
component: LayOut,
redirect: "/index",
children: [
{
path: "/index",
name: "index",
component: () => import("../views/pages/index.vue")
},
]
}
4、Layout 布局和导航
<template>
<div class="common-layout">
<el-container>
<el-header class="common-header flex-float">
<div class="flex">
<img class="logo" src="../../assets/logo.png" alt="">
<h1 class="title">商铺后台管理系统</h1>
</div>
<el-button type="danger" @click="loginOut">退出</el-button>
</el-header>
<el-container>
<el-aside class="common-aside" width="200px">
<el-menu background-color="none" text-color="#fff" :router="true">
<el-sub-menu index="1">
<template #title>
<el-icon>
<location />
</el-icon>
<span>账号管理</span>
</template>
<el-menu-item-group>
<el-menu-item index="/user">账号列表</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<el-icon>
<location />
</el-icon>
<span>角色管理</span>
</template>
<el-menu-item-group>
<el-menu-item index="/roles">角色列表</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
</el-menu>
</el-aside>
<el-main>
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import { useStore } from 'vuex'
import { useRouter } from 'vue-router'
export default {
name: "layout",
setup() {
const store = useStore()
const router = useRouter()
const loginOut = () => {
localStorage.removeItem("loginData")
store.commit("setUserInfo", {})
router.push(
{
path: "/login"
})
}
return {
loginOut
}
}
}
</script>
<style>
.el-container {
height: 100vh;
}
.common-header {
background: rgb(47, 44, 54);
display: flex;
}
.common-aside {
background: rgb(88, 85, 96);
}
.logo {
width: 80px;
}
.title {
color: #fff;
}
.el-main{
background: #efefef;
}
</style>
五、账号管理
用户列表查询/分页、新建用户/编辑用户/表单正则、用户状态更改、删除
1、router.js
2、UserList.vue
<template>
<div>
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>账号列表</el-breadcrumb-item>
</el-breadcrumb>
<div class="page_content">
<div class="flex">
<div class="input_box">
<el-input v-model="keyWord" placeholder="搜索关键字" class="" clearable>
<template #append>
<el-button @click="searchList"><el-icon>
<Search />
</el-icon></el-button>
</template>
</el-input>
</div>
<el-button type="primary" @click="addUser">新建用户</el-button>
</div>
</div>
<!-- 表格 -->
<el-table :data="userList" style="width: 100%">
<el-table-column prop="username" label="姓名" width="180" />
<el-table-column prop="email" label="邮箱" width="180" />
<el-table-column prop="mobile" label="电话" width="180" />
<el-table-column prop="role_name" label="角色" />
<el-table-column prop="mg_state" label="状态">
<template #default="scope">
<el-switch v-model="scope.row.mg_state" @change="switchChange(scope.row)" />
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" @click="editRow(scope.row)">编辑</el-button>
<el-button type="danger" @click="deleteRow(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination background layout="prev, pager, sizes, next, jumper, ->, total"
v-model:page-size="searchParams.pagesize" v-model:currentPage="searchParams.pagenum" :page-sizes="[2, 3, 4, 5]"
@size-change="searchList" @current-change="searchList" :total="total">
</el-pagination>
<!-- 添加弹窗 -->
<el-dialog title="新增用户" v-model="dialogFormVisible">
<el-form :model="formData" :rules="rules" ref="userForm">
<el-form-item label="活动名称" prop="username">
<el-input v-model="formData.username" placeholder="请输入用户名称"></el-input>
</el-form-item>
<el-form-item label="用户密码" prop="password">
<el-input type="password" v-model="formData.password" placeholder="请输入用户密码"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入用户邮箱"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入用户手机号"></el-input>
</el-form-item>
</el-form>
<div class="flex">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="submitForm(userForm)">确 定</el-button>
</div>
</el-dialog>
<!-- 编辑弹窗 -->
<el-dialog title="编辑用户" v-model="dialogFormEVisible">
<el-form :model="formData2" :rules="rules2" ref="userForm2">
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData2.email" placeholder="请输入用户邮箱"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input v-model="formData2.mobile" placeholder="请输入用户手机号"></el-input>
</el-form-item>
</el-form>
<div class="flex">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="submitEForm(userForm2)">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { onMounted, reactive, toRefs, ref } from 'vue';
import { userListApi, userAddApi, userChangeStateApi, userChangeInfoApi, userDeleteApi } from "@/util/request.js"
export default {
name: "users",
setup() {
const data = reactive({
keyWord: "",
searchParams: {
query: "",
pagesize: 5,
pagenum: 1
},
total: 0,
userList: [],
dialogFormVisible: false,
dialogFormEVisible: false,
formData: {
username: '',
password: '',
email: '',
mobile: ''
},
formData2: {
email: '',
mobile: '',
id: ''
},
rules: {
username: [
{ required: "true", message: "此项为必填项", trigger: "blur" },
],
password: [
{ required: "true", message: "此项为必填项", trigger: "blur" },
],
}
})
const searchList = () => {
userListApi(data.searchParams).then(res => {
if (res.data) {
console.log("用户数据", res)
data.userList = res.data.users
data.total = res.data.total
}
})
}
const userForm = ref()
const userForm2 = ref()
onMounted(() => {
{
data.userList = [
{
username: 'Tom',
email: 'Tom@qq.com',
mobile: '234143324',
role_name: '普通操作员'
},
{
username: 'Tom',
email: 'Tom@qq.com',
mobile: '234143324',
role_name: '普通操作员'
},
{
username: 'Tom',
email: 'Tom@qq.com',
mobile: '234143324',
role_name: '普通操作员'
},
{
username: 'Tom',
email: 'Tom@qq.com',
mobile: '234143324',
role_name: '普通操作员'
},
]
}
})
const addUser = () => {
data.dialogFormVisible = true
}
const submitForm = (formEl) => {
formEl.validate(res => {
if (!res) {
return
}
userAddApi(data.formData).then(res => {
if (res.data) {
data.dialogFormVisible = false
data.formData = {
username: '',
password: '',
email: '',
mobile: ''
}
searchList()
}
})
alert("通过")
})
}
const submitEForm = (formEl) => {
formEl.validate(res => {
if (!res) {
return
}
userChangeInfoApi(data.formData2).then(res => {
if (res.data) {
data.dialogFormEVisible = false
searchList()
}
})
})
}
const switchChange = row => {
userChangeStateApi(row).then(res => {
if (res.data) {
searchList()
}
})
}
const editRow = row => {
const { email, mobile, id } = row
data.dialogFormEVisible = true
data.formData2.email = email
data.formData2.mobile = mobile
data.formData2.id = id
}
const deleteRow = row => {
userDeleteApi(row).then(res => {
if (res.data) {
searchList()
}
})
}
return {
...toRefs(data),
searchList,
addUser,
submitForm,
submitEForm,
userForm,
switchChange,
editRow,
userForm2,
deleteRow
}
}
}
</script>
<style scoped>
.input_box {
width: 200px;
margin-right: 15px;
}
</style>
3、request.js添加账号管理API
//获取用户列表
export const userListApi=data=>{
return get({
url:"/users",
data
})
}
//添加用户
export const userAddApi=data=>{
return post({
url:"/users",
data
})
}
//更新状态
export const userChangeStateApi=data=>{
return put({
url:`users/${data.id}/state/${data.mg_state}`,
data
})
}
//更改用户信息
export const userChangeInfoApi=data=>{
return put({
url:`users/${data.id}`,
data
})
}
//删除用户信息
export const userDeleteApi=data=>{
return del({
url:`users/${data.id}`,
})
}
六、角色管理
1、列表、新建、编辑、删除 API
2、角色新增和编辑提交
3、判断弹窗是添加还是编辑
4、编辑角色
5、清除角色表单
6、删除角色
七、商品管理
1、商品查询
request.js
goods.vue
2、商品状态转换