Vue速成--專案實戰(後臺管理系統)

_Web_Z發表於2020-11-30

在這裡插入圖片描述

需要後端伺服器資料的童鞋,私信留言哦~

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")
}

相關文章