vue_shop(基於vue電商管理后台網站)


vue_shop

day01

實現登錄功能

項目預開發處理

  1. 登錄狀態保持:
    如果服務器和客戶端同源,建議可以使用cookie或者session來保持登錄狀態
    如果客戶端和服務器跨域了,建議使用token進行維持登錄狀態。

  2. 登錄邏輯:
    在登錄頁面輸入賬號和密碼進行登錄,將數據發送給服務器
    服務器返回登錄的結果,登錄成功則返回數據中帶有token
    客戶端得到token並進行保存,后續的請求都需要將此token發送給服務器,服務器會驗證token以保證用戶身份。

  3. 添加新分支login,打開vue_shop終端,使用git status確定當前項目狀態。
    確定當前工作目錄是干凈的之后,創建一個分支進行開發,開發完畢之后將其合並到master

    git checkout -b login
    

    然后查看新創建的分支:git branch
    確定我們正在使用login分支進行開發

  • main.js文件(入口文件):
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'

Vue.config.productionTip = false
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
  • components文件夾中新建Login.vue組件

  • router.js中導入組件並設置規則,在App.vue中添加路由占位符:

const router = new Router({
  routes: [
    { path: '/', redirect: '/login' },
    { path: '/login', component: Login }
  ]
})
  • 給Login.vue中的內容添加樣式的時候,會報錯“缺少less-loader”,需要配置less-loader(開發依賴),安裝less(開發依賴)

  • 然后需要添加公共樣式,在assets文件夾下面添加css文件夾,創建global.css文件,添加全局樣式:

    • 在main.js中導入global.css,使得全局樣式生效 import

Login.vue完整代碼:

<template>
    <div class="login_container">
        <!-- 登錄盒子  -->
        <div class="login_box">
            <!-- 頭像 -->
            <div class="avatar_box">
                <img src="../assets/logo.png" alt="">
            </div>
            <!-- 登錄表單 -->
            <el-form :model="loginForm" ref="LoginFormRef" :rules="loginFormRules" label-width="0px" class="login_form">
                <!-- 用戶名 -->
                <el-form-item prop="username">
                    <el-input v-model="loginForm.username" prefix-icon="iconfont icon-user" ></el-input>
                </el-form-item> 
                <!-- 密碼 -->
                <el-form-item prop="password">
                    <el-input type="password" v-model="loginForm.password" prefix-icon="iconfont icon-3702mima"></el-input>
                </el-form-item> 
                <!-- 按鈕 -->
                <el-form-item class="btns">
                    <el-button type="primary" @click="login">登錄</el-button>
                    <el-button type="info" @click="resetLoginForm">重置</el-button>
                </el-form-item> 
            </el-form>
        </div>
    </div>
</template>

<script>
export default {
  data() {
    return {
      //數據綁定
      loginForm: {
        username: 'admin',
        password: '123456'
      },
      //表單驗證規則
      loginFormRules: {
        username: [
          { required: true, message: '請輸入登錄名', trigger: 'blur' },
          {
            min: 3,
            max: 10,
            message: '登錄名長度在 3 到 10 個字符',
            trigger: 'blur'
          }
        ],
        password: [
          { required: true, message: '請輸入密碼', trigger: 'blur' },
          {
            min: 6,
            max: 15,
            message: '密碼長度在 6 到 15 個字符',
            trigger: 'blur'
          }
        ]
      }
    }
  },
  //添加行為,
  methods: {
    //添加表單重置方法
    resetLoginForm() {
      //this=>當前組件對象,其中的屬性$refs包含了設置的表單ref
      //   console.log(this)
      this.$refs.LoginFormRef.resetFields()
    },
    login() {
      //點擊登錄的時候先調用validate方法驗證表單內容是否有誤
      this.$refs.LoginFormRef.validate(async valid => {
        console.log(this.loginFormRules)
        //如果valid參數為true則驗證通過
        if (!valid) {
          return
        }

        //發送請求進行登錄
        const { data: res } = await this.$http.post('login', this.loginForm)
        //   console.log(res);
        if (res.meta.status !== 200) {
          return this.$message.error('登錄失敗:' + res.meta.msg) //console.log("登錄失敗:"+res.meta.msg)
        }

        this.$message.success('登錄成功')
        console.log(res)
        //保存token
        window.sessionStorage.setItem('token', res.data.token)
        // 導航至/home
        this.$router.push('/home')
      })
    }
  }
}
</script>

<style lang="less" scoped>
.login_container {
  background-color: #2b5b6b;
  height: 100%;
}
.login_box {
  width: 450px;
  height: 300px;
  background: #fff;
  border-radius: 3px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  .avatar_box {
    height: 130px;
    width: 130px;
    border: 1px solid #eee;
    border-radius: 50%;
    padding: 10px;
    box-shadow: 0 0 10px #ddd;
    position: absolute;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: #fff;
    img {
      width: 100%;
      height: 100%;
      border-radius: 50%;
      background-color: #eee;
    }
  }
}
.login_form {
  position: absolute;
  bottom: 0;
  width: 100%;
  padding: 0 20px;
  box-sizing: border-box;
}
.btns {
  display: flex;
  justify-content: flex-end;
}
</style>

處理步驟:

添加element-ui的表單組件

在plugins文件夾中打開element.js文件,進行element-ui的按需導入

import Vue from 'vue'
import { Button } from 'element-ui'
import { Form, FormItem } from 'element-ui'
import { Input } from 'element-ui'

Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
添加第三方字體:

復制素材中的fonts文件夾到assets中,在入口文件main.js中導入

import './assets/fonts/iconfont.css'

使用:

<el-input prefix-icon="iconfont icon-3702mima"></el-input>
添加表單驗證
  • <el-form>添加屬性:rules="loginFormRules",rules是一堆驗證規則,定義在script中
export default {
  data() {
    return {
      //表單驗證規則
      loginFormRules: {
        username: [
          { required: true, message: '請輸入登錄名', trigger: 'blur' },
          {
            min: 3,
            max: 10,
            message: '登錄名長度在 3 到 10 個字符',
            trigger: 'blur'
          }
        ],
        password: [
          { required: true, message: '請輸入密碼', trigger: 'blur' },
          {
            min: 6,
            max: 15,
            message: '密碼長度在 6 到 15 個字符',
            trigger: 'blur'
          }
        ]
      }
    }
  }

通過的 <el-form-item> prop屬性設置驗證規則

<el-form-item label="活動名稱" prop="username">

導入axios

發送ajax請求,在main.js添加:

import axios from 'axios'
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
axios:Vue.prototype.$http = axios

配置彈窗提示:
  • 在plugins文件夾中打開element.js文件,進行elementui的按需導入:

    import {Message} from 'element-ui'
    
    
登錄成功操作:

登錄成功之后,需要將后台返回的token保存到sessionStorage中,操作完畢之后,需要跳轉到/home

login() {
      //點擊登錄的時候先調用validate方法驗證表單內容是否有誤
      this.$refs.LoginFormRef.validate(async valid => {
        console.log(this.loginFormRules)
        //如果valid參數為true則驗證通過
        if (!valid) {
          return
        }

        //發送請求進行登錄
        const { data: res } = await this.$http.post('login', this.loginForm)
        //   console.log(res);
        if (res.meta.status !== 200) {
          return this.$message.error('登錄失敗:' + res.meta.msg) //console.log("登錄失敗:"+res.meta.msg)
        }

        this.$message.success('登錄成功')
        console.log(res)
        //保存token
        window.sessionStorage.setItem('token', res.data.token)
        // 導航至/home
        this.$router.push('/home')
      })
    }

再添加一個組件Home.vue,並為之添加規則

添加路由守衛

如果用戶沒有登錄,不能訪問/home,如果用戶通過url地址直接訪問,則強制跳轉到登錄頁面,router.js:

import Vue from 'vue'
import Router from 'vue-router'
import Login from './components/Login.vue'
import Home from './components/Home.vue'

Vue.use(Router)

const router = new Router({
  routes: [
    { path:'/', redirect:'/login'},
    { path:'/login' , component:Login },
    { path:'/home' , component:Home}
  ]
})

//掛載路由導航守衛,to表示將要訪問的路徑,from表示從哪里來,next是下一個要做的操作
router.beforeEach((to,from,next)=>{ 
  if(to.path === '/login')
    return next();
  
  //獲取token
  const tokenStr = window.sessionStorage.getItem('token');

  if(!tokenStr)
    return next('/login');

  next();

})

export default router 

實現退出功能

在Home組件中添加一個退出功能按鈕,給退出按鈕添加點擊事件,添加事件處理代碼如下:

export default {
    methods:{
        logout(){
            window.sessionStorage.clear();
            this.$router.push('/login');
        }
    }
}

補充
  • 處理ESLint警告:

默認情況下,ESLint和vscode格式化工具有沖突,需要添加配置文件解決沖突。在項目根目錄添加 .prettierrc 文件

{
    "semi":false,
    "singleQuote":true
}

打開.eslintrc.js文件,禁用對 space-before-function-paren 的檢查:

  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'space-before-function-paren' : 0
  },

day02

后台首頁基本布局

Home.vue組件,進行布局:

<el-container class="home-container">
  <!-- 頭部區域 -->
  <el-header>Header<el-button type="info" @click="logout"> 退出 </el-button></el-header>
  <!-- 頁面主體區域 -->
  <el-container>
    <!-- 側邊欄 -->
    <el-aside width="200px">Aside</el-aside>
    <!-- 主體結構 -->
    <el-main>Main</el-main>
  </el-container>
</el-container>

默認情況下,跟element-ui組件同名的類名可以幫助我們快速的給對應的組件添加樣式,如:

.home-container {
  height: 100%;
}
.el-header{
  background-color:#373D41;
}
.el-aside{
  background-color:#333744;
}
.el-main{
  background-color:#eaedf1;
}

頂部與側邊欄布局

<template>
    <el-container class="home-container">
      <!-- 頭部區域 -->
      <el-header>
        <div>
          <!-- 黑馬logo -->
          <img src="../assets/heima.png" alt="">
          <!-- 頂部標題 -->
          <span>電商后台管理系統</span>
        </div>
        <el-button type="info" @click="logout"> 退出 </el-button>
      </el-header>
      <!-- 頁面主體區域 -->
      <el-container>
        <!-- 側邊欄 -->
        <el-aside width="200px">
          <!-- 側邊欄菜單 -->
          <el-menu
            background-color="#333744"
            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 index="1-4-1">
                <!-- 二級菜單模板 -->
                <template slot="title">
                  <!-- 圖標 -->
                  <i class="el-icon-location"></i>
                  <!-- 文本 -->
                  <span>子菜單一</span>
                </template>
              </el-menu-item>
            </el-submenu>
            
          </el-menu>
        </el-aside>
        <!-- 主體結構 -->
        <el-main>Main</el-main>
      </el-container>
    </el-container>
</template>

axios請求攔截器

后台除了登錄接口之外,都需要token權限驗證,我們可以通過添加axios請求攔截器來添加token,以保證擁有獲取數據的權限。在main.js中添加代碼,在將axios掛載到vue原型之前添加下面的代碼:

//請求在到達服務器之前,先會調用use中的這個回調函數來添加請求頭信息
axios.interceptors.request.use(config=>{
  //為請求頭對象,添加token驗證的Authorization字段
  config.headers.Authorization = window.sessionStorage.getItem("token")
  return config
})

側邊欄數據

<script>
export default {
  data() {
    return {
      // 左側菜單數據
      menuList: null
    }
  },
  created() {
    // 在created階段請求左側菜單數據
    this.getMenuList()
  },
  methods: {
    logout() {
      window.sessionStorage.clear()
      this.$router.push('/login')
    },
    async getMenuList() {
      // 發送請求獲取左側菜單數據
      const { data: res } = await this.$http.get('menus')
      if (res.meta.status !== 200) return this.$message.error(res.meta.msg)

      this.menuList = res.data
      console.log(res)
    }
  }
}
</script>

通過v-for雙重循環渲染左側菜單:

        <el-menu
          background-color="#333744"
          text-color="#fff"
          unique-opened
          :collapse="isCollapse"
          :collapse-transition="false"
          :router="true"
          :default-active="activePath"
        >
          <!-- 一級菜單 -->
          <el-submenu
            :index="item.id + ''"
            v-for="item in menuList"
            :key="item.id"
          >
            <template slot="title" class="firstSub">
              <i :class="iconsObj[item.id]"></i>
              <span>{{ item.authName }}</span>
            </template>
            <!-- 二級表單 -->
            <el-menu-item
              :index="'/' + subItem.path"
              v-for="subItem in item.children"
              :key="subItem.id"
              @click="saveNavState('/' + subItem.path)"
            >
              <template slot="title">
                <i class="el-icon-menu"></i>
                <span>{{ subItem.authName }}</span>
              </template>
            </el-menu-item>
          </el-submenu>
        </el-menu>

設置激活子菜單樣式

通過更改el-menu的active-text-color屬性可以設置側邊欄菜單中點擊的激活項的文字顏色。
通過更改菜單項模板(template)中的i標簽的類名,可以將左側菜單欄的圖標進行設置,我們需要在項目中使用第三方字體圖標,在數據中添加一個iconsObj:

iconsObj: {
        '125':'iconfont icon-user',
        '103':'iconfont icon-tijikongjian',
        '101':'iconfont icon-shangpin',
        '102':'iconfont icon-danju',
        '145':'iconfont icon-baobiao'
      }

然后將圖標類名進行數據綁定,綁定iconsObj中的數據:

為了保持左側菜單每次只能打開一個,顯示其中的子菜單,我們可以在el-menu中添加一個屬性unique-opened
或者也可以數據綁定進行設置(此時true認為是一個bool值,而不是字符串) :unique-opened="true"

側邊菜單欄的伸縮功能

        <!-- 側邊欄,寬度根據是否折疊進行設置 -->
        <el-aside :width="isCollapse ? '64px':'200px'">
          <!-- 伸縮側邊欄按鈕 -->
          <div class="toggle-button" @click="toggleCollapse">|||</div>
          <!-- 側邊欄菜單,:collapse="isCollapse"(設置折疊菜單為綁定的 isCollapse 值),:collapse-transition="false"(關閉默認的折疊動畫) -->
          <el-menu
          :collapse="isCollapse"
          :collapse-transition="false"
          ......

然后給div添加樣式,給div添加事件:

Welcome.vue

在router.js中導入子級路由組件,並設置路由規則以及子級路由的默認重定向,打開Home.vue,在main的主體結構中添加一個路由占位符。

制作好了Welcome子級路由之后,我們需要將所有的側邊欄二級菜單都改造成子級路由鏈接。
我們只需要將el-menu的router屬性設置為true就可以了,此時當我們點擊二級菜單的時候,就會根據菜單的index屬性進行路由跳轉,如: /110,使用index id來作為跳轉的路徑不合適,我們可以重新綁定index的值為:index="'/'+subItem.path"

完整home.vue代碼

