Vue速成--项目实战(后台管理系统)
需要后端服务器资料的童鞋,私信留言哦~
umall 后台管理
1.创建项目
vue init webpack umall
2.清空工作
1.assets 清空
2.components 清空
3.router/index.js 删除helloword相关的
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
]
})
4.App.vue重置
3.项目搭建
1.目录结构
-src
-assets 静态资源
-css
-js
-components 公共组件
index.js 整合公共组件
-filters 过滤器
index.js 整合过滤器
-pages 路由组件
-home 某个路由组件
home.vue 路由组件
-components 路由组件的子组件
banner.vue
list.vue
-router 路由
index.js
-store 仓库
index.js 创建仓库并导出
mutations.js 根级别下 的state mutations getters
acions.js 根级别下的actions
-modules 模块
-utils 工具类
alert.js 弹框
request.js 数据交互
App.vue 根组件
main.js 入口文件
2.assets
css /reset.css,在main.js引入
//1.assets
import "./assets/css/reset.css"
3.公共组件 components
components/index.js
import Vue from "vue"
let obj={
}
for(let i in obj){
Vue.component(i,obj[i])
}
4.路由组件的格式
-pages
-home
home.vue
-components
banner.vue
list.vue
-detail
detail.vue
-components
picker.vue
5.数据交互
1.安装依赖包
npm i axios qs --save
2.配置代理 config/index.js
proxyTable: {
"/api":{
target:"http://localhost:3000",
changeOrigin:true,
pathRewrite:{
"^/api":"http://localhost:3000"
}
}
},
3.utils/request.js
import axios from "axios"
import qs from "qs"
let baseUrl="/api";
//响应拦截
axios.interceptors.response.use(res=>{
console.group("=====本次请求路径是:"+res.config.url)
console.log(res);
console.groupEnd()
return res;
})
6.过滤器filters
filters/index.js
import Vue from "vue"
let obj={
}
for(let i in obj){
Vue.filter(i,obj[i])
}
7.vuex
1.安装依赖包
npm i vuex --save
2.目录搭建
-store 仓库
index.js 创建仓库并导出
mutations.js 根级别下 的state mutations getters
acions.js 根级别下的actions
-modules 模块
3.store/index.js
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
import {state,getters,mutations} from "./mutations"
import {actions} from "./actions"
export default new Vuex.Store({
state,
getters,
mutations,
actions,
modules:{
}
})
store/mutations.js
export const state={}
export const mutations={}
export const getters={}
store/actions.js
export const actions={}
4.main.js引入store,并挂到根实例上
//6.vuex
import store from "./store"
new Vue({
el: '#app',
store,
})
8.element-ui
1.安装依赖包
npm i element-ui --save
2.main.js引入
//7.element-ui
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
3.二次封装弹框 utils/alert.js
import Vue from "vue"
let vm = new Vue()
export const successAlert = (msg) => {
vm.$message({
message: msg,
type: 'success'
});
}
export const warningAlert = (msg) => {
vm.$message({
message: msg,
type: 'warning'
});
}
4.配置一级路由
1.pages下创建了index 和login组件
2.router/index.js 配置路由规则
export default new Router({
routes: [
{
path:"/login",
component:()=>import("../pages/login/login.vue")
},
{
path:"/",
component:()=>import("../pages/index/index.vue")
}
]
})
3.app.vue 定义路由出口
<div>
<router-view></router-view>
</div>
4.书写了一个用户代码片段
{
"Print to console": {
"scope": "javascript,typescript,vue",
"prefix": "vue",
"body": [
"<template>",
"<div>",
"<h1>登录</h1>",
"</div>",
"</template>",
"<script>",
"import {mapGetters,mapActions} from \"vuex\"",
"export default {",
"props:[],",
"components:{},",
"data(){",
"return {",
"}",
"},",
"computed:{",
"...mapGetters({",
"})",
"},",
"methods:{",
"...mapActions({",
"})",
"},",
"mounted(){},",
"}",
"</script>",
"<style scoped>",
"</style>"
],
"description": "我的vue"
}
}
5.login.vue
1.html+css
<div class="login">
<div class="box">
<h3 class="center">登录</h3>
<div class="line">
<el-input placeholder="情输入账号" clearable></el-input>
</div>
<div class="line">
<el-input placeholder="情输入密码" clearable show-password></el-input>
</div>
<div class="center line">
<el-button type="primary" @click="login">登录</el-button>
</div>
</div>
</div>
<style scoped>
.login{
width: 100vw;
height: 100vh;
background: linear-gradient(to right,#553443,#303D60);
position: relative;
}
.box{
width: 400px;
padding: 20px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
background: #ffffff;
border-radius: 20px;
}
.center{
text-align: center;
}
.line{
margin-top: 20px;
}
</style>
6.index.vue
1.粘贴布局容器
<el-container class="index">
<el-aside class="left" width="200px">
</el-aside>
<el-container>
<el-header class="header">Header</el-header>
<el-main class="main">
<!-- 二级路由出口 -->
<router-view></router-view>
</el-main>
</el-container>
</el-container>
2.粘贴导航
<el-aside class="left" width="200px">
<!-- 左侧导航 -->
<!-- 1.background-color -->
<el-menu
default-active="2"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
background-color="#20222a"
text-color="#fff"
active-text-color="#ffd04b"
>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<span slot="title">导航二</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<i class="el-icon-document"></i>
<span slot="title">导航三</span>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">导航四</span>
</el-menu-item>
</el-menu>
</el-aside>
3.el-main 定义二级路由出口
<el-main class="main">
<!-- 二级路由出口 -->
<router-view></router-view>
</el-main>
4.创建二级路由组件
menu 菜单管理
role 角色管理
manage 管理员管理
cate 商品分类
specs 商品规格
goods 商品管理
member 会员管理
banner 轮播图管理
seckill 秒杀活动
5.配置路由规则 router/index.js
//首页下面的二级路由规则
let indexRoutes = [
{
path: "/menu",
component: () => import("../pages/menu/menu.vue")
},
{
path: "/role",
component: () => import("../pages/role/role.vue")
},
{
path: "/manage",
component: () => import("../pages/manage/manage.vue")
},
{
path: "/cate",
component: () => import("../pages/cate/cate.vue")
},
{
path: "/specs",
component: () => import("../pages/specs/specs.vue")
},
{
path: "/goods",
component: () => import("../pages/goods/goods.vue")
},
{
path: "/member",
component: () => import("../pages/member/member.vue")
},
{
path: "/banner",
component: () => import("../pages/banner/banner.vue")
},
{
path: "/seckill",
component: () => import("../pages/seckill/seckill.vue")
},
]
{
path: "/",
component: () => import("../pages/index/index.vue"),
children: [
{
path: "",
component: () => import("../pages/home/home.vue"),
},
...indexRoutes
]
}
6.实现侧边栏
<el-aside class="left" width="200px">
<!-- 左侧导航 -->
<!-- 1.background-color 背景颜色
2.router 开启路由模式,index 是你想要跳转的路径
3.unique-opened 保持只有一个子菜单展开
-->
<el-menu
class="el-menu-vertical-demo"
background-color="#20222a"
text-color="#fff"
active-text-color="#ffd04b"
router
unique-opened
>
<el-menu-item index="/">
<i class="el-icon-menu"></i>
<span slot="title">首页</span>
</el-menu-item>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-location"></i>
<span>系统设置</span>
</template>
<el-menu-item-group>
<el-menu-item index="/menu">菜单管理</el-menu-item>
<el-menu-item index="/role">角色管理</el-menu-item>
<el-menu-item index="/manage">管理员管理</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-location"></i>
<span>商城管理</span>
</template>
<el-menu-item-group>
<el-menu-item index="/cate">商品分类</el-menu-item>
<el-menu-item index="/specs">商品规格</el-menu-item>
<el-menu-item index="/goods">商品管理</el-menu-item>
<el-menu-item index="/member">会员管理</el-menu-item>
<el-menu-item index="/banner">轮播图管理</el-menu-item>
<el-menu-item index="/seckill">秒杀活动</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
7.添加了一个面包屑
<el-main class="main">
<!-- 面包屑 -->
<el-breadcrumb v-if="$route.name" separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>{{$route.name}}</el-breadcrumb-item>
</el-breadcrumb>
<!-- 二级路由出口 -->
<router-view></router-view>
</el-main>
8.给每一个二级路由规格添加了name属性 router/index.js
let indexRoutes = [
{
path: "/menu",
name:"菜单管理",
component: () => import("../pages/menu/menu.vue")
},
{
path: "/role",
name:"角色管理",
component: () => import("../pages/role/role.vue")
},
]
7.menu.vue
1.拆分menu.vue
将menu组件分为 添加按钮 list add
<template>
<div>
<el-button type="primary" @click="willAdd">添加</el-button>
<!-- 表格 -->
<v-list></v-list>
<!-- 添加组件 -->
<v-add :info="info"></v-add>
</div>
</template>
2.通过模拟数据实现list.vue
<template>
<el-table
:data="tableData"
style="width: 100%; margin-bottom: 20px"
row-key="id"
border
default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column prop="date" label="日期" sortable width="180">
</el-table-column>
<el-table-column prop="name" label="姓名" sortable width="180">
</el-table-column>
<el-table-column prop="address" label="地址"> </el-table-column>
</el-table>
</template>
data() {
return {
tableData: [
{
id: 1,
date: "2016-05-02",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
{
id: 2,
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1517 弄",
},
{
id: 3,
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1519 弄",
children: [
{
id: 31,
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1519 弄",
},
{
id: 32,
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1519 弄",
},
],
},
{
id: 4,
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1516 弄",
},
],
};
},
3.add.vue
1.form
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="菜单名称">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="上级菜单">
<el-select v-model="form.name" placeholder="请选择上级菜单">
<el-option label="*菜单" value="shanghai"></el-option>
</el-select>
</el-form-item>
<el-form-item label="菜单类型">
<el-radio v-model="form.name" label="1">目录</el-radio>
<el-radio v-model="form.name" label="2">菜单</el-radio>
</el-form-item>
<el-form-item label="菜单图标">
<el-select v-model="form.name" placeholder="请选择上级菜单">
<el-option v-for="item in icons" :key="item" :value="item">
<i :class="item"></i>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="菜单地址">
<el-select v-model="form.name" placeholder="请选择上级菜单">
<el-option
v-for="item in indexRoutes"
:key="item.path"
:label="item.name"
:value="'/' + item.path"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="form.name"></el-switch>
</el-form-item>
</el-form>
data() {
return {
//图标
icons: [
"el-icon-s-tools",
"el-icon-user",
"el-icon-camera",
"el-icon-message-solid",
],
//路由
indexRoutes: indexRoutes,
form: {
name: "",
},
};
},
4.对话框
<el-dialog title="添加菜单" :visible.sync="info.isshow">
<el-form ref="form" :model="form" label-width="80px">
...
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel">取 消</el-button>
<el-button type="primary">添 加</el-button
>
</div>
</el-dialog>
对话框需要一个变量isshow来控制出现和消失,将数据isshow定义在menu.vue中,传递过来,但是dialog需要直接修改这个数据,所以不能传递简单类型,需要传递一个json格式的数据,才可以实现父变子变,子变 父变得要求。
menu.vue
data() {
return {
//传递给add的数据
info: {
//添加弹框的状态
isshow: false,
},
};
},
methods: {
//点击了添加按钮
willAdd() {
this.info.isshow = true;
},
},
<v-add :info="info"></v-add>
add.vue通过props接收
{
props:["info"]
}
<el-dialog :visible.sync="info.isshow"></el-dialog>
5.交互的接口 utils/request.js
//添加
export const reqMenuAdd=(params)=>{
return axios({
url:baseUrl+"/api/menuadd",
method:"post",
data:qs.stringify(params)
})
}
//列表
export const reqMenuList=()=>{
return axios({
url:baseUrl+"/api/menulist",
method:"get",
params:{
istree:true
}
})
}
//删除
export const reqMenuDel=(id)=>{
return axios({
url:baseUrl+"/api/menudelete",
method:"post",
data:qs.stringify({id:id})
})
}
6.add.vue 添加交互
1.form的字段和后端保持一致,然后绑定到每个表单上
form: {
pid:0,
title:"",
icon:"",
type:1,
url:"",
status:1
},
2.点击了添加按钮,开始添加交互
//取消
cancel(){
this.info.isshow=false;
},
//数据重置
empty(){
this.form={
pid:0,
title:"",
icon:"",
type:1,
url:"",
status:1
}
},
//点击了添加按钮
add(){
reqMenuAdd(this.form).then(res=>{
if(res.data.code==200){
//成功
successAlert(res.data.msg)
//数据重置
this.empty()
//弹框消失
this.cancel()
//list数据要刷新
this.reqListAction()
}else{
warningAlert(res.data.msg)
}
})
}
3.如果是 *菜单下,就是目录,目录有图标,没有地址;
如果不是*菜单下的,就是菜单,菜单没有图标。有地址
<el-form-item label="上级菜单">
<el-select v-model="form.pid" placeholder="请选择上级菜单" @change="changePid">
...
</el-select>
</el-form-item>
<el-form-item label="菜单类型">
<el-radio v-model="form.type" :label="1" disabled>目录</el-radio>
<el-radio v-model="form.type" :label="2" disabled>菜单</el-radio>
</el-form-item>
<el-form-item label="菜单图标" v-if="form.type==1">
...
</el-form-item>
<el-form-item label="菜单地址" v-else>
...
</el-form-item>
//修改了pid
changePid(){
if(this.form.pid==0){
this.form.type=1
}else{
this.form.type=2
}
},
7.获取菜单的list
由于菜单的list的数据,很多地方都要用到。所以将他存在状态层。
1.store/modules/menu.js
import {reqMenuList} from "../../utils/request"
const state = {
// 菜单的list
list:[]
}
const mutations = {
//修改list
changeList(state,arr){
state.list=arr;
}
}
const actions = {
//页面请求
reqListAction(context){
//发请求
reqMenuList().then(res=>{
context.commit("changeList",res.data.list)
})
}
}
const getters = {
list(state){
return state.list
}
}
export default {
state,
mutations,
actions,
getters,
namespaced: true
}
2.store/index.js 引入menu模块
import menu from "./modules/menu"
export default new Vuex.Store({
state,
getters,
mutations,
actions,
modules:{
menu
}
})
3.list.vue 通过mapGetters mapActions从状态层取数据和方法
computed: {
...mapGetters({
list: "menu/list",
}),
},
methods: {
...mapActions({
reqListAction: "menu/reqListAction",
}),
},
mounted(){
this.reqListAction()
}
4.add.vue 里也要用list数据和reqListAction 方法
computed: {
...mapGetters({
list: "menu/list",
}),
},
methods: {
...mapActions({
reqListAction: "menu/reqListAction",
}),
},
<el-form-item label="上级菜单">
<el-select v-model="form.pid" placeholder="请选择上级菜单" @change="changePid">
<el-option label="*菜单" :value="0"></el-option>
<el-option v-for="item in list" :key="item.id" :label="item.title" :value="item.id"></el-option>
</el-select>
</el-form-item>
当添加完成的时候,刷新list数据
//点击了添加按钮
add(){
reqMenuAdd(this.form).then(res=>{
if(res.data.code==200){
//成功
successAlert(res.data.msg)
//数据重置
this.empty()
//弹框消失
this.cancel()
//list数据要刷新
this.reqListAction()
}else{
warningAlert(res.data.msg)
}
})
}
8.删除交互
list.vue
<el-button type="danger" @click="del(scope.row.id)">删除</el-button>
//删除
del(id) {
this.$confirm("你确定要删除吗?", "删除提示", {
confirmButtonText: "删除",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
//点了确定按钮
reqMenuDel(id).then(res=>{
if(res.data.code==200){
successAlert(res.data.msg)
this.reqListAction();
}else{
warningAlert(res.data.msg)
}
})
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除",
});
});
},
9.编辑功能
1.list.vue 点击了编辑按钮
<el-button type="primary" @click="edit(scope.row.id)">编辑</el-button>
//编辑
edit(id){
this.$emit("edit",id)
},
2.menu.vue 触发自定事件 edit,收到id, 弹出弹框,让add.vue 的look函数执行
<!-- 表格 -->
<v-list @edit="edit"></v-list>
<!-- 添加组件 -->
<v-add :info="info" ref="add"></v-add>
//编辑
edit(id){
this.info.isshow=true;
this.info.isAdd=false;
//让add组件发起获取详情的请求
this.$refs.add.look(id)
}
3.add.vue look函数开始请求一条数据,请求完成后,赋值给form,但是请求得到的数据中没有id字段,但是一会儿修改是要id的,所以要补一个id
//获取菜单详情 (1条)
look(id) {
//发请求
reqMenuDetail(id).then((res) => {
if (res.data.code == 200) {
//这个时候form是没有id的
this.form = res.data.list;
this.form.id = id;
} else {
warningAlert(res.data.msg);
}
});
},
4.add.vue 点击了修改按钮
//修改
update() {
reqMenuUpdate(this.form).then((res) => {
if (res.data.code == 200) {
successAlert(res.data.msg);
this.empty();
this.cancel();
this.reqListAction();
} else {
warningAlert(res.data.msg);
}
});
},
10.弹框bug
点击了 编辑–》取消 --》添加 ,这个时候,直接有上一次的数据。要改成:
如果是添加开的弹框,就什么都不做;如果是编辑开的弹框,就清除form
<el-dialog @closed="close"></el-dialog>
//弹框消失完成
close(){
//如果是添加开的弹框,就什么都不做;如果是编辑开的弹框,就清除form
if(!this.info.isAdd){
this.empty()
}
},
8.role.vue
1.拆分组件 list add 类似菜单
2.静态页实现
list.vue
和菜单list一样
add.vue
<!-- 树形控件 -->
<!-- data要展示的数组 -->
<!-- props 配置 :children 用来判断是否有下一层的字段;label用来展示在页面中的字段 -->
<el-form-item label="角色权限">
<el-tree
ref="tree"
:data="menuList"
show-checkbox
node-key="id"
:props="{children: 'children',label: 'title',}"
>
</el-tree>
</el-form-item>
3.交互接口书写
request.js
/*********角色管理***************/
//添加
export const reqRoleAdd=(params)=>{
return axios({
url:baseUrl+"/api/roleadd",
method:"post",
data:qs.stringify(params)
})
}
//列表
export const reqRoleList=()=>{
return axios({
url:baseUrl+"/api/rolelist",
method:"get",
})
}
//删除
export const reqRoleDel=(id)=>{
return axios({
url:baseUrl+"/api/roledelete",
method:"post",
data:qs.stringify({id:id})
})
}
//1条
export const reqRoleDetail=(id)=>{
return axios({
url:baseUrl+"/api/roleinfo",
method:"get",
params:{
id:id
}
})
}
//修改
export const reqRoleUpdate=(params)=>{
return axios({
url:baseUrl+"/api/roleedit",
method:"post",
data:qs.stringify(params)
})
}
4.获取列表数据
1.store/modules/role.js
import {reqRoleList} from "../../utils/request"
const state = {
// list
list:[]
}
const mutations = {
//修改list
changeList(state,arr){
state.list=arr;
}
}
const actions = {
//页面请求
reqListAction(context){
//发请求
reqRoleList().then(res=>{
let list=res.data.list?res.data.list:[]
context.commit("changeList",list)
})
}
}
const getters = {
list(state){
return state.list
}
}
export default {
state,
mutations,
actions,
getters,
namespaced: true
}
2.store/index.js 引入
import role from "./modules/role"
export default new Vuex.Store({
modules:{
menu,
role
}
})
3.pages/role/list.vue 引入数据和方法
computed: {
...mapGetters({
list: "role/list",
}),
},
methods: {
...mapActions({
reqListAction: "role/reqListAction",
}),
},
mounted() {
this.reqListAction();
},
5.添加
0.由于添加的树形控件需要菜单的list ,所以从vuex中取出
computed: {
...mapGetters({
//菜单list
menuList: "menu/list",
}),
},
methods: {
...mapActions({
//请求菜单list
reqMenuListAction: "menu/reqListAction",
//角色的list
reqRoleList:"role/reqListAction"
}),
},
mounted() {
//如果菜单list没有请求过,就请求一下,如果请求过了,就不用请求了
if(this.menuList.length==0){
this.reqMenuListAction()
}
},
1.初始数据form,保持和后端字段一致
data() {
return {
form: {
rolename:"",
menus:'[]',
status: 1,
},
};
},
2.当点击了添加按钮,先获取到树形控件的数据,赋值给form,才做数据交互。
//点击了添加按钮
add() {
树形控件取值 this.$refs.tree.getCheckedKeys()
this.form.menus=JSON.stringify(this.$refs.tree.getCheckedKeys())
reqRoleAdd(this.form).then((res) => {
if (res.data.code == 200) {
//成功
successAlert(res.data.msg);
//数据重置
this.empty();
//弹框消失
this.cancel();
//list数据要刷新
this.reqRoleList();
} else {
warningAlert(res.data.msg);
}
});
},
由于form和树形控件没有关联,所以在清空的时候,清空了form,还要把属性控件清空
//数据重置
empty() {
this.form = {
rolename:"",
menus:'[]',
status: 1,
};
//树形控件设置值
this.$refs.tree.setCheckedKeys([])
},
6.删除
//删除2
dele(id) {
//点了确定按钮
reqRoleDel(id).then((res) => {
if (res.data.code == 200) {
successAlert(res.data.msg);
this.reqListAction();
} else {
warningAlert(res.data.msg);
}
});
},
7.编辑
点了编辑,获取到数据,赋值给form,需要补id,而且树形控件也需要赋值
//获取菜单详情 (1条)
look(id) {
//发请求
reqRoleDetail(id).then((res) => {
if (res.data.code == 200) {
//这个时候form是没有id的
this.form = res.data.list;
this.form.id=id
//给树形控件赋值
this.$refs.tree.setCheckedKeys(JSON.parse(this.form.menus))
} else {
warningAlert(res.data.msg);
}
});
},
8.修改
树形控件数据赋值给form
//修改
update() {
this.form.menus=JSON.stringify(this.$refs.tree.getCheckedKeys())
reqRoleUpdate(this.form).then((res) => {
if (res.data.code == 200) {
successAlert(res.data.msg);
this.empty();
this.cancel();
this.reqRoleList();
} else {
warningAlert(res.data.msg);
}
});
},
9.manage.vue 管理员管理
1.拆分组件
list add 粘贴了menu 的list 和 add
2.静态页
list.vue
add.vue
3.交互接口书写
/*********管理员管理***************/
//添加
export const reqManageAdd=(params)=>{
return axios({
url:baseUrl+"/api/useradd",
method:"post",
data:qs.stringify(params)
})
}
//管理员总数
export const reqManageCount=()=>{
return axios({
url:baseUrl+"/api/usercount",
})
}
//列表 params={page:1,size:10}
export const reqManageList=(params)=>{
return axios({
url:baseUrl+"/api/userlist",
method:"get",
params:params
})
}
//删除
export const reqManageDel=(uid)=>{
return axios({
url:baseUrl+"/api/userdelete",
method:"post",
data:qs.stringify({uid:uid})
})
}
//1条
export const reqManageDetail=(uid)=>{
return axios({
url:baseUrl+"/api/userinfo",
method:"get",
params:{
uid:uid
}
})
}
//修改
export const reqManageUpdate=(params)=>{
return axios({
url:baseUrl+"/api/useredit",
method:"post",
data:qs.stringify(params)
})
}
4.获取列表数据
1.store/modules/manage.js
import {reqManageList} from "../../utils/request"
const state = {
// 菜单的list
list:[]
}
const mutations = {
//修改list
changeList(state,arr){
state.list=arr;
}
}
const actions = {
//页面请求
reqListAction(context){
//发请求
reqManageList({page:1,size:10}).then(res=>{
let list=res.data.list?res.data.list:[]
context.commit("changeList",list)
})
}
}
const getters = {
list(state){
return state.list
}
}
export default {
state,
mutations,
actions,
getters,
namespaced: true
}
2.store/index.js 引入manage模块
import manage from "./modules/manage"
export default new Vuex.Store({
state,
getters,
mutations,
actions,
modules:{
menu,
role,
manage
}
})
3.list.vue使用
computed: {
...mapGetters({
list: "manage/list",
}),
},
methods: {
...mapActions({
reqListAction: "manage/reqListAction",
}),
},
mounted() {
this.reqListAction();
},
5.添加功能-add.vue
0.获取角色列表
computed: {
...mapGetters({
roleList: "role/list",
}),
},
methods: {
...mapActions({
reqRoleListAction: "role/reqListAction",
reqManageList:"manage/reqListAction"
}),
},
mounted() {
//如果没有请求过角色管理的数据,就请求一次,如果请求过了,就不用请求了
if(this.roleList.length==0){
this.reqRoleListAction()
}
},
1.定义form初始值
data() {
return {
form: {
roleid:"",
username:"",
password:"",
status:1
},
};
},
2.点击了添加按钮
//点击了添加按钮
add() {
reqManageAdd(this.form).then((res) => {
if (res.data.code == 200) {
//成功
successAlert(res.data.msg);
//数据重置
this.empty();
//弹框消失
this.cancel();
//list数据要刷新
this.reqManageList();
} else {
warningAlert(res.data.msg);
}
});
},
3.清空form
//数据重置
empty() {
this.form ={
roleid:"",
username:"",
password:"",
status:1
};
},
6.删除
<del-btn @confirm="dele(scope.row.uid)"></del-btn>
//删除2
dele(uid) {
//点了确定按钮
reqManageDel(uid).then((res) => {
if (res.data.code == 200) {
successAlert(res.data.msg);
this.reqListAction();
} else {
warningAlert(res.data.msg);
}
});
},
7.编辑
<el-button type="primary" @click="edit(scope.row.uid)">编辑</el-button>
add.vue
//获取菜单详情 (1条)
look(uid) {
//发请求
reqManageDetail(uid).then((res) => {
if (res.data.code == 200) {
//这个时候form是没有id的
this.form = res.data.list;
//密码是加密过的,所以需要重置密码
this.form.password=""
} else {
warningAlert(res.data.msg);
}
});
},
8.修改
//修改
update() {
reqManageUpdate(this.form).then((res) => {
if (res.data.code == 200) {
successAlert(res.data.msg);
this.empty();
this.cancel();
this.reqManageList();
} else {
warningAlert(res.data.msg);
}
});
},
9.分页
1.分页组件
<!-- 分页组件 -->
<!--
total:设置总数
page-size:每页数量
有多少页,不用自己计算
current-change:当前页码发生了改变,就会触发,参数就是当前的页码
-->
<el-pagination background layout="prev, pager, next" :total="total" :page-size="size"
@current-change="changePage"
>
</el-pagination>
2.状态层(store/modules/manage.js)定义了状态size ,并导出
const state = {
//一页的数量
size: 2,
}
const getters = {
size(state) {
return state.size
},
}
组件使用
<el-pagination background layout="prev, pager, next"
:page-size="size"
>
</el-pagination>
3.总数
定义了一个total状态,他的修改的mutations,和请求的action,导出的getters
const state = {
//总数
total:0,
}
const mutations = {
//修改total
changeTotal(state,num){
state.total=num;
},
}
const actions = {
//获取总数的请求
reqTotalAction(context){
reqManageCount().then(res=>{
context.commit("changeTotal",res.data.list[0].total)
})
},
}
const getters = {
total(state){
return state.total;
}
}
list.vue取出total使用
<el-pagination background layout="prev, pager, next"
:total="total"
>
</el-pagination>
4.页码
在状态层定义了一个状态页码 page,修改
const state = {
//页码
page:1,
}
const mutations = {
//修改页码
changePage(state,page){
state.page=page
}
}
const actions = {
//组件修改了页码
changePageAction(context,page){
//修改页码
context.commit("changePage",page)
//发起list请求
context.dispatch('reqListAction')
}
}
list.vue 绑定事件,触发修改页码
<el-pagination background layout="prev, pager, next" :total="total" :page-size="size"
@current-change="changePage"
>
</el-pagination>
methods: {
...mapActions({
changePageAction:"manage/changePageAction"
}),
//修改了当前页码
changePage(e){
this.changePageAction(e)
}
},
5.在请求list数据的时候,不在使用固定值,而是使用变量作为参数
const actions = {
//页面请求
reqListAction(context) {
//发请求
reqManageList(
{ page: context.state.page, size: context.state.size }
).then(res => {
let list = res.data.list ? res.data.list : []
context.commit("changeList", list)
})
},
}
6.注意
一进来挂载完成、添加完成、删除完成 都要重新请求总数。
7.bug
假设目前page是3,在page3 减到没有数据了,就会显示“暂无数据”,但是应该展示第2页的数据。
//页面请求
reqListAction(context) {
//发请求
reqManageList({ page: context.state.page, size: context.state.size }).then(res => {
//如果去list的时候,取到null,那么有可能是最后一页没数据了,需要减一页,再次请求list
let list = res.data.list ? res.data.list : []
//如果取到的数据是null,并且不是第一页,那么就页码减1,重新请求list
if(context.state.page>1&&list.length==0){
context.commit("changePage",context.state.page-1)
context.dispatch("reqListAction")
return;
}
context.commit("changeList", list)
})
},
10、cate商品分类
1、拆分页面(复制menu页面)
2、add.vue
<!-- 原生 -->
<el-form-item label="图片">
<div class="fileBox">
<h3>+</h3>
<img :src="imgUrl" v-if="imgUrl">
<input type="file" @change="changeFile" v-if="info.isShow">
</div>
</el-form-item>
<!-- element-ui -->
<el-form-item label="图片">
<el-upload
class="avatar-uploader"
action="#"
:show-file-list="false" :on-change="changeFile2">
<img v-if="imgUrl" :src="imgUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
// 原生改变图片
changeFile(e) {
let file = e.target.files[0];
// 限制大小file.size 限制大小为2M B-->1024KB 1024KB-->1M
if(file.size > 2*1024*1024){
warningAlert("图片大小超出限制")
return
}
// URL.createObjectURL 可以将图片信息转换成具体的图片
this.imgUrl = URL.createObjectURL(file);
this.form.img = file;
},
// element-ui改变图片
changeFile2(e){
let file = e.raw
this.imgUrl = URL.createObjectURL(file);
this.form.img = file
}
3、在添加的请求里面
export const reqCateAdd = (form) => {
// 因为form中包含了文件,所以必须要这样传参
let data = new FormData()
// data.append("pid",1)
// data.append("catename",'hahaha')
// data.append("img",file)
// data.append("status",1)
for(let i in form){
data.append(i,form[i])
}
return axios({
url: baseUrl + "/api/cateadd",
method: "post",
data: data
})
}
4、列表渲染图片
<el-table-column label="图片">
<template slot-scope="scope">
<div class="imgBox">
<img :src="$imgUrl+scope.row.img" alt="">
</div>
</template>
</el-table-column>
// 开发环境
let baseUrl = "/api"
Vue.prototype.$imgUrl = "http://localhost:3000/"
// 上线环境
// let bstUrl = ""
// Vue.prototype.$imgUrl = ""
5、编辑功能也要修改request.js
export const reqCateUpdate = (form)=>{
let data = new FormData()
for(let i in form){
data.append(i,form[i])
}
return axios({
url:baseUrl+"/api/cateedit",
method:"POST",
data:data
})
}
11、specs商品规格
1、拆分静态页(复制manage页面)
2、add.vue静态页面
<el-form-item label="规格属性" v-for="(item,index) in attrArr" :key="index">
<div class="inpTop">
<el-input v-model="item.value"></el-input>
<el-button type="primary" @click="addAttr" v-if="index==0">新增规格属性</el-button>
<el-button type="danger" @click="delAttr(index)" v-else>删除</el-button>
</div>
</el-form-item>
data() {
return {
attrArr:[
{value:''},
{value:''}
],
};
}
3、点击添加商品规格和删除
// 点击新增按钮
addAttr(){
this.attrArr.push({value:''})
},
// 点击删除按钮
delAttr(index){
this.attrArr.splice(index,1)
}
4、点击提交按钮
add() {
this.form.attrs = JSON.stringify(this.attrArr.map(item=>item.value))
reqSpecsAdd(this.form).then((res) => {
if (res.data.code === 200) {
// 弹窗
successAlert(res.data.msg);
// 取消弹窗并重置form数据
this.cancel();
// 刷新list列表
this.reqManageList();
// 重新获取总条数
this.reqChangeTotal();
} else {
warningAlert(res.data.msg);
}
});
}
5、在状态层处理一下list数据
list.forEach(item=>{
item.attrs = JSON.parse(item.attrs)
})
<el-table-column prop="rolename" label="规格属性">
<template slot-scope="scope">
<div>
<el-tag v-for="item in scope.row.attrs" :key="item">{{item}}</el-tag>
</div>
</template>
</el-table-column>
6、删除功能
del2(id) {
reqSpecsDel(id).then((res) => {
if (res.data.code == 200) {
successAlert(res.data.msg);
// 重新请求列表
this.reqChangeList();
// 重新请求总条数
this.reqChangeTotal()
} else {
warningAlert(res.data.msg);
}
});
}
7、获取一条详情信息
getOne(id) {
reqSpecsOne(id).then((res) => {
this.form = res.data.list[0];
this.attrArr = JSON.parse(this.form.attrs).map(item=>({value:item}))
});
}
8、编辑功能
update() {
this.form.attrs = JSON.stringify(this.attrArr.map(item=>item.value))
reqSpecsUpdate(this.form).then((res) => {
if (res.data.code === 200) {
successAlert(res.data.msg);
// 关闭弹窗并重置数据
this.cancel();
// 刷新list列表
this.reqManageList();
} else {
warningAlert(res.data.msg);
}
});
},
}
12、goods商品管理
1、拆分静态页
2、统一设置接口
3、add.vue
3.1、设置静态页
3.2、设置初始form
form: {
first_cateid: "",
second_cateid: "",
goodsname: "",
price: "",
market_price: "",
img: "",
description: "",
specsid: "",
specsattr: "",
isnew: 1,
ishot: 1,
status: 1,
}
3.3、绑定数据v-model
3.3.1、通过状态层获取一级分类
3.3.2、二级分类
// 用于渲染二级分类
secondArr: [],
// 当一级分类改变的时候
changeFirst() {
reqCateList({ pid: this.form.first_cateid }).then((res) => {
// 清空form里面的second_cateid
this.form.second_cateid = "";
// 用于渲染二级分类
this.secondArr = res.data.list;
});
}
3.3.3、图片
<el-form-item label="图片">
<div class="fileBox">
<h3>+</h3>
<img :src="imgUrl" v-if="imgUrl" />
<input type="file" @change="changeFile" v-if="info.isShow" />
</div>
</el-form-item>
// 改变了图片
changeFile(e) {
console.log(e);
let file = e.target.files[0];
// 限制大小file.size 限制大小为2M B-->1024KB 1024KB-->1M
if (file.size > 2 * 1024 * 1024) {
warningAlert("图片大小超出限制");
return;
}
// URL.createObjectURL 可以将图片信息转换成具体的图片
this.imgUrl = URL.createObjectURL(file);
this.form.img = file;
}
3.3.4、商品规格
通过状态层取到specs商品规格
3.3.5、规格属性
// 用于渲染规格属性
specsArr: []
// 改变了商品规格
changeSpecs() {
// 清空规格属性
this.specsArr = [];
this.form.specsattr = [];
// 找到specsList里面id是我需要的那一条数据
let obj = this.specsList.find((item) => item.id == this.form.specsid);
// 把那一条数据的attrs赋值给我的specsArr
this.specsArr = obj.attrs;
}
4、list.vue
4.1、设置静态页
4.2、设置状态层(复制的mange状态层)
4.3、页面一进来就请求列表数据
4.4、绑定数据
5、删除
6、获取一条信息
getOne(id) {
reqGoodsOne(id).then((res) => {
this.form = res.data.list;
// 补充id,因为修改的时候需要id
this.form.id = id
// 1、根据一级分类的id获取二级分类的列表
reqCateList({ pid: this.form.first_cateid }).then((res) => {
// 用于渲染二级分类
this.secondArr = res.data.list;
});
// 2、处理图片
this.imgUrl = this.$imgUrl + this.form.img
// 3、处理规格属性
this.form.specsattr = JSON.parse(this.form.specsattr)
// 4、处理规格属性的下拉框
let obj = this.specsList.find((item) => item.id == this.form.specsid);
this.specsArr = obj.attrs;
});
}
7、修改功能
update() {
let data = {
...this.form,
specsattr: JSON.stringify(this.form.specsattr),
};
reqGoodsUpdate(data).then((res) => {
if (res.data.code === 200) {
successAlert(res.data.msg);
// 关闭弹窗并重置数据
this.cancel();
// 刷新list列表
this.reqChangeList()
} else {
warningAlert(res.data.msg);
}
});
}
富文本编辑器
1、官网:http://www.wangeditor.com/
2、安装方式:
npm安装 npm i wangeditor --save
CDN链接 https://unpkg.com/wangeditor/dist/wangEditor.min.js
3、使用
// 当打开动画完成时
opened(){
// 加载富文本
this.editor = new E('#div1')
this.editor.create()
// 设置富文本的内容
this.editor.txt.html(this.form.description)
}
// 在添加请求之前
// 将富文本的内容加入到form中
this.form.description = this.editor.txt.html()
权限
1、登录交互
login(){
reqLogin(this.info).then(res=>{
console.log(res)
if(res.data.code === 200){
successAlert(res.data.msg)
// 1、存res.data.list。不仅存了状态层还存了本地存储
this.reqUserInfoAction(res.data.list)
// 2、进入页面
this.$router.push("/")
}else{
warningAlert(res.data.msg)
}
})
}
2、设置状态层
// mutations.js
export const state = {
userInfo:sessionStorage.getItem("userInfo")?JSON.parse(sessionStorage.getItem("userInfo")):{}
}
export const getters = {
userInfo(state){
return state.userInfo
}
}
export const mutations = {
changeUserInfo(state,info){
state.userInfo = info
if(info.id){
//在本地存储也存一份
sessionStorage.setItem("userInfo",JSON.stringify(info))
}else{
//清空本地存储的userInfo
sessionStorage.removeItem("userInfo")
}
}
}
// actions.js
import {reqLogin} from "../util/request"
export const actions = {
reqUserInfoAction(context,info){
context.commit("changeUserInfo",info)
}
}
3、登陆拦截
// 登录拦截
router.beforeEach((to, from, next) => {
//1、如果去的是登录页,就直接进
if (to.path == '/login') {
next()
return
}
//2、如果去的是其他页面,验证仓库里面的token是否存在,如果存在说明登录过了。
if (store.state.userInfo.token) {
next();
return
}
next("/login")
})
4、路由独享守卫
function changeEnter(path,next) {
// 取出你可以去的地址
let menus_url = store.state.userInfo.menus_url
if (menus_url.includes(path)) {
next()
} else {
next("/")
}
}
export const indexRouters = [{
path: "menu",
name: "菜单管理",
component: () => import("../page/menu/menu.vue"),
beforeEnter: (to, from, next) => {
changeEnter("/menu",next)
}
}
}]
5、动态侧边栏index.vue
<div v-for="item in userInfo.menus" :key="item.id">
<!-- 目录 -->
<el-submenu :index="item.id+''" v-if="item.children">
<template slot="title">
<i :class="item.icon"></i>
<span>{{item.title}}</span>
</template>
<el-menu-item-group>
<el-menu-item v-for="i in item.children" :key="i.id" :index="i.url">
<span>{{i.title}}</span>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
<!-- 菜单 -->
<el-menu-item :index="item.url" v-else>
<span>{{item.title}}</span>
</el-menu-item>
</div>
注意:动态侧边栏需要一些数据,数据是从状态层拿过来的
6、退出
<el-header class="header">
<div>
<span>{{userInfo.username}}</span>
<el-button @click="logOut">退出</el-button>
</div>
</el-header>
logOut(){
// 清空状态层的userinfo
this.reqUserInfoAction({})
// 跳到登录页
this.$router.push("/login")
}
上一篇: JSP 实战学习笔记-13-jsp response 响应
下一篇: MySQL 索引
推荐阅读