<template>
  <el-container class="home-container">
    <!-- 頭部區域 -->
    <el-header>
      <div>
        <img src="../assets/vue.png" alt="" />
        <span>電商后台管理系統</span>
      </div>
      <el-button type="info" @click="loginOut">退出</el-button>
    </el-header>
    <!-- 頁面主體區域 -->
    <el-container>
      <!-- 側邊欄 -->
      <el-aside :width="isCollapse ? '64px' : '200px'">
        <!-- 折疊所有菜單按鈕 -->
        <div class="toggle-button" @click="toggleCollapse">|||</div>
        <!-- 菜單所有按鈕 -->
        <el-menu
          background-color="#333744"
          text-color="#fff"
          unique-opened
          :collapse="isCollapse"
          :collapse-transition="false"
          :router="true"
          :default-active="activePath"
        >
          <!-- 一級菜單 -->
          <el-submenu
            :index="item.id + ''"
            v-for="item in menuList"
            :key="item.id"
          >
            <template slot="title" class="firstSub">
              <i :class="iconsObj[item.id]"></i>
              <span>{{ item.authName }}</span>
            </template>
            <!-- 二級表單 -->
            <el-menu-item
              :index="'/' + subItem.path"
              v-for="subItem in item.children"
              :key="subItem.id"
              @click="saveNavState('/' + subItem.path)"
            >
              <template slot="title">
                <i class="el-icon-menu"></i>
                <span>{{ subItem.authName }}</span>
              </template>
            </el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>
      <!-- 主體 -->
      <el-main>
        <!-- 路由占位符 -->
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default {
  created() {
    this.getMenuList()
    this.activePath = window.sessionStorage.getItem('activePath')
  },
  data() {
    return {
      menuList: [],
      iconsObj: {
        '125': 'iconfont icon-user',
        '103': 'iconfont icon-tijikongjian',
        '101': 'iconfont icon-shangpin',
        '102': 'iconfont icon-danju',
        '145': 'iconfont icon-baobiao'
      },
      isCollapse: false,
      activePath: ''
    }
  },
  methods: {
    loginOut() {
      window.sessionStorage.clear()
      this.$router.push('/login')
    },
    // 獲取所有菜單
    async getMenuList() {
      const { data: res } = await this.$http.get('menus')
      if (res.meta.status !== 200) return this.$message.error(res.meta.msg)
      this.menuList = res.data
    },
    // 點擊按鈕,切換菜單的折疊與展開
    toggleCollapse() {
      this.isCollapse = !this.isCollapse
    },
    saveNavState(activePath) {
      window.sessionStorage.setItem('activePath', activePath)
      this.activePath = activePath
    }
  }
}
</script>
<style lang="less" scoped>
.home-container {
  height: 100%;
}
.el-header {
  background-color: #373d41;
  display: flex;
  justify-content: space-between;
  padding-left: 0;
  align-items: center;
  color: #fff;
  font-size: 20px;
  > div {
    display: flex;
    align-items: center;
    img {
      width: 40px;
      height: 40px;
    }
    span {
      margin-left: 15px !important;
    }
  }
}

.el-aside {
  background-color: #333744;
  .el-menu {
    border-right: none;
  }
}

.el-main {
  background-color: #eaedf1;
}

.el-submenu {
  span {
    margin-left: 10px;
  }
}

.el-icon-menu {
  margin-right: 0px !important;
}

.toggle-button {
  background-color: #4a5064;
  font-size: 10px;
  line-height: 24px;
  text-align: center;
  color: #fff;
  letter-spacing: 0.2em;
  cursor: pointer;
}
</style>

用戶列表

  1. 新建用戶列表組件 user/Users.vue

  2. 在router.js中導入子級路由組件Users.vue,並設置路由規則

  3. 當點擊二級菜單的時候,被點擊的二級子菜單並沒有高亮,我們需要正在被使用的功能高亮顯示。我們可以通過設置el-menu的default-active屬性來設置當前激活菜單的index,但是default-active屬性也不能寫死,固定為某個菜單值,所以我們可以先給所有的二級菜單添加點擊事件,並將path值作為方法的參數

    @click="saveNavState('/'+subItem.path)"
    // 在saveNavState方法中將path保存到sessionStorage中
    saveNavState( path ){
      //點擊二級菜單的時候保存被點擊的二級菜單信息
      window.sessionStorage.setItem("activePath",path);
      this.activePath = path;
    }然后在數據中添加一個activePath綁定數據,並將el-menu的default-active屬性設置為activePath
    
    
  4. 然后在數據中添加一個activePath綁定數據,並將el-menu的default-active屬性設置為activePath

  5. 最后在created中將sessionStorage中的數據賦值給activePath
    this.activePath =window.sessionStorage.getItem("activePath")

用戶列表基本結構

  1. .使用element-ui面包屑組件完成頂部導航路徑(復制面包屑代碼,在element.js中導入組件Breadcrumb,BreadcrumbItem)
  2. .使用element-ui卡片組件完成主體表格(復制卡片組件代碼,在element.js中導入組件Card),再使用element-ui輸入框完成搜索框及搜索按鈕,
  3. 此時我們需要使用柵格布局來划分結構(復制卡片組件代碼,在element.js中導入組件Row,Col),然后再使用el-button制作添加用戶按鈕
<div>
    <h3>用戶列表組件</h3>
    <!-- 面包屑導航 -->
    <el-breadcrumb separator="/">
        <el-breadcrumb-item :to="{ path: '/home' }">首頁</el-breadcrumb-item>
        <el-breadcrumb-item>用戶管理</el-breadcrumb-item>
        <el-breadcrumb-item>用戶列表</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 卡片視圖區域 -->
    <el-card>
        <!-- 搜索與添加區域 -->
        <el-row :gutter="20">
            <el-col :span="7">
                <el-input placeholder="請輸入內容">
                    <el-button slot="append" icon="el-icon-search"></el-button>
                </el-input> 
            </el-col>
            <el-col :span="4">
                <el-button type="primary">添加用戶</el-button>
            </el-col>
        </el-row> 
    </el-card>
</div>

請求用戶列表數據

<script>
export default {
  data() {
    return {
      //獲取查詢用戶信息的參數
      queryInfo: {
        query: '',
        pagenum: 1,
        pagesize: 2
      },
      //保存請求回來的用戶列表數據
      userList:[],
      total:0
    }
  },
  created() {
    this.getUserList()
  },
  methods: {
    async getUserList() {
      //發送請求獲取用戶列表數據
      const { res: data } = await this.$http.get('users', {
        params: this.queryInfo
      })
      //如果返回狀態為異常狀態則報錯並返回
      if (res.meta.status !== 200)
        return this.$message.error('獲取用戶列表失敗')
      //如果返回狀態正常,將請求的數據保存在data中
      this.userList = res.data.users;
      this.total = res.data.total;
    }
  }
}
</script>

展示用戶列表數據

  1. 使用表格來展示用戶列表數據,使用element-ui表格組件完成列表展示數據(復制表格代碼,在element.js中導入組件Table,TableColumn),在渲染展示狀態時,會使用作用域插槽獲取每一行的數據

  2. 再使用switch開關組件展示狀態信息(復制開關組件代碼,在element.js中導入組件Switch)

而渲染操作列時,也是使用作用域插槽來進行渲染的,在操作列中包含了修改,刪除,分配角色按鈕,當我們把鼠標放到分配角色按鈕上時希望能有一些文字提示,此時我們需要使用文字提示組件(復制文字提示組件代碼,在element.js中導入組件Tooltip),將分配角色按鈕包含
代碼結構如下:

<!-- 用戶列表(表格)區域 -->
<el-table :data="userList" border stripe>
    <el-table-column type="index"></el-table-column>
    <el-table-column label="姓名" prop="username"></el-table-column>
    <el-table-column label="郵箱" prop="email"></el-table-column>
    <el-table-column label="電話" prop="mobile"></el-table-column>
    <el-table-column label="角色" prop="role_name"></el-table-column>
    <el-table-column label="狀態">
        <template slot-scope="scope">
            <el-switch v-model="scope.row.mg_state"></el-switch>
        </template>
    </el-table-column>
    <el-table-column label="操作" width="180px">
        <template slot-scope="scope">
            <!-- 修改 -->
            <el-button type="primary" icon="el-icon-edit" size='mini'></el-button>
            <!-- 刪除 -->
            <el-button type="danger" icon="el-icon-delete" size='mini'></el-button>
            <!-- 分配角色 -->
            <el-tooltip class="item" effect="dark" content="分配角色" placement="top" :enterable="false">
                <el-button type="warning" icon="el-icon-setting" size='mini'></el-button>
            </el-tooltip>
        </template>
    </el-table-column>
</el-table>

實現用戶列表分頁

  1. .使用表格來展示用戶列表數據,可以使用分頁組件完成列表分頁展示數據(復制分頁組件代碼,在element.js中導入組件Pagination)
  2. 更改組件中的綁定數據
    <!-- 分頁導航區域 
    @size-change(pagesize改變時觸發) 
    @current-change(頁碼發生改變時觸發)
    :current-page(設置當前頁碼)
    :page-size(設置每頁的數據條數)
    :total(設置總頁數) -->
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="queryInfo.pagenum"
        :page-sizes="[1, 2, 5, 10]"
        :page-size="queryInfo.pagesize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
      >
      </el-pagination>

  1. 添加兩個事件的事件處理函數@size-change,@current-change
handleSizeChange(newSize) {
  //pagesize改變時觸發,當pagesize發生改變的時候,我們應該
  //以最新的pagesize來請求數據並展示數據
  //   console.log(newSize)
  this.queryInfo.pagesize = newSize;
  //重新按照pagesize發送請求,請求最新的數據
  this.getUserList();  
},
handleCurrentChange( current ) {
  //頁碼發生改變時觸發當current發生改變的時候,我們應該
  //以最新的current頁碼來請求數據並展示數據
  //   console.log(current)
  this.queryInfo.pagenum = current;
  //重新按照pagenum發送請求,請求最新的數據
  this.getUserList();  
}

實現更新用戶狀態

當用戶點擊列表中的switch組件時,用戶的狀態應該跟隨發生改變。

  1. 首先監聽用戶點擊switch組件的事件,並將作用域插槽的數據當做事件參數進行傳遞
<el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)"></el-switch>

  1. 在事件中發送請求完成狀態的更改
async userStateChanged(row) {
  //發送請求進行狀態修改
  const { data: res } = await this.$http.put(
    `users/${row.id}/state/${row.mg_state}`
  )
  //如果返回狀態為異常狀態則報錯並返回
  if (res.meta.status !== 200) {
    row.mg_state = !row.mg_state
    return this.$message.error('修改狀態失敗')
  }
  this.$message.success('更新狀態成功')
},

實現搜索功能

添加數據綁定,添加搜索按鈕的點擊事件(當用戶點擊搜索按鈕的時候,調用getUserList方法根據文本框內容重新請求用戶列表數據),當我們在輸入框中輸入內容並點擊搜索之后,會按照搜索關鍵字搜索,我們希望能夠提供一個X刪除搜索關鍵字並重新獲取所有的用戶列表數據,只需要給文本框添加clearable屬性並添加clear事件,在clear事件中重新請求數據即可。

<el-col :span="7">
    <el-input placeholder="請輸入內容" v-model="queryInfo.query" clearable @clear="getUserList">
        <el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button>
    </el-input>
</el-col>

實現添加用戶

  1. .當我們點擊添加用戶按鈕的時候,彈出一個對話框來實現添加用戶的功能,首先我們需要復制對話框組件的代碼並在element.js文件中引入Dialog組件

  2. 接下來我們要為“添加用戶”按鈕添加點擊事件,在事件中將addDialogVisible設置為true,即顯示對話框

  3. 更改Dialog組件中的內容

<!-- 對話框組件  :visible.sync(設置是否顯示對話框) width(設置對話框的寬度)
:before-close(在對話框關閉前觸發的事件) -->
<el-dialog title="添加用戶" :visible.sync="addDialogVisible" width="50%">
    <!-- 對話框主體區域 -->
    <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="70px">
        <el-form-item label="用戶名" prop="username">
            <el-input v-model="addForm.username"></el-input>
        </el-form-item>
        <el-form-item label="密碼" prop="password">
            <el-input v-model="addForm.password"></el-input>
        </el-form-item>
        <el-form-item label="郵箱" prop="email">
            <el-input v-model="addForm.email"></el-input>
        </el-form-item>
        <el-form-item label="電話" prop="mobile">
            <el-input v-model="addForm.mobile"></el-input>
        </el-form-item>
    </el-form>
    <!-- 對話框底部區域 -->
    <span slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="addDialogVisible = false">確 定</el-button>
    </span>
</el-dialog>

  1. 添加數據綁定和校驗規則:
data() {
  //驗證郵箱的規則
  var checkEmail = (rule, value, cb) => {
    const regEmail = /^\w+@\w+(\.\w+)+$/
    if (regEmail.test(value)) {
      return cb()
    }
    //返回一個錯誤提示
    cb(new Error('請輸入合法的郵箱'))
  }
  //驗證手機號碼的規則
  var checkMobile = (rule, value, cb) => {
    const regMobile = /^1[34578]\d{9}$/
    if (regMobile.test(value)) {
      return cb()
    }
    //返回一個錯誤提示
    cb(new Error('請輸入合法的手機號碼'))
  }
  return {
    //獲取查詢用戶信息的參數
    queryInfo: {
      // 查詢的條件
      query: '',
      // 當前的頁數,即頁碼
      pagenum: 1,
      // 每頁顯示的數據條數
      pagesize: 2
    },
    //保存請求回來的用戶列表數據
    userList: [],
    total: 0,
    //是否顯示添加用戶彈出窗
    addDialogVisible: false,
    // 添加用戶的表單數據
    addForm: {
      username: '',
      password: '',
      email: '',
      mobile: ''
    },
    // 添加表單的驗證規則對象
    addFormRules: {
      username: [
        { required: true, message: '請輸入用戶名稱', trigger: 'blur' },
        {
          min: 3,
          max: 10,
          message: '用戶名在3~10個字符之間',
          trigger: 'blur'
        }
      ],
      password: [
        { required: true, message: '請輸入密碼', trigger: 'blur' },
        {
          min: 6,
          max: 15,
          message: '用戶名在6~15個字符之間',
          trigger: 'blur'
        }
      ],
      email: [
          { required: true, message: '請輸入郵箱', trigger: 'blur' },
          { validator:checkEmail, message: '郵箱格式不正確,請重新輸入', trigger: 'blur'}
      ],
      mobile: [
          { required: true, message: '請輸入手機號碼', trigger: 'blur' },
          { validator:checkMobile, message: '手機號碼不正確,請重新輸入', trigger: 'blur'}
      ]
    }
  }
}

  1. 當關閉對話框時,重置表單。給el-dialog添加@close事件,在事件中添加重置表單的代碼
methods:{
  ...
  addDialogClosed(){
      //對話框關閉之后,重置表達
      this.$refs.addFormRef.resetFields();
  }
}

  1. 點擊對話框中的確定按鈕,發送請求完成添加用戶的操作
    • 首先給確定按鈕添加點擊事件,在點擊事件中完成業務邏輯代碼
methods:{
  ....
  addUser(){
      //點擊確定按鈕,添加新用戶
      //調用validate進行表單驗證
      this.$refs.addFormRef.validate( async valid => {
          if(!valid) return this.$message.error("請填寫完整用戶信息");
          //發送請求完成添加用戶的操作
          const {data:res} = await this.$http.post("users",this.addForm)
          //判斷如果添加失敗,就做提示
          if (res.meta.status !== 200)
              return this.$message.error('添加用戶失敗')
          //添加成功的提示
          this.$message.success("添加用戶成功")
          //關閉對話框
          this.addDialogVisible = false
          //重新請求最新的數據
          this.getUserList()
      })
  }
}

day03

修改用戶

  1. 為用戶列表中的修改按鈕綁定點擊事件
  2. .在頁面中添加修改用戶對話框,並修改對話框的屬性
  3. .根據id查詢需要修改的用戶數據
//展示編輯用戶的對話框
async showEditDialog(id) {
    //發送請求根據id獲取用戶信息
    const { data: res } = await this.$http.get('users/' + id)
    //判斷如果添加失敗,就做提示
    if (res.meta.status !== 200) return this.$message.error('獲取用戶信息失敗')
    //將獲取到的數據保存到數據editForm中
    this.editForm = res.data
    //顯示彈出窗
    this.editDialogVisible = true
}

  1. 在彈出窗中添加修改用戶信息的表單並做響應的數據綁定以及數據驗證
<!-- 對話框主體區域 -->
<el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="70px">
    <el-form-item label="用戶名">
        <el-input v-model="editForm.username" disabled></el-input>
    </el-form-item>
    <el-form-item label="郵箱" prop="email">
        <el-input v-model="editForm.email"></el-input>
    </el-form-item>
    <el-form-item label="電話" prop="mobile">
        <el-input v-model="editForm.mobile"></el-input>
    </el-form-item>
</el-form>

數據綁定以及驗證:

//控制修改用戶對話框的顯示與否
editDialogVisible: false,
//修改用戶的表單數據
editForm: {
    username: '',
    email: '',
    mobile: ''
},
//修改表單的驗證規則對象
editFormRules: {
    email: [
        { required: true, message: '請輸入郵箱', trigger: 'blur' },
        {
        validator: checkEmail,
        message: '郵箱格式不正確,請重新輸入',
        trigger: 'blur'
        }
    ],
    mobile: [
        { required: true, message: '請輸入手機號碼', trigger: 'blur' },
        {
        validator: checkMobile,
        message: '手機號碼不正確,請重新輸入',
        trigger: 'blur'
        }
    ]
}

  1. 監聽對話框關閉事件,在對話框關閉之后,重置表單
<el-dialog title="修改用戶" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed">

editDialogClosed(){
    //對話框關閉之后,重置表達
    this.$refs.editFormRef.resetFields()
}

  1. 在用戶點擊確定按鈕的時候,驗證數據成功之后發送請求完成修改
editUser() {
    //用戶點擊修改表單中的確定按鈕之后,驗證表單數據
    this.$refs.editFormRef.validate(async valid => {
    if (!valid) return this.$message.error('請填寫完整用戶信息')
    //發送請求完成修改用戶的操作
    const { data: res } = await this.$http.put(
        'users/' + this.editForm.id,
        this.editForm
    )
    //判斷如果修改失敗,就做提示
    if (res.meta.status !== 200) return this.$message.error('修改用戶失敗')
    //修改成功的提示
    this.$message.success('修改用戶成功')
    //關閉對話框
    this.editDialogVisible = false
    //重新請求最新的數據
    this.getUserList()
    })
}

刪除用戶

在點擊刪除按鈕的時候,我們應該跳出提示信息框,讓用戶確認要進行刪除操作。如果想要使用確認取消提示框,我們需要先將提示信息框掛載到vue中。

  1. 導入MessageBox組件,並將MessageBox組件掛載到實例。
    Vue.prototype.$confirm = MessageBox.confirm
  2. .給用戶列表中的刪除按鈕添加事件,並在事件處理函數中彈出確定取消窗,最后再根據id發送刪除用戶的請求
async removeUserById(id){
    //彈出確定取消框,是否刪除用戶
    const confirmResult = await this.$confirm('請問是否要永久刪除該用戶','刪除提示',{
    confirmButtonText:'確認刪除',
    cancelButtonText:'取消',
    type:'warning'
    }).catch(err=>err)
    //如果用戶點擊確認,則confirmResult 為'confirm'
    //如果用戶點擊取消, 則confirmResult獲取的就是catch的錯誤消息'cancel'
    if(confirmResult != "confirm"){
        return this.$message.info("已經取消刪除")
    }
    //發送請求根據id完成刪除操作
    const {data:res} = await this.$http.delete('users/'+id)
    //判斷如果刪除失敗,就做提示
    if (res.meta.status !== 200) return this.$message.error('刪除用戶失敗')
    //修改成功的提示
    this.$message.success('刪除用戶成功')
    //重新請求最新的數據
    this.getUserList()
}

權限列表

  1. 添加權限列表路由,創建權限管理組件(Rights.vue),並在router.js添加對應的路由規則
import Rights from './components/power/Rights.vue'
......
      path: '/home', component: Home, redirect: '/welcome', children: [
        { path: "/welcome", component: Welcome },
        { path: "/users", component: Users },
        { path: "/rights", component: Rights }
      ]
......

  1. 添加面包屑導航,在Rights.vue中添加面包屑組件展示導航路徑

  2. 顯示數據在data中添加一個rightsList數據,在methods中提供一個getRightsList方法發送請求獲取權限列表數據,在created中調用這個方法獲取數據

<el-table :data="rightsList" stripe>
    <el-table-column type="index"></el-table-column>
    <el-table-column label="權限名稱" prop="authName"></el-table-column>
    <el-table-column label="路徑" prop="path"></el-table-column>
    <el-table-column label="權限等級" prop="level">
        <template slot-scope="scope"> 
            <el-tag v-if="scope.row.level === 0">一級權限</el-tag>
            <el-tag v-if="scope.row.level === 1" type="success">二級權限</el-tag>
            <el-tag v-if="scope.row.level === 2" type="warning">三級權限</el-tag>
        </template>
    </el-table-column>
</el-table>
<script>
export default {
    data(){
        return {
            //列表形式的權限
            rightsList:[]
        }
    },
    created(){
        this.getRightsList();
    },
    methods:{
        async getRightsList(){
            const {data:res} = await this.$http.get('rights/list')
            //如果返回狀態為異常狀態則報錯並返回
            if (res.meta.status !== 200)
                return this.$message.error('獲取權限列表失敗')
            //如果返回狀態正常,將請求的數據保存在data中
            this.rightsList = res.data
        }
    }
}
</script>

角色列表

  1. 添加角色列表路由,添加角色列表子組件(power/Roles.vue),並添加對應的規則
path: '/home', component: Home, redirect: '/welcome', children: [
        { path: "/welcome", component: Welcome },
        { path: "/users", component: Users },
        { path: "/rights", component: Rights },
        { path: "/roles", component: Roles  }
      ]

  1. 添加面包屑導航, 在Roles.vue中添加面包屑組件展示導航路徑

  2. 顯示數據在data中添加一個roleList數據,在methods中提供一個getRoleList方法發送請求獲取權限列表數據,在created中調用這個方法獲取數據

<!-- 角色列表區域 -->
<!-- row-key="id" 是2019年3月提供的新特性,
if there's nested data, rowKey is required.
如果這是一個嵌套的數據,rowkey 是必須添加的屬性 -->
<el-table row-key="id" :data="roleList" border>
    <!-- 添加展開列 -->
    <el-table-column type="expand"></el-table-column>
    <el-table-column type="index"></el-table-column>
    <el-table-column label="角色名稱" prop="roleName"></el-table-column>
    <el-table-column label="角色描述" prop="roleDesc"></el-table-column>
    <el-table-column label="操作" width="300px">
        <template slot-scope="scope"> 
            <el-button size="mini" type="primary" icon="el-icon-edit">編輯</el-button>
            <el-button size="mini" type="danger" icon="el-icon-delete">刪除</el-button>
            <el-button size="mini" type="warning" icon="el-icon-setting">分配權限</el-button>
        </template>
    </el-table-column>
</el-table>

<script>
export default {
    data(){
        return {
            roleList:[]
        }
    },created(){
        this.getRoleList();
    },methods:{
        async getRoleList(){
            const {data:res} = await this.$http.get('roles')
            //如果返回狀態為異常狀態則報錯並返回
            // if (res.meta.status !== 200)
            //     return this.$message.error('獲取角色列表失敗')
            // //如果返回狀態正常,將請求的數據保存在data中
            // this.roleList = res.data
            console.log(res.data)
            this.roleList = res.data;
        }
    }
}
</script>

補充說明:
之前學習過類似的添加角色,刪除角色,編輯角色請參照之前編寫過的代碼還有接口文檔完成效果。

  1. 生成權限列表, 使用三重嵌套for循環生成權限下拉列表
<!-- 添加展開列 -->
<el-table-column type="expand">
    <template slot-scope="scope">
        <el-row :class="['bdbottom',i1===0?'bdtop':'']" v-for="(item1,i1) in scope.row.children" :key="item1.id">
            <!-- 渲染一級權限 -->
            <el-col :span="5">
                <el-tag>
                    {{item1.authName}}
                </el-tag>
                <i class="el-icon-caret-right"></i>
            </el-col>
            <!-- 渲染二,三級權限 -->
            <el-col :span="19">
                <!-- 通過for循環嵌套渲染二級權限  -->
                <el-row :class="[i2===0?'':'bdtop' ]" v-for="(item2,i2) in item1.children" :key="item2.id">
                    <el-col :span="6">
                        <el-tag type="success">{{item2.authName}}</el-tag>
                        <i class="el-icon-caret-right"></i>
                    </el-col>
                    <el-col :span="18">
                        <el-tag type="warning" v-for="(item3) in item2.children" :key="item3.id">
                            {{item3.authName}}
                        </el-tag>
                    </el-col>
                </el-row>
            </el-col>
        </el-row>
    </template>
</el-table-column>

  1. 美化樣式

通過設置global.css中的#app樣式min-width:1366px 解決三級權限換行的問題,通過給一級權限el-row添加display:flex,align-items:center的方式解決一級權限垂直居中的問題,二級權限也類似添加,因為需要給多個內容添加,可以將這個樣式設置.vcenter{display:flex;align-items:center}

  1. 添加權限刪除功能, 給每一個權限的el-tag添加closable屬性,是的權限右側出現“X”圖標再給el-tag添加綁定close事件處理函數
async removeRightById(role,rightId){
    //彈窗提示用戶是否要刪除
    const confirmResult = await this.$confirm('請問是否要刪除該權限','刪除提示',{
        confirmButtonText:'確認刪除',
        cancelButtonText:'取消',
        type:'warning'
    }).catch(err=>err)
    //如果用戶點擊確認,則confirmResult 為'confirm'
    //如果用戶點擊取消, 則confirmResult獲取的就是catch的錯誤消息'cancel'
    if(confirmResult != "confirm"){
        return this.$message.info("已經取消刪除")
    }

    //用戶點擊了確定表示真的要刪除
    //當發送delete請求之后,返回的數據就是最新的角色權限信息
    const {data:res} = await this.$http.delete(`roles/${role.id}/rights/${rightId}`)
    if (res.meta.status !== 200)
        return this.$message.error('刪除角色權限失敗')

    //無需再重新加載所有權限
    //只需要對現有的角色權限進行更新即可
    role.children = res.data
    // this.getRoleList();

}

  1. 完成權限分配功能, 先給分配權限按鈕添加事件
async showSetRightDialog() {
    //當點擊分配權限按鈕時,展示對應的對話框
    this.setRightDialogVisible = true;
    //獲取所有權限的數據
    const {data:res} = await this.$http.get('rights/tree')
    //如果返回狀態為異常狀態則報錯並返回
    if (res.meta.status !== 200)
        return this.$message.error('獲取權限樹失敗')
    //如果返回狀態正常,將請求的數據保存在data中
    this.rightsList = res.data
}

添加分配權限對話框,並添加綁定數據setRightDialogVisible

  1. 完成樹形結構彈窗,在element.js中引入Tree,注冊Tree
<!-- 分配權限對話框 -->
<el-dialog title="分配權限" :visible.sync="setRightDialogVisible" width="50%" @close="setRightDialogClose">
    <!-- 樹形組件
    show-checkbox:顯示復選框
    node-key:設置選中節點對應的值
    default-expand-all:是否默認展開所有節點
    :default-checked-keys 設置默認選中項的數組
    ref:設置引用 -->
    <el-tree :data="rightsList" :props="treeProps" show-checkbox node-key="id" default-expand-all :default-checked-keys="defKeys" ref="treeRef"></el-tree>
    <span slot="footer" class="dialog-footer">
        <el-button @click="setRightDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="allotRights">確 定</el-button>
    </span>
</el-dialog>

<script>
export default {
  data() {
    return {
      //角色列表數據
      roleList: [],
      //控制分配權限對話框的顯示
      setRightDialogVisible: false,
      //權限樹數據
      rightsList: [],
      //樹形控件的屬性綁定對象
      treeProps: {
        //通過label設置樹形節點文本展示authName
        label: 'authName',
        //設置通過children屬性展示子節點信息
        children: 'children'
      },
      //設置樹形控件中默認選中的內容
      defKeys: [],
      //保存正在操作的角色id
      roleId:''
    }
  },
  created() {
    this.getRoleList()
  },
  methods: {
    async getRoleList() {
      const { data: res } = await this.$http.get('roles')
      //如果返回狀態為異常狀態則報錯並返回
      if (res.meta.status !== 200)
        return this.$message.error('獲取角色列表失敗')
      //如果返回狀態正常,將請求的數據保存在data中
      // this.roleList = res.data
      console.log(res.data)
      this.roleList = res.data
    },
    async removeRightById(role, rightId) {
      //彈窗提示用戶是否要刪除
      const confirmResult = await this.$confirm(
        '請問是否要刪除該權限',
        '刪除提示',
        {
          confirmButtonText: '確認刪除',
          cancelButtonText: '取消',
          type: 'warning'
        }
      ).catch(err => err)
      //如果用戶點擊確認,則confirmResult 為'confirm'
      //如果用戶點擊取消, 則confirmResult獲取的就是catch的錯誤消息'cancel'
      if (confirmResult != 'confirm') {
        return this.$message.info('已經取消刪除')
      }

      //用戶點擊了確定表示真的要刪除
      //當發送delete請求之后,返回的數據就是最新的角色權限信息
      const { data: res } = await this.$http.delete(
        `roles/${role.id}/rights/${rightId}`
      )
      if (res.meta.status !== 200)
        return this.$message.error('刪除角色權限失敗')

      //無需再重新加載所有權限
      //只需要對現有的角色權限進行更新即可
      role.children = res.data
      // this.getRoleList();
    },
    async showSetRightDialog(role) {
      //將role.id保存起來以供保存權限時使用
      this.roleId = role.id;  
      //獲取所有權限的數據
      const { data: res } = await this.$http.get('rights/tree')
      //如果返回狀態為異常狀態則報錯並返回
      if (res.meta.status !== 200) return this.$message.error('獲取權限樹失敗')
      //如果返回狀態正常,將請求的數據保存在data中
      this.rightsList = res.data

      //調用getLeafKeys進行遞歸,將三級權限添加到數組中
      this.getLeafKeys(role, this.defKeys)
      //當點擊分配權限按鈕時,展示對應的對話框
      this.setRightDialogVisible = true
      console.log(this.defKeys)
    },
    getLeafKeys(node, arr) {
      //該函數會獲取到當前角色的所有三級權限id並添加到defKeys中
      //如果當前節點不包含children屬性,則表示node為三級權限
      if (!node.children) {
        return arr.push(node.id)
      }
      //遞歸調用
      node.children.forEach(item => this.getLeafKeys(item, arr))
    },
    setRightDialogClose() {
      //當用戶關閉樹形權限對話框的時候,清除掉所有選中狀態
      this.defKeys = []
    },
    async allotRights() {
      //當用戶在樹形權限對話框中點擊確定,將用戶選擇的
      //權限發送請求進行更新

      //獲取所有選中及半選的內容
      const keys = [
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys()
      ]
      //將數組轉換為 , 拼接的字符串
      const idStr = keys.join(',')

      //發送請求完成更新
      const { data: res } = await this.$http.post(
        `roles/${this.roleId}/rights`,
        { rids:idStr }
      )
      if (res.meta.status !== 200)
        return this.$message.error('分配權限失敗')

      this.$message.success("分配權限成功")
      this.getRoleList();
      //關閉對話框
      this.setRightDialogVisible = false;
    }
  }
}
</script>

分配角色

打開Users.vue,完成分配角色的功能

  1. 添加分配角色對話框
<!-- 分配角色對話框 -->
<el-dialog title="分配角色" :visible.sync="setRoleDialogVisible" width="50%">
    <div>
    <p>當前的用戶:{{userInfo.username}}</p>
    <p>當前的角色:{{userInfo.role_name}}</p>
    <p>分配新角色:</p>
    </div>
    <span slot="footer" class="dialog-footer">
    <el-button @click="setRoleDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="setRoleDialogVisible = false">確 定</el-button>
    </span>
</el-dialog>

  1. 給分配角色按鈕添加點擊事件,點擊之后彈出一個對話框進行角色分配
<!-- 分配角色 -->
<el-tooltip class="item" effect="dark" content="分配角色" placement="top" :enterable="false">
    <el-button type="warning" icon="el-icon-setting" size='mini' @click="setRole(scope.row)"></el-button>
</el-tooltip>
<script>
data(){
    ......
    //控制顯示分配角色對話框
    setRoleDialogVisible:false,
    //保存正在操作的那個用戶信息
    userInfo:{},
    //保存所有的角色信息
    rolesList:[],
    //保存用戶選中的角色id
    selectedRoleId:''
},
methods:{
    ......
    async setRole( userInfo ){
      //保存起來以供后續使用
      this.userInfo = userInfo;
      //獲取所有的角色信息,以備下拉列表使用
      //發送請求根據id完成刪除操作
      const { data: res } = await this.$http.get('roles')
      //判斷如果刪除失敗,就做提示
      if (res.meta.status !== 200) return this.$message.error('獲取角色列表失敗')
      
      this.rolesList = res.data;
      //展示分配角色對話框
      this.setRoleDialogVisible = true;

      
    }
}
</script>

  1. 在element.js中引入Select,Option,注冊Select,Option
<!-- 角色選擇下拉框
v-model:設置用戶選中角色之后的id綁定數據
-->
<el-select v-model="selectedRoleId" placeholder="請選擇角色">
<!-- :label 顯示文本,:value 選中值 -->
<el-option v-for="item in rolesList" :key="item.id" :label="item.roleName" :value="item.id">
</el-option>
</el-select>

  1. 當用戶點擊對話框中的確定之后,完成分配角色的操作
<!-- 分配角色對話框 -->
<el-dialog title="分配角色" :visible.sync="setRoleDialogVisible" width="50%" @close="setRoleDialogClosed">
    <div>
    <p>當前的用戶:{{userInfo.username}}</p>
    <p>當前的角色:{{userInfo.role_name}}</p>
    <p>分配新角色:
        <!-- 角色選擇下拉框
        v-model:設置用戶選中角色之后的id綁定數據
        -->
        <el-select v-model="selectedRoleId" placeholder="請選擇角色">
        <!-- :label 顯示文本,:value 選中值 -->
        <el-option v-for="item in rolesList" :key="item.id" :label="item.roleName" :value="item.id">
        </el-option>
        </el-select>
    </p>
    </div>
    <span slot="footer" class="dialog-footer">
    <el-button @click="setRoleDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="saveRoleInfo">確 定</el-button>
    </span>
</el-dialog>

<script>
methods:{
    .......
    async saveRoleInfo(){
      //當用戶點擊確定按鈕之后
      //判斷用戶是否選擇了需要分配的角色
      if(!this.selectedRoleId){
        return this.$message.error('請選擇需要分配的角色')
      }
      //發送請求完成分配角色的操作
      const {data:res} = await this.$http.put(`users/${this.userInfo.id}/role`,{rid:this.selectedRoleId})

      //判斷如果刪除失敗,就做提示
      if (res.meta.status !== 200)
        return this.$message.error('分配角色失敗')

      this.$message.success('分配角色成功')
      this.getUserList();
      //關閉對話框
      this.setRoleDialogVisible = false
    },
    setRoleDialogClosed(){
      //當關閉對話框的時候,重置下拉框中的內容
      this.selectedRoleId = ''
      this.userInfo = {}
    }
}
</script>

day04

商品分類

  1. 創建categories子級路由組件並設置路由規則
import Cate from './components/goods/Cate.vue'

path: '/home', component: Home, redirect: '/welcome', children: [
    { path: "/welcome", component: Welcome },
    { path: "/users", component: Users },
    { path: "/rights", component: Rights },
    { path: "/roles", component: Roles  },
    { path: "/categories", component: Cate  }
]

  1. 添加組件基本布局, 在Cate.vue組件中添加面包屑導航以及卡片視圖中的添加分類按鈕
<template>
    <div>
        <h3>商品分類</h3>
        <!-- 面包屑導航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首頁</el-breadcrumb-item>
            <el-breadcrumb-item>商品管理</el-breadcrumb-item>
            <el-breadcrumb-item>商品分類</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片視圖區域 -->
        <el-card>
            <!-- 添加分類按鈕區域 -->
            <el-row>
                <el-col>
                    <el-button type="primary">添加分類</el-button>
                </el-col>
            </el-row>
            <!-- 分類表格  -->

            <!-- 分頁 -->
        </el-card>
    </div>
</template>

  1. 請求分類數據, 請求分類數據並將數據保存在data中
<script>
export default {
  data() {
    return {
      // 商品分類數據列表
      cateList: [],
      //查詢分類數據的條件
      queryInfo: {
        type: 3,
        pagenum: 1,
        pagesize: 5
      },
      //保存總數據條數
      total: 0
    }
  },
  created() {
    this.getCateList()
  },
  methods: {
    async getCateList() {
      //獲取商品分類數據
      const { data: res } = await this.$http.get('categories', {
        params: queryInfo
      })

      if (res.meta.status !== 200) {
        return this.$message.error('獲取商品列表數據失敗')
      }
      //將數據列表賦值給cateList
      this.cateList = res.data.result
      //保存總數據條數
      this.total = res.data.total
      //   console.log(res.data);
    }
  }
}
</script>

  1. 使用插件展示數據, 使用第三方插件vue-table-with-tree-grid展示分類數據

    • 在vue 控制台中點擊依賴->安裝依賴->運行依賴->輸入vue-table-with-tree-gird->點擊安裝

    • 打開main.js,導入vue-table-with-tree-grid

      import TreeTable from 'vue-table-with-tree-grid'
         .....
      Vue.config.productionTip = false
      
      //全局注冊組件
      Vue.component('tree-table', TreeTable)
      3).使用組件展示分類數據
      
      
<!-- 分類表格
:data(設置數據源) :columns(設置表格中列配置信息) :selection-type(是否有復選框)
:expand-type(是否展開數據) show-index(是否設置索引列) index-text(設置索引列頭)
border(是否添加縱向邊框) :show-row-hover(是否鼠標懸停高亮) -->
<tree-table :data="cateList" :columns="columns" :selection-type="false"
:expand-type="false" show-index index-text="#" border :show-row-hover="false">

</tree-table>

在數據中添加columns:
columns: [
    {label:'分類名稱',prop:'cat_name'}
]

  1. 自定義數據列, 使用vue-table-with-tree-grid定義模板列並添加自定義列
<script>
//先在columns中添加一個列
columns: [
    {label:'分類名稱',prop:'cat_name'},
    //type:'template'(將該列設置為模板列),template:'isok'(設置該列模板的名稱為isok)
    {label:'是否有效',prop:'',type:'template',template:'isok'},
    {label:'排序',prop:'',type:'template',template:'order'},
    {label:'操作',prop:'',type:'template',template:'opt'}
]
</script>

<!-- 是否有效區域, 設置對應的模板列: slot="isok"(與columns中設置的template一致) -->
<template slot="isok" slot-scope="scope">
  <i class="el-icon-success" v-if="scope.row.cat_deleted === false" style="color:lightgreen"></i>
  <i class="el-icon-error" v-else style="color:red"></i>
</template>
<!-- 排序 -->
<template slot="order" slot-scope="scope">
  <el-tag size="mini" v-if="scope.row.cat_level===0">一級</el-tag>
  <el-tag size="mini" type="success" v-else-if="scope.row.cat_level===1">二級</el-tag>
  <el-tag size="mini" type="warning" v-else>三級</el-tag>
</template>

<!-- 操作 -->
<template slot="opt" slot-scope="scope">
  <el-button size="mini" type="primary" icon="el-icon-edit">編輯</el-button>
  <el-button size="mini" type="danger" icon="el-icon-delete">刪除</el-button> 
</template>

  1. 完成分頁功能
<!-- 分頁 -->
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>

<script>
//添加對應的事件函數
methods:{
  .......
  handleSizeChange(newSize){
    //當pagesize發生改變時觸發
    this.queryInfo.pagesize = newSize;
    this.getCateList();
  },
  handleCurrentChange(newPage){
    //當pagenum發生改變時觸發
    this.queryInfo.pagenum = newPage;
    this.getCateList();
  }
}
</script>

  1. 完成添加分類
......
<!-- 添加分類按鈕區域 -->
<el-row>
  <el-col>
    <el-button type="primary" @click="showAddCateDialog">添加分類</el-button>
  </el-col>
</el-row>
......
<!-- 添加分類對話框 -->
<el-dialog title="添加分類" :visible.sync="addCateDialogVisible" width="50%"  @close="addCateDialogClosed">
  <!-- 添加分類表單 -->
  <el-form :model="addCateForm" :rules="addCateFormRules" ref="addCateFormRuleForm" label-width="100px">
    <el-form-item label="分類名稱" prop="cat_name">
      <el-input v-model="addCateForm.cat_name"></el-input>
    </el-form-item>
    <el-form-item label="父級分類" prop="cat_pid">
      
    </el-form-item>
  </el-form>
  <span slot="footer" class="dialog-footer">
    <el-button @click="addCateDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="addCate">確 定</el-button>
  </span>
</el-dialog>

<script>
//用來顯示或隱藏添加分類對話框
addCateDialogVisible: false,
//添加分類的表單數據對象
addCateForm:{
  //分類名稱
  cat_name:'',
  //添加分類的父級id,0則表示父級為0.添加一級分類
  cat_pid:0,
  //添加分類的等級,0則表示添加一級分類
  cat_level:0
},
//添加分類校驗規則
addCateFormRules:{
  //驗證規則
  cat_name:[ {required:true , message:'請輸入分類名稱',trigger:'blur'} ]
},
//保存1,2級父級分類的列表
parentCateList:[]
.......
showAddCateDialog() {
  //調用getParentCateList獲取分類列表
  this.getParentCateList()
  //顯示添加分類對話框
  this.addCateDialogVisible = true
},
async getParentCateList(){
  //獲取父級分類數據列表
  const { data: res } = await this.$http.get('categories', {
    params: {type:2}
  })

  if (res.meta.status !== 200) {
    return this.$message.error('獲取商品分類列表數據失敗')
  }
  this.parentCateList = res.data
}
</script>

  1. 添加級聯菜單顯示父級分類先導入Cascader組件,並注冊
    然后添加使用級聯菜單組件:
<el-form-item label="父級分類" prop="cat_pid">
  <!-- expandTrigger='hover'(鼠標懸停觸發級聯) v-model(設置級聯菜單綁定數據) :options(指定級聯菜單數據源)  :props(用來配置數據顯示的規則) 
  clearable(提供“X”號完成刪除文本功能) change-on-select(是否可以選中任意一級的菜單) -->
  <el-cascader expandTrigger='hover' v-model="selectedKeys" :options="parentCateList" :props="cascaderProps" @change="parentCateChange" clearable change-on-select></el-cascader>
</el-form-item>

<script>
添加數據
//配置級聯菜單中數據如何展示
cascaderProps:{
  value:'cat_id',
  label:'cat_name',
  children:'children',
  expandTrigger:'hover'
},
//綁定用戶選擇的分類值
selectedKeys:[]
.....
methods:{
  .....
  parentCateChange(){
    //級聯菜單中選擇項發生變化時觸發
    console.log(this.selectedKeys)
    //如果用戶選擇了父級分類
    if(this.selectedKeys.length > 0){
      //則將數組中的最后一項設置為父級分類
      this.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1]
      //level也要跟着發生變化
      this.addCateForm.cat_level = this.selectedKeys.length
      return
    }else{
      this.addCateForm.cat_pid = 0
      this.addCateForm.cat_level = 0
      return
    }
  },
  addCateDialogClosed(){
    //當關閉添加分類對話框時,重置表單
    this.$refs.addCateFormRef.resetFields()
    this.selectedKeys = [];
    this.addCateForm.cat_pid = 0
    this.addCateForm.cat_level = 0
  },
  addCate() {
    //點擊確定,完成添加分類
    console.log(this.addCateForm)
    this.$refs.addCateFormRef.validate(async valid => {
      if (!valid) return
      //發送請求完成添加分類
      const { data: res } = await this.$http.post(
        'categories',
        this.addCateForm
      )

      if (res.meta.status !== 201) {
        return this.$message.error('添加分類失敗')
      }

      this.$message.success('添加分類成功')
      this.getCateList()
      this.addCateDialogVisible = false
    })
  }
}
</script>

參數管理

只允許給三級分類內容設置參數,參數分為動態參數和靜態參數屬性

  1. 添加子級組件, 添加Params.vue子組件,並在router.js中引入該組件並設置路由規則
import Params from './components/goods/Params.vue'
......
path: '/home', component: Home, redirect: '/welcome', children: [
  { path: "/welcome", component: Welcome },
  { path: "/users", component: Users },
  { path: "/rights", component: Rights },
  { path: "/roles", component: Roles  },
  { path: "/categories", component: Cate  },
  { path: "/params", component: Params  }
]

  1. 完成組件基本布局, 完成Params.vue組件的基本布局, 其中警告提示信息使用了el-alert,在element.js引入該組件並注冊
<template>
    <div>
        <h3>分類參數</h3>
        <!-- 面包屑導航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首頁</el-breadcrumb-item>
            <el-breadcrumb-item>商品管理</el-breadcrumb-item>
            <el-breadcrumb-item>分類參數</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片視圖區域 -->
        <el-card>
            <!-- 警告區域 :closable="false"(是否展示“X”號) show-icon(顯示圖標) -->
            <el-alert title="注意:只允許為第三級分類設置相關參數" type="warning" :closable="false" show-icon>
            </el-alert>

            <!-- 選擇商品分類區域 -->
            <el-row class="cat_opt">
                <el-col>
                    <span>選擇商品分類:</span>
                    <!-- 選擇商品分類的級聯選擇框 -->
                </el-col>
                <el-col></el-col>
            </el-row>
        </el-card>
    </div>
</template>

  1. 完成級聯選擇框, 完成商品分類級聯選擇框
<!-- 選擇商品分類區域 -->
<el-row class="cat_opt">
    <el-col>
        <span>選擇商品分類:</span>
        <!-- 選擇商品分類的級聯選擇框 -->
        <el-cascader expandTrigger='hover' v-model="selectedCateKeys" :options="cateList" :props="cateProps" @change="handleChange" clearable></el-cascader>
    </el-col>
    <el-col></el-col>
</el-row>
......
<script>
export default {
  data() {
    return {
        //分類列表
        cateList:[],
        //用戶在級聯下拉菜單中選中的分類id
        selectedCateKeys:[],
        //配置級聯菜單中數據如何展示
        cateProps: {
            value: 'cat_id',
            label: 'cat_name',
            children: 'children'
        }
    }
  },
  created() {
      this.getCateList()
  },
  methods: {
      async getCateList(){
        //獲取所有的商品分類列表
        const { data: res } = await this.$http.get('categories')

        if (res.meta.status !== 200) {
            return this.$message.error('獲取分類數據失敗')
        }
        //將數據列表賦值給cateList
        this.cateList = res.data
        // //保存總數據條數
        // this.total = res.data.total
        //   console.log(res.data);
      },
      handleChange(){
        //當用戶在級聯菜單中選擇內容改變時觸發
        console.log(this.selectedCateKeys);
      }
  }
}
</script>

  1. 展示參數, 展示動態參數數據以及靜態屬性數據
<!-- tab頁簽區域 -->
<el-tabs v-model="activeName" @tab-click="handleTabClick">
  <!-- 添加動態參數的面板 將標簽頁改為many -->
  <el-tab-pane label="動態參數" name="many">
    <el-button size="mini" type="primary" :disabled="isButtonDisabled">添加參數</el-button>
    <!-- 動態參數表格 -->
    <el-table :data="manyTableData" border stripe>
      <!-- 展開行 -->
      <el-table-column type="expand"></el-table-column>
      <!-- 索引列 -->
      <el-table-column type="index"></el-table-column>
      <el-table-column label="參數名稱" prop="attr_name"></el-table-column>
      <el-table-column label="操作">
        <template slot-scope="scope">
          <el-button size="mini" type="primary" icon="el-icon-edit">編輯</el-button>
          <el-button size="mini" type="danger" icon="el-icon-delete">刪除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-tab-pane>
  <!-- 添加靜態屬性的面板 將標簽頁改為only -->
  <el-tab-pane label="靜態屬性" name="only">
    <el-button size="mini" type="primary" :disabled="isButtonDisabled">添加屬性</el-button>
    <!-- 靜態屬性表格 -->
    <el-table :data="onlyTableData" border stripe>
      <!-- 展開行 -->
      <el-table-column type="expand"></el-table-column>
      <!-- 索引列 -->
      <el-table-column type="index"></el-table-column>
      <el-table-column label="屬性名稱" prop="attr_name"></el-table-column>
      <el-table-column label="操作">
        <template slot-scope="scope">
          <el-button size="mini" type="primary" icon="el-icon-edit">編輯</el-button>
          <el-button size="mini" type="danger" icon="el-icon-delete">刪除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-tab-pane>

</el-tabs>


<script>
export default {
  data() {
    return {
      ......
      //tab頁簽激活顯示的頁簽項
      activeName: 'many',
      //用來保存動態參數數據
      manyTableData: [],
      //用來保存靜態屬性數據
      onlyTableData: []  
    }
  methods: {
    .......
    async handleChange() {
      //當用戶在級聯菜單中選擇內容改變時觸發
      console.log(this.selectedCateKeys)
      //發送請求,根據用戶選擇的三級分類和面板獲取參數數據
      const { data: res } = await this.$http.get(
        `categories/${this.cateId}/attributes`,
        { params: { sel: this.activeName } }
      )
      if (res.meta.status !== 200) {
        return this.$message.error('獲取參數列表數據失敗')
      }

      console.log(res.data)
      if (this.activeName === 'many') {
        //獲取的是動態參數
        this.manyTableData = res.data
      } else if (this.activeName === 'only') {
        //獲取的是靜態屬性
        this.onlyTableData = res.data
      }
    },
    handleTabClick() {
      console.log(this.activeName)
      this.handleChange()
    }
  },
  computed: {
    //添加計算屬性用來獲取按鈕禁用與否
    isButtonDisabled() {
      return this.selectedCateKeys.length !== 3
    },
    //獲取選中的三級分類id
    cateId() {
      if (this.selectedCateKeys.length === 3) {
        return this.selectedCateKeys[this.selectedCateKeys.length - 1]
      }
      return null
    }
  }

  1. 添加參數, 完成添加參數或屬性
<!-- 添加參數或屬性對話框 -->
<el-dialog :title="'添加'+titleText" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed">
  <!-- 添加表單 -->
  <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px">
    <el-form-item :label="titleText" prop="attr_name">
      <el-input v-model="addForm.attr_name"></el-input>
    </el-form-item>
  </el-form>
  <span slot="footer" class="dialog-footer">
    <el-button @click="addDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="addParams">確 定</el-button>
  </span>
</el-dialog>

<script>
export default {
  data() {
    return {
      .......
      //控制添加參數.屬性對話框的顯示或隱藏
      addDialogVisible: false,
      //添加參數的表單數據對象
      addForm: {
        attr_name: ''
      },
      //添加表單驗證規則
      addFormRules: {
        attr_name: [{ required: true, message: '請輸入名稱', trigger: 'blur' }]
      }
    }
  },methods: {
    .......
    addParams() {
      //當用戶點擊對話框中的確定時,校驗表單
      this.$refs.addFormRef.validate(async valid => {
        //校驗不通過,return
        if (!valid) return
        //校驗通過,發送請求完成添加參數或者屬性
        const { data: res } = this.$http.post(`categories/${this.cateId}/attributes`,
          { 
            attr_name: this.addForm.attr_name, 
            attr_sel: this.activeName,
            attr_vals: "a,b,c" 
          }
        )

        console.log(res)
        if (res.meta.status !== 201) {
          return this.$message.error('添加' + this.titleText + '數據失敗')
        }
        this.$message.success('添加' + this.titleText + '數據成功')
        this.addDialogVisible = false
        this.getCateList()
      })
    }
  }
</script>

  1. 編輯參數, 完成編輯參數或屬性
<!-- 修改參數或屬性對話框 -->
<el-dialog :title="'修改'+titleText" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed">
  <!-- 添加表單 -->
  <el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px">
    <el-form-item :label="titleText" prop="attr_name">
      <el-input v-model="editForm.attr_name"></el-input>
    </el-form-item>
  </el-form>
  <span slot="footer" class="dialog-footer">
    <el-button @click="editDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="editParams">確 定</el-button>
  </span>
</el-dialog>

<script>
export default {
  data() {
    return {
      .......
      //控制修改參數.屬性對話框的顯示或隱藏
      editDialogVisible:false,
      //修改參數.屬性對話框中的表單
      editForm:{
        attr_name:''
      },
      //修改表單的驗證規則
      editFormRules:{
        attr_name:[
          { required: true, message: '請輸入名稱', trigger: 'blur' }
        ]
      }
    }
  },methods: {
    .......
    async showEditDialog(attr_id){
      //發起請求獲取需要修改的那個參數數據
      const {data:res} = await this.$http.get(`categories/${this.cateId}/attributes/${attr_id}`,
      {params:{ attr_sel:this.activeName }})
      if (res.meta.status !== 200) {
        return this.$message.error('獲取參數數據失敗')
      }
      this.editForm = res.data;
      //顯示修改參數.屬性對話框
      this.editDialogVisible = true;
    },
    editDialogClosed(){
      //當關閉修改參數.屬性對話框時
      this.$refs.editFormRef.resetFields()
    },
    editParams(){
      //驗證表單
      this.$refs.editFormRef.validate(async valid => {
        if(!valid) return;

        //發送請求完成修改
        const {data:res} = await this.$http.put(`categories/${this.cateId}/attributes/${this.editForm.attr_id}`,
        {attr_name:this.editForm.attr_name,attr_sel:this.activeName})

        if (res.meta.status !== 200) {
          return this.$message.error('獲取參數數據失敗')
        }
        this.$message.success('修改' + this.titleText + '數據成功')
        this.editDialogVisible = false
        this.handleChange();
      })
    }
  }
</script>

  1. 刪除參數, 刪除參數或屬性
給兩個刪除按鈕添加事件
<el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">刪除</el-button>
<el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">刪除</el-button>

<script>
添加對應的事件處理函數
async removeParams(attr_id){
  //根據id刪除對應的參數或屬性
  //彈窗提示用戶是否要刪除
  const confirmResult = await this.$confirm(
    '請問是否要刪除該'+this.titleText,
    '刪除提示',
    {
      confirmButtonText: '確認刪除',
      cancelButtonText: '取消',
      type: 'warning'
    }
  ).catch(err => err)
  //如果用戶點擊確認,則confirmResult 為'confirm'
  //如果用戶點擊取消, 則confirmResult獲取的就是catch的錯誤消息'cancel'
  if (confirmResult != 'confirm') {
    return this.$message.info('已經取消刪除')
  }

  //沒有取消就是要刪除,發送請求完成刪除
  const {data:res} = await this.$http.delete(`categories/${this.cateId}/attributes/${attr_id}`)

  if (res.meta.status !== 200) {
    return this.$message.error('刪除參數數據失敗')
  }
  this.$message.success('刪除' + this.titleText + '數據成功')
  this.handleChange()
}
</script>

  1. 展示動態參數可選項,動態參數可選項展示及操作在獲取動態參數的方法中進行處理。
//將獲取到的數據中的attr_vals字符串轉換為數組
res.data.forEach(item => {
  item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []
  //添加一個bool值控制文本框的顯示或者隱藏
  item.inputVisible = false
  //添加一個inputValue保存文本框值
  item.inputValue = ''
})

//然后再修改展開行中的代碼,生成el-tag和文本框以及添加按鈕
<!-- 展開行 -->
<el-table-column type="expand">
  <template slot-scope="scope">
    <!-- 循環生成的el-tag -->
    <el-tag v-for="(item,i) in scope.row.attr_vals" :key="i" closable>{{item}}</el-tag>
    <!-- 輸入框 -->
    <el-input class="input-new-tag" v-if="scope.row.inputVisible" v-model="scope.row.inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm(scope.row)" @blur="handleInputConfirm(scope.row)">
    </el-input>
    <!-- 添加按鈕 -->
    <el-button v-else class="button-new-tag" size="small" @click="showInput(scope.row)">+ New Tag</el-button>
  </template>
</el-table-column>


//最后對應文本框的事件和按鈕的事件添加處理函數
handleInputConfirm(row){
  //當用戶在文本框中按下enter鍵或者焦點離開時都會觸發執行
  //判斷用戶在文本框中輸入的內容是否合法
  if(row.inputValue.trim().length===0){
    row.inputValue = ''
    row.inputVisible = false
    return
  }

  // row.inputVisible = false
  //如果用戶輸入了真實合法的數據,需要保存起來
},
showInput(row){
  //用戶點擊添加按鈕時觸發
  row.inputVisible = true
  //$nextTick:在頁面上元素被重新渲染之后,調用回調函數的代碼
  this.$nextTick(_=>{
    //讓文本框自動獲得焦點
    this.$refs.saveTagInput.$refs.input.focus()
  })
}

9.添加/刪除可選項,添加/刪除動態參數可選項

給el-tag添加刪除事件
<el-tag v-for="(item,i) in scope.row.attr_vals" :key="i" closable @close="handleClose(i,scope.row)">{{item}}</el-tag>

<script>
//在methods中添加新增,刪除事件處理函數
handleInputConfirm(row){
//當用戶在文本框中按下enter鍵或者焦點離開時都會觸發執行
  //判斷用戶在文本框中輸入的內容是否合法
  if(row.inputValue.trim().length===0){
    row.inputValue = ''
    row.inputVisible = false
    return
  }

  // row.inputVisible = false
  //如果用戶輸入了真實合法的數據,需要保存起來
  row.attr_vals.push(row.inputValue.trim())
  row.inputValue = ''
  row.inputVisible = false

  this.saveAttrVals(row)
},
handleClose(index,row){
  //刪除對應索引的參數可選項
  row.attr_vals.splice(index,1)
  //調用函數,完成保存可選項的操作
  this.saveAttrVals(row)
},
async saveAttrVals(row){
  //封裝函數,完成保存可選項的操作
  //發起請求,保存參數細項
  const {data:res} = await this.$http.put(`categories/${this.cateId}/attributes/${row.attr_id}`,
  {attr_name:row.attr_name,attr_sel:row.attr_sel,attr_vals:row.attr_vals.join(' ')})

  if (res.meta.status !== 200) {
    return this.$message.error('修改參數項失敗')
  }

  this.$message.success('修改參數項成功')
}

補充:

當完成了動態參數可選項的功能之后,我們也需要一樣的方式完成靜態屬性可選項的功能。

此時我們只需要將動態參數可選項中的展開行復制到靜態屬性的表格中即可

商品列表

  1. 制作商品列表基本結構,添加子級路由組件以及對應的規則,並設置組件的基本機構打開router.js,添加下面的代碼
import GoodList from './components/goods/List.vue'

path: '/home', component: Home, redirect: '/welcome', children: [
  { path: "/welcome", component: Welcome },
  { path: "/users", component: Users },
  { path: "/rights", component: Rights },
  { path: "/roles", component: Roles  },
  { path: "/categories", component: Cate  },
  { path: "/params", component: Params  },
  { path: "/goods", component: GoodList  }
]j's

打開List.vue組件,添加下列代碼

<template>
    <div>
        <h3>商品列表</h3>
        <!-- 面包屑導航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首頁</el-breadcrumb-item>
            <el-breadcrumb-item>商品管理</el-breadcrumb-item>
            <el-breadcrumb-item>商品列表</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片視圖區域 -->
        <el-card>
            <el-row :gutter="20">
                <el-col :span="8">
                    <el-input placeholder="請輸入內容">
                        <el-button slot="append" icon="el-icon-search"></el-button>
                    </el-input>
                </el-col>
                <el-col :span="4">
                    <el-button type="primary">添加商品</el-button>
                </el-col>
            </el-row>
        </el-card>
    </div>
</template>

<script>
export default {
  data() {
    return {}
  },
  created() {},
  methods: {}
}
</script>

<style lang="less" scoped>
</style>

  1. 數據展示,添加數據表格展示數據以及分頁功能的實現,搜索功能的實現在main.js中添加過濾器:
//創建過濾器將秒數過濾為年月日,時分秒
Vue.filter('dateFormat',function(originVal){
  const dt = new Date(originVal)
  const y = dt.getFullYear()
  const m = (dt.getMonth()+1+'').padStart(2,'0')
  const d = (dt.getDate()+'').padStart(2,'0')

  const hh = (dt.getHours()+'').padStart(2,'0')
  const mm = (dt.getMinutes()+'').padStart(2,'0')
  const ss = (dt.getSeconds()+'').padStart(2,'0')

  return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})

<!-- 卡片視圖區域 -->
<el-card>
    <!-- 搜索欄 -->
    <el-row :gutter="20">
        <el-col :span="8">
            <el-input placeholder="請輸入內容" v-model="queryInfo.query" clearable @clear="getGoodsList">
                <el-button slot="append" icon="el-icon-search" @click="getGoodsList"></el-button>
            </el-input>
        </el-col>
        <el-col :span="4">
            <el-button type="primary">添加商品</el-button>
        </el-col>
    </el-row>

    <!-- 表格區域 -->
    <el-table :data="goodsList" border stripe>
        <el-table-column type="index"></el-table-column>
        <el-table-column label="商品名稱" prop="goods_name"></el-table-column>
        <el-table-column label="商品價格(元)" prop="goods_price" width="95px"></el-table-column>
        <el-table-column label="商品重量" prop="goods_weight" width="95px"></el-table-column>
        <el-table-column label="創建時間" prop="add_time" width="140px">
            <template slot-scope="scope">
                {{scope.row.add_time | dateFormat}}
            </template>
        </el-table-column>
        <el-table-column label="操作" width="125px">
            <template slot-scope="scope">
                <el-button size="mini" type="primary" icon="el-icon-edit"></el-button>
                <el-button size="mini" type="danger" icon="el-icon-delete"></el-button>
            </template>
        </el-table-column>
    </el-table>

    <!-- 分頁 -->
    <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
    </el-pagination>
</el-card>

//綁定數據以及添加方法
<script>
export default {
  data() {
    return {
      //查詢參數
      queryInfo: {
        query: '',
        pagenum: 1,
        pagesize: 10
      },
      //保存商品列表信息
      goodsList: [],
      //總數據條數
      total: 0
    }
  },
  created() {
    this.getGoodsList()
  },
  methods: {
    async getGoodsList() {
      //   根據分頁獲取對應的商品列表
      const { data: res } = await this.$http.get('goods', {
        params: this.queryInfo
      })

      if (res.meta.status !== 200) {
        return this.$message.error('獲取商品列表失敗')
      }
      console.log(res.data)
      this.$message.success('獲取商品列表成功')
      this.goodsList = res.data.goods
      this.total = res.data.total
    },
    handleSizeChange(newSize){
        //當頁號發生改變時,更改pagesize,重新請求
        this.queryInfo.pagesize = newSize
        this.getGoodsList();
    },
    handleCurrentChange(newPage){
        //當頁碼發生改變時,更改pagesize,重新請求
        this.queryInfo.pagenum = newPage
        this.getGoodsList();
    }
  }
}
</script>

  1. 實現刪除商品
//綁定按鈕點擊事件
<el-button size="mini" type="danger" icon="el-icon-delete" @click="removeGoods(scope.row.goods_id)"></el-button>

<script>
//事件函數代碼編寫
async removeGoods(goods_id) {
  //根據id刪除對應的參數或屬性
  //彈窗提示用戶是否要刪除
  const confirmResult = await this.$confirm(
    '請問是否要刪除該商品',
    '刪除提示',
    {
      confirmButtonText: '確認刪除',
      cancelButtonText: '取消',
      type: 'warning'
    }
  ).catch(err => err)
  //如果用戶點擊確認,則confirmResult 為'confirm'
  //如果用戶點擊取消, 則confirmResult獲取的就是catch的錯誤消息'cancel'
  if (confirmResult != 'confirm') {
    return this.$message.info('已經取消刪除')
  }

  //沒有取消就是要刪除,發送請求完成刪除
  const {data:res} = await this.$http.delete(`goods/${goods_id}`)

  if (res.meta.status !== 200) {
    return this.$message.error('刪除商品失敗')
  }

  this.$message.success('刪除商品成功')
  this.getGoodsList()
}

  1. 添加商品

    • 添加編程式導航在List.vue中添加編程式導航,並創建添加商品路由組件及規則
    //在List.vue中添加編程式導航
    <el-col :span="4">
        <el-button type="primary" @click="goAddPage">添加商品</el-button>
    </el-col>
    
    <script>
    goAddPage(){
        this.$router.push('/goods/add')
    }
    </script>
    
    

    在router.js中引入goods/Add.vue,並添加路由規則

    import GoodAdd from './components/goods/Add.vue'
    path: '/home', component: Home, redirect: '/welcome', children: [
      { path: "/welcome", component: Welcome },
      { path: "/users", component: Users },
      { path: "/rights", component: Rights },
      { path: "/roles", component: Roles  },
      { path: "/categories", component: Cate  },
      { path: "/params", component: Params  },
      { path: "/goods", component: GoodList  },
      { path: "/goods/add", component: GoodAdd  }
    ]
    
    
    • 布局Add.vue組件, 布局過程中需要使用Steps組件,在element.js中引入並注冊該組件,並在global.css中給組件設置全局樣式

      import {Steps,Step} from 'element-ui'
      Vue.use(Step)
      Vue.use(Steps)
      
      //global.css
      .el-steps{
          margin:15px 0;
      }
      .el-step__title{
          font-size: 13px;
      }
      
      

      然后再在Add.vue中進行頁面布局

      <template>
          <div>
              <h3>添加商品</h3>
              <!-- 面包屑導航 -->
              <el-breadcrumb separator="/">
                  <el-breadcrumb-item :to="{ path: '/home' }">首頁</el-breadcrumb-item>
                  <el-breadcrumb-item>商品管理</el-breadcrumb-item>
                  <el-breadcrumb-item>添加商品</el-breadcrumb-item>
              </el-breadcrumb>
              <!-- 卡片視圖區域 -->
              <el-card>
                  <!-- 消息提示 -->
                  <el-alert title="添加商品信息" type="info" center show-icon :closable="false">
                  </el-alert>
      
                  <!-- 步驟條組件 -->
                  <!-- align-center(居中效果) -->
                  <el-steps :space="200" :active="activeIndex - 0" finish-status="success" align-center>
                      <el-step title="基本信息"></el-step>
                      <el-step title="商品參數"></el-step>
                      <el-step title="商品屬性"></el-step>
                      <el-step title="商品圖片"></el-step>
                      <el-step title="商品內容"></el-step>
                      <el-step title="完成"></el-step>
                  </el-steps>
      
                  <!-- tab欄區域:el-tab-pane必須是el-tabs的子節點
                  :tab-position="'left'"(設置tab欄為左右結構tab欄) -->
                  <!-- 表單:label-position="top"(設置label在文本框上方) -->
                  <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top">
                      <el-tabs v-model="activeIndex" :tab-position="'left'">
                          <el-tab-pane label="基本信息" name="0">
                              <el-form-item label="商品名稱" prop="goods_name">
                                  <el-input v-model="addForm.goods_name"></el-input>
                              </el-form-item>
                              <el-form-item label="商品價格" prop="goods_price">
                                  <el-input v-model="addForm.goods_price" type="number"></el-input>
                              </el-form-item>
                              <el-form-item label="商品重量" prop="goods_weight">
                                  <el-input v-model="addForm.goods_weight" type="number"></el-input>
                              </el-form-item>
                              <el-form-item label="商品數量" prop="goods_number">
                                  <el-input v-model="addForm.goods_number" type="number"></el-input>
                              </el-form-item>
                              <el-form-item label="商品分類" prop="goods_cat">
                                  <!-- 選擇商品分類的級聯選擇框 -->
                                  <el-cascader expandTrigger='hover' v-model="addForm.goods_cat" :options="cateList" :props="cateProps" @change="handleChange" clearable></el-cascader>
                              </el-form-item>
                          </el-tab-pane>
                          <el-tab-pane label="商品參數" name="1">
      
                          </el-tab-pane>
                          <el-tab-pane label="商品屬性" name="2">
      
                          </el-tab-pane>
                          <el-tab-pane label="商品圖片" name="3">
      
                          </el-tab-pane>
                          <el-tab-pane label="商品內容" name="4">
      
                          </el-tab-pane>
                      </el-tabs>
                  </el-form>
              </el-card>
          </div>
      </template>
      
      <script>
      export default {
        data() {
          return {
            //保存步驟條激活項索引
            activeIndex: '0',
            //添加商品的表單數據對象
            addForm: {
              goods_name: '',
              goods_price: 0,
              goods_weight: 0,
              goods_number: 0,
              goods_cat:[]
            },
            //驗證規則
            addFormRules: {
              goods_name: [
                { required: true, message: '請輸入商品名稱', trigger: 'blur' }
              ],
              goods_price: [
                { required: true, message: '請輸入商品價格', trigger: 'blur' }
              ],
              goods_weight: [
                { required: true, message: '請輸入商品重量', trigger: 'blur' }
              ],
              goods_number: [
                { required: true, message: '請輸入商品數量', trigger: 'blur' }
              ],
              goods_cat: [
                { required: true, message: '請選擇商品分類', trigger: 'blur' }
              ]
            },
            //用來保存分類數據
            cateList: [],
            //配置級聯菜單中數據如何展示
            cateProps: {
              value: 'cat_id',
              label: 'cat_name',
              children: 'children'
            }
          }
        },
        created() {
          this.getCateList()
        },
        methods: {
          async getCateList() {
            const { data: res } = await this.$http.get('categories')
      
            if (res.meta.status !== 200) {
              return this.$message.error('獲取商品分類數據失敗')
            }
            this.cateList = res.data
          },
          handleChange(){
            //如果用戶選擇的不是三級分類,該次選擇無效,因為必須選擇三級分類
            if(this.addForm.goods_cat.length !== 3){
              this.addForm.goods_cat = []
              return
            }
          }
        }
      }
      </script>
      
      <style lang="less" scoped>
      </style>
      
      
    • 添加tab欄切換驗證, 也就是說不輸入某些內容,無法切換到別的tab欄

      //首先給tabs添加tab切換前事件
      <el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave">
      ......
      </el-tabs>
      
      <script>
      //再到methods編寫事件函數beforeTabLeave
      beforeTabLeave(activeName,oldActiveName){
        //在tab欄切換之前觸發,兩個形參為切換前,后的tab欄name
        if(oldActiveName === '0'){
            //在第一個標簽頁的時候
            if(this.addForm.goods_cat.length !== 3){
                this.$message.error('請選擇商品的分類')
                return false
            }else if(this.addForm.goods_name.trim() === ''){
                this.$message.error('請輸入商品名稱')
                return false
            }else if(this.addForm.goods_price.trim() === '0'){
                this.$message.error('請輸入商品價格')
                return false
            }else if(this.addForm.goods_weight.trim() === '0'){
                this.$message.error('請輸入商品重量')
                return false
            }else if(this.addForm.goods_number.trim() === '0'){
                this.$message.error('請輸入商品數量')
                return false
            }
        }
      }
      </script>
      
      
    • .展示信息, 展示商品參數信息,商品屬性信息
      在商品參數信息展示中使用的el-checkbox,el-checkbox-group組件,打開element.js引入組件並注冊組件

      //在用戶點擊tab欄時觸發事件
      <el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave" @tab-click="tabClicked">
      ........
      
      //在參數信息,商品屬性面板中添加循環生成結構的代碼
      <el-tab-pane label="商品參數" name="1">
        <!-- 渲染表單item項 -->
        <el-form-item :label="item.attr_name" :key="item.attr_id" v-for="item in manyTableData">
            <!-- 使用數組渲染復選框組 -->
            <el-checkbox-group v-model="item.attr_vals">
                <el-checkbox border :label="val" v-for="(val,i) in item.attr_vals" :key="i"></el-checkbox>
            </el-checkbox-group>
        </el-form-item>
      </el-tab-pane>
      <el-tab-pane label="商品屬性" name="2">
        <!-- 循環生成靜態屬性 -->
        <el-form-item :label="item.attr_name" v-for="item in onlyTableData" :key="item.attr_id">
            <el-input v-model="item.attr_vals"></el-input>
        </el-form-item>
      </el-tab-pane>
      
      <script>
      //在data數據中添加保存動態參數和靜態屬性的數組
      export default {
        data() {
          return {
            ......
            //動態參數列表
            manyTableData: [],
            //靜態屬性列表
            onlyTableData:[]
            }
        },methods: {
          .......
          async tabClicked() {
            //當用戶點擊切換tab欄時觸發
            if (this.activeIndex === '1') {
              //發送請求獲取動態參數
              const { data: res } = await this.$http.get(
                `categories/${this.cateId}/attributes`,
                { params: { sel: 'many' } }
              )
      
              if (res.meta.status !== 200) {
                return this.$message.error('獲取動態參數列表失敗')
              }
              //將attr_vals字符串轉換為數組
              res.data.forEach(item => {
                item.attr_vals =
                  item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ')
              })
              this.manyTableData = res.data
            } else if (this.activeIndex === '2') {
              //發送請求獲取靜態屬性
              const { data: res } = await this.$http.get(
                `categories/${this.cateId}/attributes`,
                { params: { sel: 'only' } }
              )
      
              if (res.meta.status !== 200) {
                return this.$message.error('獲取靜態屬性列表失敗')
              }
      
              this.onlyTableData = res.data
            }
          }
        },
        //添加 計算屬性獲取三級分類
        computed: {
          cateId() {
            if (this.addForm.goods_cat.length === 3) {
              return this.addForm.goods_cat[2]
            }
            return null
          }
        }
      }
      </script>
      
      

day05

添加商品

  1. 完成圖片上傳,使用upload組件完成圖片上傳在element.js中引入upload組件,並注冊因為upload組件進行圖片上傳的時候並不是使用axios發送請求所以,我們需要手動為上傳圖片的請求添加token,即為upload組件添加headers屬性
//在頁面中添加upload組件,並設置對應的事件和屬性
<el-tab-pane label="商品圖片" name="3">
  <!-- 商品圖片上傳
  action:指定圖片上傳api接口
  :on-preview : 當點擊圖片時會觸發該事件進行預覽操作,處理圖片預覽
  :on-remove : 當用戶點擊圖片右上角的X號時觸發執行
  :on-success:當用戶點擊上傳圖片並成功上傳時觸發
  list-type :設置預覽圖片的方式
  :headers :設置上傳圖片的請求頭 -->
  <el-upload :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove" :on-success="handleSuccess" list-type="picture" :headers="headerObj">
    <el-button size="small" type="primary">點擊上傳</el-button>
  </el-upload>
</el-tab-pane>
//在el-card卡片視圖下面添加對話框用來預覽圖片
<!-- 預覽圖片對話框 -->
<el-dialog title="圖片預覽" :visible.sync="previewVisible" width="50%">
  <img :src="previewPath" class="previewImg" />
</el-dialog>

<script>
//在data中添加數據
data(){
  return {
    ......
    //添加商品的表單數據對象
    addForm: {
      goods_name: '',
      goods_price: 0,
      goods_weight: 0,
      goods_number: 0,
      goods_cat: [],
      //上傳圖片數組
      pics: []
    },
    //上傳圖片的url地址
    uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',
    //圖片上傳組件的headers請求頭對象
    headerObj: { Authorization: window.sessionStorage.getItem('token') },
    //保存預覽圖片的url地址
    previewPath: '',
    //控制預覽圖片對話框的顯示和隱藏
    previewVisible:false
  }
},
//在methods中添加事件處理函數
methods:{
  .......
  handlePreview(file) {
    //當用戶點擊圖片進行預覽時執行,處理圖片預覽
    //形參file就是用戶預覽的那個文件
    this.previewPath = file.response.data.url
    //顯示預覽圖片對話框
    this.previewVisible = true
  },
  handleRemove(file) {
    //當用戶點擊X號刪除時執行
    //形參file就是用戶點擊刪除的文件
    //獲取用戶點擊刪除的那個圖片的臨時路徑
    const filePath = file.response.data.tmp_path
    //使用findIndex來查找符合條件的索引
    const index = this.addForm.pics.findIndex(item => item.pic === filePath)
    //移除索引對應的圖片
    this.addForm.pics.splice(index, 1)
  },
  handleSuccess(response) {
    //當上傳成功時觸發執行
    //形參response就是上傳成功之后服務器返回的結果
    //將服務器返回的臨時路徑保存到addForm表單的pics數組中
    this.addForm.pics.push({ pic: response.data.tmp_path })
  }
}
</script>

  1. 使用富文本插件,想要使用富文本插件vue-quill-editor,就必須先從依賴安裝該插件引入並注冊vue-quill-editor,打開main.js,編寫如下代碼
//導入vue-quill-editor(富文本編輯器)
import VueQuillEditor from 'vue-quill-editor'
//導入vue-quill-editor的樣式
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
......
//全局注冊組件
Vue.component('tree-table', TreeTable)
//全局注冊富文本組件
Vue.use(VueQuillEditor)

使用富文本插件vue-quill-editor

<!-- 富文本編輯器組件 -->
<el-tab-pane label="商品內容" name="4">
  <!-- 富文本編輯器組件 -->
  <quill-editor v-model="addForm.goods_introduce"></quill-editor>
  <!-- 添加商品按鈕 -->
  <el-button type="primary" class="btnAdd">添加商品</el-button>
</el-tab-pane>

<script>
//在數據中添加goods_introduce
//添加商品的表單數據對象
addForm: {
  goods_name: '',
  goods_price: 0,
  goods_weight: 0,
  goods_number: 0,
  goods_cat: [],
  //上傳圖片數組
  pics: [],
  //商品的詳情介紹
  goods_introduce:''
}
//在global.css樣式中添加富文本編輯器的最小高度
.ql-editor{
    min-height: 300px;
}
//給添加商品按鈕添加間距
.btnAdd{
  margin-top:15px;
}
</script>

  1. 添加商品,完成添加商品的操作在添加商品之前,為了避免goods_cat數組轉換字符串之后導致級聯選擇器報錯我們需要打開vue控制條,點擊依賴,安裝lodash,把addForm進行深拷貝
//打開Add.vue,導入lodash
<script>
//官方推薦將lodash導入為_
import _ from 'lodash'

//給添加商品按鈕綁定點擊事件
<!-- 添加商品按鈕 -->
<el-button type="primary" class="btnAdd" @click="add">添加商品</el-button>
//編寫點擊事件完成商品添加
add(){
  this.$refs.addFormRef.validate(async valid=>{
    if(!valid) return this.$message.error("請填寫必要的表單項!")

    //將addForm進行深拷貝,避免goods_cat數組轉換字符串之后導致級聯選擇器報錯
    const form = _.cloneDeep(this.addForm)
    //將goods_cat從數組轉換為"1,2,3"字符串形式
    form.goods_cat = form.goods_cat.join(",")
    //處理attrs數組,數組中需要包含商品的動態參數和靜態屬性
    //將manyTableData(動態參數)處理添加到attrs
    this.manyTableData.forEach(item=>{
      form.attrs.push({ attr_id:item.attr_id, attr_value:item.attr_vals.join(" ") }) 
    })
    //將onlyTableData(靜態屬性)處理添加到attrs
    this.onlyTableData.forEach(item=>{
      form.attrs.push({ attr_id:item.attr_id, attr_value:item.attr_vals }) 
    })

    //發送請求完成商品的添加,商品名稱必須是唯一的
    const {data:res} = await this.$http.post('goods',form)
    if(res.meta.status !== 201){
      return this.$message.error('添加商品失敗')
    }
    this.$message.success('添加商品成功')
    //編程式導航跳轉到商品列表
    this.$router.push('/goods')
  })
}
</script>

訂單列表

  1. 創建路由,創建訂單列表路由組件並添加路由規則
//在components中新建order文件夾,新建Order.vue組件,組件中添加代碼如下
<template>
    <div>
        <h3>訂單列表</h3>
        <!-- 面包屑導航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首頁</el-breadcrumb-item>
            <el-breadcrumb-item>訂單管理</el-breadcrumb-item>
            <el-breadcrumb-item>訂單列表</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片視圖區域 -->
        <el-card>
            <!-- 搜索欄 -->
            <el-row :gutter="20">
                <el-col :span="8">
                    <el-input placeholder="請輸入內容" v-model="queryInfo.query" clearable>
                        <el-button slot="append" icon="el-icon-search" ></el-button>
                    </el-input>
                </el-col>
            </el-row>
        </el-card>
    </div>
</template>

<script>
export default {
  data() {
    return {
        //查詢條件
        queryInfo:{
            query:'',
            pagenum:1,
            pagesize:10
        }
    }
  },
  created() {
      
  },
  methods: {
      
  }
}
</script>

<style lang="less" scoped>

</style>


//打開router.js導入Order.vue並添加規則
import Order from './components/order/Order.vue'

path: '/home', component: Home, redirect: '/welcome', children: [
  { path: "/welcome", component: Welcome },
  { path: "/users", component: Users },
  { path: "/rights", component: Rights },
  { path: "/roles", component: Roles  },
  { path: "/categories", component: Cate  },
  { path: "/params", component: Params  },
  { path: "/goods", component: GoodList  },
  { path: "/goods/add", component: GoodAdd  },
  { path: "/orders", component: Order  }
]

  1. 實現數據展示及分頁
<!-- 卡片視圖區域 -->
<el-card>
    <!-- 搜索欄 -->
    <el-row :gutter="20">
        <el-col :span="8">
            <el-input placeholder="請輸入內容" v-model="queryInfo.query" clearable>
                <el-button slot="append" icon="el-icon-search"></el-button>
            </el-input>
        </el-col>
    </el-row>

    <!-- 訂單表格 -->
    <el-table :data="orderList" border stripe>
        <el-table-column type="index"></el-table-column>
        <el-table-column label="訂單編號" prop="order_number"></el-table-column>
        <el-table-column label="訂單價格" prop="order_price"></el-table-column>
        <el-table-column label="是否付款" prop="pay_status">
            <template slot-scope="scope">
                <el-tag type="success" v-if="scope.row.pay_status === '1'">已付款</el-tag>
                <el-tag type="danger" v-else>未付款</el-tag>
            </template>
        </el-table-column>
        <el-table-column label="是否發貨" prop="is_send"></el-table-column>
        <el-table-column label="下單時間" prop="create_time">
            <template slot-scope="scope">
                {{scope.row.create_time | dateFormat}}
            </template>
        </el-table-column>
        <el-table-column label="操作" width="125px">
            <template slot-scope="scope">
                <el-button size="mini" type="primary" icon="el-icon-edit"></el-button>
                <el-button size="mini" type="success" icon="el-icon-location"></el-button>
            </template>
        </el-table-column>
    </el-table>

    <!-- 分頁 -->
    <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
    </el-pagination>
</el-card>

<script>
export default {
  data() {
    return {
      //查詢條件
      queryInfo: {
        query: '',
        pagenum: 1,
        pagesize: 10
      },
      //訂單列表數據
      orderList: [],
      //數據總條數
      total: 0
    }
  },
  created() {
    this.getOrderList()
  },
  methods: {
    async getOrderList() {
      const { data: res } = await this.$http.get('orders', {
        params: this.queryInfo
      })

      if (res.meta.status !== 200) {
        return this.$message.error('獲取訂單列表數據失敗!')
      }

      this.total = res.data.total
      this.orderList = res.data.goods
    },
    handleSizeChange(newSize){
        this.queryInfo.pagesize = newSize
        this.getOrderList()
    },
    handleCurrentChange(newPage){
        this.queryInfo.pagenum = newPage
        this.getOrderList()
    }
  }
}
</script>

  1. 制作省市區縣聯動,打開今天的資料,找到素材文件夾,復制citydata.js文件到components/order文件夾中然后導入citydata.js文件
//給修改地址按鈕添加點擊事件
<el-button size="mini" type="primary" icon="el-icon-edit" @click="showEditAddress"></el-button>
//添加修改地址對話框,在卡片視圖下方添加
<!-- 修改地址對話框 -->
<el-dialog title="修改收貨地址" :visible.sync="addressVisible" width="50%" @close="addressDialogClosed">
    <!-- 添加表單 -->
    <el-form :model="addressForm" :rules="addressFormRules" ref="addressFormRef" label-width="100px">
        <el-form-item label="省市區縣" prop="address1">
            <el-cascader :options="cityData" v-model="addressForm.address1"></el-cascader>
        </el-form-item>
        <el-form-item label="詳細地址" prop="address2">
            <el-input v-model="addressForm.address2"></el-input>
        </el-form-item>
    </el-form>
    <span slot="footer" class="dialog-footer">
        <el-button @click="addressVisible = false">取 消</el-button>
        <el-button type="primary" @click="addressVisible = false">確 定</el-button>
    </span>
</el-dialog>

//js部分的代碼
<script>
import cityData from "./citydata.js"
export default {
  data() {
    return {
      ......
      //控制修改地址對話框的顯示和隱藏
      addressVisible:false,
      //修改收貨地址的表單
      addressForm:{
          address1:[],
          address2:''
      },
      addressFormRules:{
          address1:[{ required: true, message: '請選擇省市區縣', trigger: 'blur' }],
          address2:[{ required: true, message: '請輸入詳細地址', trigger: 'blur' }],
      },
      //將導入的cityData數據保存起來
      cityData:cityData
      }
  },methods: {
    ......
    showEditAddress() {
      //當用戶點擊修改收貨地址按鈕時觸發
      this.addressVisible = true;
    },
    addressDialogClosed(){
        this.$refs.addressFormRef.resetFields()
    }
  }
}
</script>
<style lang="less" scoped>
.el-cascader{
    width: 100%;
}
</style>

  1. 制作物流進度對話框,因為我們使用的是element-ui中提供的Timeline組件,所以需要導入並注冊組件打開element.js,編寫代碼會進行導入和注冊
import {
    Timeline,TimelineItem
} from 'element-ui'

Vue.use(Timeline)
Vue.use(TimelineItem)

打開Order.vue文件,添加代碼實現物流進度對話框

<!-- 物流信息進度對話框 -->
<el-dialog title="物流進度" :visible.sync="progressVisible" width="50%">
    <!-- 時間線組件  -->
    <el-timeline>
        <el-timeline-item v-for="(activity, index) in progressInfo" 
        :key="index" :timestamp="activity.time">
            {{activity.context}}
        </el-timeline-item>
    </el-timeline>
</el-dialog>

<script>
import cityData from './citydata.js'
export default {
  data() {
    return {
      ......
      //控制物流進度對話框的顯示和隱藏
      progressVisible: false,
      //保存物流信息
      progressInfo: []
      }
  },methods: {
    ......
    async showProgress() {
      //發送請求獲取物流數據
      const { data: res } = await this.$http.get('/kuaidi/804909574412544580')

      if (res.meta.status !== 200) {
        return this.$message.error('獲取物流進度失敗!')
      }
      this.progressInfo = res.data
      //顯示對話框
      this.progressVisible = true
    }
  }
}
</script>

數據統計

  1. 創建路由 創建數據統計路由組件並添加路由規則
//在components中新建report文件夾,新建Report.vue組件,組件中添加代碼如下
<template>
    <div>
        <h3>數據報表</h3>
        <!-- 面包屑導航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首頁</el-breadcrumb-item>
            <el-breadcrumb-item>數據統計</el-breadcrumb-item>
            <el-breadcrumb-item>數據報表</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片視圖區域 -->
        <el-card></el-card>
    </div>
</template>
    
<script>
export default {
  data() {
    return {

    }
  },created(){

  },methods:{

  }
}
</script>

<style lang="less" scoped>

</style>

打開router.js,導入Report.vue並設置路由規則

import Report from './components/report/Report.vue'
path: '/home', component: Home, redirect: '/welcome', children: [
  { path: "/welcome", component: Welcome },
  { path: "/users", component: Users },
  { path: "/rights", component: Rights },
  { path: "/roles", component: Roles  },
  { path: "/categories", component: Cate  },
  { path: "/params", component: Params  },
  { path: "/goods", component: GoodList  },
  { path: "/goods/add", component: GoodAdd  },
  { path: "/orders", component: Order  },
  { path: "/reports", component: Report  }
]

  1. 導入ECharts並使用
<template>
    <div>
        <h3>數據報表</h3>
        <!-- 面包屑導航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首頁</el-breadcrumb-item>
            <el-breadcrumb-item>數據統計</el-breadcrumb-item>
            <el-breadcrumb-item>數據報表</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片視圖區域 -->
        <el-card>
            <div id="main" style="width:750px;height:400px;"></div>
        </el-card>
    </div>
</template>
    
<script>
//導入echarts
import echarts from 'echarts'
//導入lodash
import _ from 'lodash'
export default {
  data() {
    return {
      //需要跟請求的折線圖數據合並的options
      options: {
        title: {
          text: '用戶來源'
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'cross',
            label: {
              backgroundColor: '#E9EEF3'
            }
          }
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: [
          {
            boundaryGap: false
          }
        ],
        yAxis: [
          {
            type: 'value'
          }
        ]
      }
    }
  },
  created() {},
  async mounted() {
    //在頁面dom元素加載完畢之后執行的鈎子函數mounted
    // 基於准備好的dom,初始化echarts實例
    var myChart = echarts.init(document.getElementById('main'))
    //准備數據和配置項
    //發送請求獲取折線圖數據
    const { data: res } = await this.$http.get('reports/type/1')

    if (res.meta.status !== 200) {
      return this.$message.error('獲取折線圖數據失敗')
    }

    //合並res.data和this.options
    const result = _.merge(res.data,this.options)

    // 使用獲取的數據展示圖表
    myChart.setOption(result)
  },
  methods: {}
}
</script>

<style lang="less" scoped>
</style>

day06

項目優化

A.生成打包報告,根據報告優化項目
B.第三方庫啟用CDN
C.Element-UI組件按需加載
D.路由懶加載
E.首頁內容定制

  1. 添加進度條,給項目添加進度條效果,先打開項目控制台,打開依賴,安裝nprogress打開main.js,編寫如下代碼
//導入進度條插件
import NProgress from 'nprogress'
//導入進度條樣式
import 'nprogress/nprogress.css'
.....
//請求在到達服務器之前,先會調用use中的這個回調函數來添加請求頭信息
axios.interceptors.request.use(config => {
  //當進入request攔截器,表示發送了請求,我們就開啟進度條
  NProgress.start()
  //為請求頭對象,添加token驗證的Authorization字段
  config.headers.Authorization = window.sessionStorage.getItem("token")
  //必須返回config
  return config
})
//在response攔截器中,隱藏進度條
axios.interceptors.response.use(config =>{
  //當進入response攔截器,表示請求已經結束,我們就結束進度條
  NProgress.done()
  return config
})

  1. 據報錯修改代碼,根據ESLint的警告提示更改對應的代碼在.prettierrc文件中更改設置"printWidth":200, 將每行代碼的文字數量更改為200
{
    "semi":false,
    "singleQuote":true,
    "printWidth":200
}

  1. 執行build,安裝一個插件(babel-plugin-transform-remove-console)在項目build階段移除所有的console信息打開項目控制台,點擊依賴->開發依賴,輸入babel-plugin-transform-remove-console,安裝打開babel.config.js,編輯代碼如下:
//項目發布階段需要用到的babel插件
const productPlugins = []

//判斷是開發還是發布階段
if(process.env.NODE_ENV === 'production'){
  //發布階段
  productPlugins.push("transform-remove-console")
}

module.exports = {
  "presets": [
    "@vue/app"
  ],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ],
    ...productPlugins
  ]
}

  1. 生成打包報告
    A.命令行形式生成打包報告
    vue-cli-service build --report
    B.在vue控制台生成打包報告
    點擊“任務”=>“build”=>“運行”
    運行完畢之后點擊右側“分析”,“控制台”面板查看報告

  2. 修改webpack的默認配置,默認情況下,vue-cli 3.0生成的項目,隱藏了webpack配置項,如果我們需要配置webpack需要通過vue.config.js來配置。在項目根目錄中創建vue.config.js文件,

module.exports = {
    chainWebpack:config=>{
        //發布模式
        config.when(process.env.NODE_ENV === 'production',config=>{
            //entry找到默認的打包入口,調用clear則是刪除默認的打包入口
            //add添加新的打包入口
            config.entry('app').clear().add('./src/main-prod.js')
        })
        //開發模式
        config.when(process.env.NODE_ENV === 'development',config=>{
            config.entry('app').clear().add('./src/main-dev.js')
        })
    }
}

補充:
chainWebpack可以通過鏈式編程的形式,修改webpack配置
configureWebpack可以通過操作對象的形式,修改webpack配置

  1. 加載外部CDN,默認情況下,依賴項的所有第三方包都會被打包到js/chunk-vendors..js文件中,導致該js文件過大那么我們可以通過externals排除這些包,使它們不被打包到js/chunk-vendors..js文件中
module.exports = {
    chainWebpack:config=>{
        //發布模式
        config.when(process.env.NODE_ENV === 'production',config=>{
            //entry找到默認的打包入口,調用clear則是刪除默認的打包入口
            //add添加新的打包入口
            config.entry('app').clear().add('./src/main-prod.js')

            //使用externals設置排除項
            config.set('externals',{
                vue:'Vue',
                'vue-router':'VueRouter',
                axios:'axios',
                lodash:'_',
                echarts:'echarts',
                nprogress:'NProgress',
                'vue-quill-editor':'VueQuillEditor'
            })
        })
        //開發模式
        config.when(process.env.NODE_ENV === 'development',config=>{
            config.entry('app').clear().add('./src/main-dev.js')
        })
    }
}

設置好排除之后,為了使我們可以使用vue,axios等內容,我們需要加載外部CDN的形式解決引入依賴項。
打開開發入口文件main-prod.js,刪除掉默認的引入代碼

import Vue from 'vue'
import App from './App.vue'
import router from './router'
// import './plugins/element.js'
//導入字體圖標
import './assets/fonts/iconfont.css'
//導入全局樣式
import './assets/css/global.css'
//導入第三方組件vue-table-with-tree-grid
import TreeTable from 'vue-table-with-tree-grid'
//導入進度條插件
import NProgress from 'nprogress'
//導入進度條樣式
// import 'nprogress/nprogress.css'
// //導入axios
import axios from 'axios'
// //導入vue-quill-editor(富文本編輯器)
import VueQuillEditor from 'vue-quill-editor'
// //導入vue-quill-editor的樣式
// import 'quill/dist/quill.core.css'
// import 'quill/dist/quill.snow.css'
// import 'quill/dist/quill.bubble.css'

axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
//請求在到達服務器之前,先會調用use中的這個回調函數來添加請求頭信息
axios.interceptors.request.use(config => {
  //當進入request攔截器,表示發送了請求,我們就開啟進度條
  NProgress.start()
  //為請求頭對象,添加token驗證的Authorization字段
  config.headers.Authorization = window.sessionStorage.getItem("token")
  //必須返回config
  return config
})
//在response攔截器中,隱藏進度條
axios.interceptors.response.use(config =>{
  //當進入response攔截器,表示請求已經結束,我們就結束進度條
  NProgress.done()
  return config
})
Vue.prototype.$http = axios

Vue.config.productionTip = false

//全局注冊組件
Vue.component('tree-table', TreeTable)
//全局注冊富文本組件
Vue.use(VueQuillEditor)

//創建過濾器將秒數過濾為年月日,時分秒
Vue.filter('dateFormat',function(originVal){
  const dt = new Date(originVal)
  const y = dt.getFullYear()
  const m = (dt.getMonth()+1+'').padStart(2,'0')
  const d = (dt.getDate()+'').padStart(2,'0')

  const hh = (dt.getHours()+'').padStart(2,'0')
  const mm = (dt.getMinutes()+'').padStart(2,'0')
  const ss = (dt.getSeconds()+'').padStart(2,'0')

  return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

然后打開public/index.html添加外部cdn引入代碼

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>電商后台管理系統</title>

    <!-- nprogress 的樣式表文件 -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.css" />
    <!-- 富文本編輯器 的樣式表文件 -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.core.min.css" />
    <link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.snow.min.css" />
    <link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.bubble.min.css" />
    <!-- element-ui 的樣式表文件 -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/element-ui/2.8.2/theme-chalk/index.css" />

    <script src="https://cdn.staticfile.org/vue/2.5.22/vue.min.js"></script>
    <script src="https://cdn.staticfile.org/vue-router/3.0.1/vue-router.min.js"></script>
    <script src="https://cdn.staticfile.org/axios/0.18.0/axios.min.js"></script>
    <script src="https://cdn.staticfile.org/lodash.js/4.17.11/lodash.min.js"></script>
    <script src="https://cdn.staticfile.org/echarts/4.1.0/echarts.min.js"></script>
    <script src="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.js"></script>
    <!-- 富文本編輯器的 js 文件 -->
    <script src="https://cdn.staticfile.org/quill/1.3.4/quill.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-quill-editor@3.0.4/dist/vue-quill-editor.js"></script>

    <!-- element-ui 的 js 文件 -->
    <script src="https://cdn.staticfile.org/element-ui/2.8.2/index.js"></script>

  </head>
  <body>
    <noscript>
      <strong>We're sorry but vue_shop doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

  1. 定制首頁內容
    開發環境的首頁和發布環境的首頁展示內容的形式有所不同
    如開發環境中使用的是import加載第三方包,而發布環境則是使用CDN,那么首頁也需根據環境不同來進行不同的實現
    我們可以通過插件的方式來定制首頁內容,打開vue.config.js,編寫代碼如下:
module.exports = {
    chainWebpack:config=>{
        config.when(process.env.NODE_ENV === 'production',config=>{
            ......
            
            //使用插件
            config.plugin('html').tap(args=>{
                //添加參數isProd
                args[0].isProd = true
                return args
            })
        })

        config.when(process.env.NODE_ENV === 'development',config=>{
            config.entry('app').clear().add('./src/main-dev.js')

            //使用插件
            config.plugin('html').tap(args=>{
                //添加參數isProd
                args[0].isProd = false
                return args
            })
        })
    }
}

然后在public/index.html中使用插件判斷是否為發布環境並定制首頁內容

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.isProd ? '' : 'dev - ' %>電商后台管理系統</title>

    <% if(htmlWebpackPlugin.options.isProd){ %>
    <!-- nprogress 的樣式表文件 -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.css" />
    ........
    <!-- element-ui 的 js 文件 -->
    <script src="https://cdn.staticfile.org/element-ui/2.8.2/index.js"></script>
    <% } %>
  </head>
  .......

  1. 路由懶加載
    當路由被訪問時才加載對應的路由文件,就是路由懶加載。
    路由懶加載實現步驟:

    • 安裝 @babel/plugin-syntax-dynamic-import
      打開vue控制台,點擊依賴->安裝依賴->開發依賴->搜索@babel/plugin-syntax-dynamic-import
      點擊安裝。

    • 在babel.config.js中聲明該插件,打開babel.config.js

//項目發布階段需要用到的babel插件
const productPlugins = []

//判斷是開發還是發布階段
if(process.env.NODE_ENV === 'production'){
  //發布階段
  productPlugins.push("transform-remove-console")
}

module.exports = {
  "presets": [
    "@vue/app"
  ],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ],
    ...productPlugins,
    //配置路由懶加載插件
    "@babel/plugin-syntax-dynamic-import"
  ]
}	

​ * 將路由更改為按需加載的形式,打開router.js,更改引入組件代碼如下:

import Vue from 'vue'
import Router from 'vue-router'
const Login = () => import(/* webpackChunkName:"login_home_welcome" */ './components/Login.vue')
// import Login from './components/Login.vue'
const Home = () => import(/* webpackChunkName:"login_home_welcome" */ './components/Home.vue')
// import Home from './components/Home.vue'
const Welcome = () => import(/* webpackChunkName:"login_home_welcome" */ './components/Welcome.vue')
// import Welcome from './components/Welcome.vue'
const Users = () => import(/* webpackChunkName:"user" */ './components/user/Users.vue')
// import Users from './components/user/Users.vue'
const Rights = () => import(/* webpackChunkName:"power" */ './components/power/Rights.vue')
// import Rights from './components/power/Rights.vue'
const Roles = () => import(/* webpackChunkName:"power" */ './components/power/Roles.vue')
// import Roles from './components/power/Roles.vue'
const Cate = () => import(/* webpackChunkName:"goods" */ './components/goods/Cate.vue')
// import Cate from './components/goods/Cate.vue'
const Params = () => import(/* webpackChunkName:"goods" */ './components/goods/Params.vue')
// import Params from './components/goods/Params.vue'
const GoodList = () => import(/* webpackChunkName:"goods" */ './components/goods/List.vue')
// import GoodList from './components/goods/List.vue'
const GoodAdd = () => import(/* webpackChunkName:"goods" */ './components/goods/Add.vue')
// import GoodAdd from './components/goods/Add.vue'
const Order = () => import(/* webpackChunkName:"order" */ './components/order/Order.vue')
// import Order from './components/order/Order.vue'
const Report = () => import(/* webpackChunkName:"report" */ './components/report/Report.vue')
// import Report from './components/report/Report.vue'

  1. 項目上線
    A.通過node創建服務器
    在vue_shop同級創建一個文件夾vue_shop_server存放node服務器
    使用終端打開vue_shop_server文件夾,輸入命令 npm init -y
    初始化包之后,輸入命令 npm i express -S
    打開vue_shop目錄,復制dist文件夾,粘貼到vue_shop_server中
    在vue_shop_server文件夾中創建app.js文件,編寫代碼如下:

    const express = require('express')
    
    const app = express()
    
    app.use(express.static('./dist'))
    
    app.listen(8998,()=>{
        console.log("server running at http://127.0.0.1:8998")
    })
    
    

然后再次在終端中輸入 node app.js

​ B.開啟gzip壓縮
​ 打開vue_shop_server文件夾的終端,輸入命令:npm i compression -D打開app.js,編寫代碼:

const express = require('express')

const compression = require('compression')

const app = express()

app.use(compression())
app.use(express.static('./dist'))

app.listen(8998,()=>{
    console.log("server running at http://127.0.0.1:8998")
})

配置https服務
配置https服務一般是后台進行處理,前端開發人員了解即可。
首先,需要申請SSL證書,進入https://freessl.cn官網
在后台導入證書,打開今天資料/素材,復制素材中的兩個文件到vue_shop_server中
打開app.js文件,編寫代碼導入證書,並開啟https服務

const express = require('express')
const compression = require('compression')
const https = require('https')
const fs = require('fs')

const app = express()
//創建配置對象設置公鑰和私鑰
const options = {
    cert:fs.readFileSync('./full_chain.pem'),
    key:fs.readFileSync('./private.key')
}

app.use(compression())
app.use(express.static('./dist'))

// app.listen(8998,()=>{
//     console.log("server running at http://127.0.0.1:8998")
// })

//啟動https服務
https.createServer(options,app).listen(443)

注意:因為我們使用的證書有問題,所以無法正常使用https服務

D.使用pm2管理應用
打開vue_shop_server文件夾的終端,輸入命令:npm i pm2 -g
使用pm2啟動項目,在終端中輸入命令:pm2 start app.js --name 自定義名稱
查看項目列表命令:pm2 ls
重啟項目:pm2 restart 自定義名稱
停止項目:pm2 stop 自定義名稱
刪除項目:pm2 delete 自定義名稱

項目源碼地址

碼雲:https://gitee.com/garyxirapper/vue_shop.git

github:https://github.com/LionSSSN/vue_shop.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM