电商管理系统(PC端)项目实施



一、创建项目

1.安装 vue-cli

$ npm install -g @vue/cli

2.创建项目

$ vue create management
  1. 第一步:image-20230104140734184

  2. 第二步:image-20230104140759157

  3. 第三步:image-20230104140823294

  4. 第四步:image-20230104140846665

  5. 第五步:image-20230104140901679

  6. 第六步:image-20230104140917470

  7. 项目创建成功:image-20230104141122607

二、安装使用 Element UI

1.安装 Element UI

$ npm i element-ui -S

2.引入使用Element UI

修改 main.js

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

三、添加登录页

image-20230104153226267

1.views 文件夹新建 LoginView.vue

2.添加访问路由

{
    path: '/login',
    name: 'login',
    component: () => import('../views/LoginView')
},

3.修改 LoginView.vue

<template>
  <div class="login">
    <section>
      <h2>电商管理系统</h2>
      <el-form
        :model="loginForm"
        status-icon
        :rules="rules"
        ref="loginForm"
      >
        <el-form-item prop="username">
          <el-input v-model="loginForm.username" prefix-icon="el-icon-user" placeholder="用户名"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input type="password" v-model="loginForm.password" prefix-icon="el-icon-lock" placeholder="密码"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button style="width: 100%" type="primary" @click="handleLogin('loginForm')">登录</el-button>
        </el-form-item>
      </el-form>
    </section>
  </div>
</template>

<script>
export default {
  data() {
    return {
      loginForm: {
        username: 'admin123',
        password: 'admin123',
        code: ''
      },
      rules: {
        username: [
          { required: true, message: '用户名不能为空', trigger: 'blur' },
        ],
        password: [
          { required: true, message: '密码不能为空', trigger: 'blur' },
        ],
      }
    }
  },
  methods: {
    handleLogin(formName) {
      this.$refs[formName].validate(valid => {
        if (valid) {
          //
        } else {
          console.log('error submit!')
          return false
        }
      })
    }
  }
}
</script>

<style scoped>
.login {
  display: flex;
  align-items: center;
  justify-content: center;
  background: url("../assets/login-background.jpg") no-repeat center / cover;
  height: 100vh;
}

.login h2 {
  text-align: center;
  color: #707070;
}

.login section {
  border-radius: 6px;
  background: #fff;
  width: 400px;
  padding: 25px 25px 5px 25px;
}
</style>

4.添加登录功能

1)导入数据库

drop database if exists litemall;
drop user if exists 'litemall'@'%';
-- 支持emoji:需要mysql数据库参数: character_set_server=utf8mb4
create database litemall default character set utf8mb4 collate utf8mb4_unicode_ci;
use litemall;
create user 'litemall'@'%' identified by 'litemall123456';
grant all privileges on litemall.* to 'litemall'@'%';
flush privileges;
  1. 导入 litemall_table.sql 文件创建数据表

  2. 导入 litemall_data.sql 文件创建数据

2)启动 litemall-admin-api-0.1.0-exec.jar

$ java -jar litemall-admin-api-0.1.0-exec.jar

3)安装 axios

$ npm i axios -S

4)修改 main.js

import axios from "axios";
Vue.prototype.$axios = axios

5)完善 LoginView.vue 登录方法

handleLogin(formName) {
    this.$refs[formName].validate(valid => {
        if (valid) {
            this.$axios({
                method: 'POST',
                url: 'http://localhost:8080/admin/auth/login',
                data: {
                    ...this.loginForm
                }
            }).then(res => {
                // console.log(res.data.data)
                localStorage.setItem('X-Litemall-Admin-Token', res.data.data.token)
                this.$router.push({
                    name: 'home'
                })
            }).catch(e => {
                console.log(e)
            })
        } else {
            console.log('error submit!')
            return false
        }
    })
}

四、创建 Layout 布局组件

1. components/ 文件夹创建 Layout.vue

<template>
  <el-container>
    <el-header>
        <span>
          <el-image
            style="width: 50px; height: 50px"
            :src="require('../assets/logo.png')"
            fit="cover"/>
          <b>电商管理系统</b>
        </span>
      <span>
          <el-image
            style="width: 50px; height: 50px"
            :src="$store.state.userinfo.avatar"
            fit="cover"/>
          <span></span>
        </span>
    </el-header>
    <el-container class="container">
      <el-aside width="200px">
        <ul>
          <li><router-link to="/">首页</router-link></li>
        </ul>
      </el-aside>
      <el-main>
        <slot></slot>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default {
  name: "Layout",
}
</script>

<style scoped>
.container {
  height: calc(100vh - 60px);
}

.el-header {
  background-color: #B3C0D1;
  color: #333;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.el-header span {
  display: flex;
  align-items: center;
}

.el-header span .el-image {
  margin-right: 5px;
}

.el-aside {
  background-color: #D3DCE6;
  color: #333;
}

.el-aside ul {
  list-style: none;
}

.el-aside ul li {
  height: 40px;
  line-height: 40px;
}

.el-aside a {
  text-decoration: none;
  color: #333333;
}

.el-aside a.router-link-exact-active {
  color: #42b983;
}

.el-main {
  background-color: #E9EEF3;
  color: #333;
}
</style>

2.main.js 注册组件

import Layout from "@/components/Layout";
Vue.component(Layout.name, Layout)

五、完善主页

image-20230104153305340

修改 views/HomeView.vue

<template>
  <layout class="home">
    <el-row :gutter="20" class="data">
      <el-col :span="6">
        <el-statistic group-separator="," :value="dashboard.goodsTotal" title="商品总量"></el-statistic>
      </el-col>
      <el-col :span="6">
        <el-statistic group-separator="," :value="dashboard.userTotal" title="用户数量"></el-statistic>
      </el-col>
      <el-col :span="6">
        <el-statistic group-separator="," :value="dashboard.productTotal" title="商品数量"></el-statistic>
      </el-col>
      <el-col :span="6">
        <el-statistic group-separator="," :value="dashboard.orderTotal" title="订单总量"></el-statistic>
      </el-col>
    </el-row>
  </layout>
</template>

<script>

export default {
  name: 'HomeView',
  data() {
    return {
      dashboard: {
        goodsTotal: 0,
        userTotal: 0,
        productTotal: 0,
        orderTotal: 0
      }
    };
  },
  created() {
    this.$axios({
      headers: {
        'X-Litemall-Admin-Token': localStorage.getItem('X-Litemall-Admin-Token')
      },
      url: 'http://localhost:8080/admin/dashboard',
    }).then(res => {
      this.dashboard = res.data.data
    })
  }
}
</script>
<style scoped>
.home .data {
  margin-top: 300px;
}
</style>

六、添加商品管理页

image-20230104153401712

1.views/ 文件夹创建 GoodsList.vue

<template>
  <layout>
    <el-card class="box-card">
      <div slot="header" style="display: flex; justify-content: space-between">
        <span>商品列表</span>
        <el-button type="primary" size="mini">上架商品</el-button>
      </div>
      <el-table
        :data="goodsList"
        style="width: 100%">
        <el-table-column label="商品名称" prop="name"/>
        <el-table-column label="商品图片">
          <template slot-scope="scope">
            <el-image style="width: 100px;" :src="scope.row.picUrl" fit="cover"/>
          </template>
        </el-table-column>
        <el-table-column label="简介" prop="brief"/>
        <el-table-column label="销售价" prop="retailPrice" width="100" align="center"/>
        <el-table-column label="库存价" prop="counterPrice" width="100" align="center"/>
        <el-table-column label="创建时间" prop="addTime"/>
        <el-table-column label="更新时间" prop="updateTime"/>
        <el-table-column label="是否上架" prop="isOnSale" width="100" align="center">
          <template slot-scope="scope">
            <el-switch
              v-model="!scope.row.deleted"
              active-color="#13ce66"
              inactive-color="#ff4949"/>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="180" align="center">
          <template slot-scope="scope">
            <el-button size="mini" type="primary" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
            <el-button size="mini" type="danger" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination
        background
        layout="prev, pager, next"
        :page-count="pager.pages"
        :current-page="pager.page"
        @current-change="handlePageChange"
      >
      </el-pagination>
    </el-card>
  </layout>
</template>

<script>
export default {
  name: "GoodsList",
  data() {
    return {
      goodsList: [],
      pager: {
        page: 1,
        limit: 20,
        total: 1,
        pages: 1
      }
    }
  },
  created() {
    this.$axios({
      headers: {
        'X-Litemall-Admin-Token': localStorage.getItem('X-Litemall-Admin-Token')
      },
      url: "http://localhost:8080/admin/goods/list?page=1&limit=20&goodsSn&name&sort=add_time&order=desc&goodsId"
    }).then(res => {
      // console.log(res.data.data)
      this.goodsList = res.data.data.list
      this.pager = {
        total: res.data.data.total,
        pages: res.data.data.pages,
        page: res.data.data.page,
        limit: res.data.data.limit,
      }
    })
  },
  methods: {
    handlePageChange(page) {
      this.$axios({
        headers: {
          'X-Litemall-Admin-Token': localStorage.getItem('X-Litemall-Admin-Token')
        },
        url: `http://localhost:8080/admin/goods/list?page=${page}&limit=20&goodsSn&name&sort=add_time&order=desc&goodsId`
      }).then(res => {
        // console.log(res.data.data)
        this.goodsList = res.data.data.list
        this.pager = {
          total: res.data.data.total,
          pages: res.data.data.pages,
          page: res.data.data.page,
          limit: res.data.data.limit,
        }
      })
    },
    handleEdit(row) {
      console.log(row.id)
    },
    handleDelete(row) {
      this.$confirm('此操作将永久删除该商品, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.$message({
          type: 'success',
          message: '删除成功!'
        });
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除'
        });
      });
    }
  }
}
</script>

<style scoped>

</style>

2.添加访问路由

  {
    path: '/goods',
    name: 'goods-list',
    component: () => import('../views/GoodsList')
  },

七、添加商品分类页

image-20230104153415183

1.views/ 文件夹创建 CategoryList.vue

<template>
  <layout>
    <el-card class="box-card">
      <div slot="header" style="display: flex; justify-content: space-between">
        <span>分类列表</span>
        <el-button type="primary" size="mini" @click="$router.push({ name: 'category-create' })">新建分类</el-button>
      </div>
      <el-table
        :data="categoryList"
        style="width: 100%;"
        row-key="id"
        border
        :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
      >
        <el-table-column label="名称" prop="name"/>
        <el-table-column label="层级" prop="level" width="70" align="center"/>
        <el-table-column label="图片">
          <template slot-scope="scope">
            <el-image style="width: 100%;" :src="scope.row.picUrl" fit="cover"/>
          </template>
        </el-table-column>
        <el-table-column label="图标" width="100" align="center">
          <template slot-scope="scope">
            <el-image style="width: 50px;" :src="scope.row.iconUrl" fit="cover"/>
          </template>
        </el-table-column>
        <el-table-column label="描述" prop="desc"/>
        <el-table-column label="操作" width="130" align="center">
          <template slot-scope="scope">
            <el-button size="mini" circle type="primary" icon="el-icon-view" @click="handleDetail(scope.row)"/>
            <el-button size="mini" circle type="primary" icon="el-icon-edit" @click="handleEdit(scope.row)"/>
            <el-button size="mini" circle type="danger" icon="el-icon-delete" @click="handleDelete(scope.row)"/>
          </template>
        </el-table-column>
      </el-table>
    </el-card>
  </layout>
</template>

<script>
export default {
  name: "CategoryList",
  data() {
    return {
      categoryList:  []
    }
  },
  created() {
    this.getList()
  },
  methods: {
    getList() {
      this.$axios({
        headers: {
          'X-Litemall-Admin-Token': localStorage.getItem('X-Litemall-Admin-Token')
        },
        url: 'http://localhost:8080/admin/category/list'
      }).then(res => {
        this.categoryList = res.data.data.list
        // console.log(res.data.data)
      })
    },
    handleDetail(row) {
      this.$router.push({
        name: 'category-detail',
        params: {
          id: row.id
        }
      })
    },
    handleEdit(row) {
      this.$router.push({
        name: 'category-edit',
        params: {
          id: row.id
        }
      })
    },
    handleDelete(row) {

    }
  }
}
</script>
<style scoped>
</style>

2.添加路由访问

{
    path: '/category',
    name: 'category-list',
    component: () => import('../views/CategoryList')
  },

八、添加分类创建页

image-20230104153429855

1.views/ 文件夹创建 CategoryCreate.vue

<template>
  <layout>
    <el-card class="box-card">
      <div slot="header" style="display: flex; justify-content: space-between">
        <span>新建分类</span>
      </div>
      <el-tabs type="border-card" v-model="levelVal" @tab-click="handleTabChange">
        <el-tab-pane label="一级类目" name="L1">
          <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
            <el-form-item label="名称" prop="name">
              <el-input v-model="ruleForm.name" placeholder="请输入名称"></el-input>
            </el-form-item>
            <el-form-item label="图片" prop="picUrl">
              <el-upload
                class="avatar-uploader"
                action="http://localhost:8080/admin/storage/create"
                :headers="headers"
                :show-file-list="false"
                :on-success="handleAvatarSuccess"
              >
                <img v-if="ruleForm.picUrl" :src="ruleForm.picUrl" class="avatar">
                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
              </el-upload>
            </el-form-item>
            <el-form-item label="描述" prop="desc">
              <el-input type="textarea" v-model="ruleForm.desc"></el-input>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button>
              <el-button @click="resetForm('ruleForm')">重置</el-button>
            </el-form-item>
          </el-form>
        </el-tab-pane>
        <el-tab-pane label="二级类目" name="L2">
          <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
            <el-form-item label="父分类" prop="pid">
              <el-select v-model="ruleForm.pid" placeholder="请选择父分类">
                <el-option
                  v-for="item of level1"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
            <el-form-item label="名称" prop="name">
              <el-input v-model="ruleForm.name" placeholder="请输入名称"></el-input>
            </el-form-item>
            <el-form-item label="图片" prop="picUrl">
              <el-upload
                class="avatar-uploader"
                action="http://localhost:8080/admin/storage/create"
                :headers="headers"
                :show-file-list="false"
                :on-success="handleAvatarSuccess"
              >
                <img v-if="ruleForm.picUrl" :src="ruleForm.picUrl" class="avatar">
                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
              </el-upload>
            </el-form-item>
            <el-form-item label="描述" prop="desc">
              <el-input type="textarea" v-model="ruleForm.desc"></el-input>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button>
              <el-button @click="resetForm('ruleForm')">重置</el-button>
            </el-form-item>
          </el-form>
        </el-tab-pane>
      </el-tabs>
    </el-card>
  </layout>
</template>

<script>
export default {
  name: "CategoryCreate",
  data() {
    return {
      headers: {
        'X-Litemall-Admin-Token': window.localStorage.getItem('X-Litemall-Admin-Token')
      },
      ruleForm: {
        pid: '',
        name: '',
        desc: '',
        iconUrl: '',
        picUrl: '',
        level: "L2",
      },
      rules: {
        pid: [{ required: true, message: '请选择父分类', trigger: 'change' }],
        name: [{ required: true, message: '请输入名称', trigger: 'blur' },],
        picUrl: [{ required: true, message: '图片不能为空', trigger: 'change' }]
      },
      level1: [],
      levelVal: 'L2',
    }
  },
  created() {
    this.$axios({
      url: 'http://localhost:8080/admin/category/l1',
      headers: {
        'X-Litemall-Admin-Token': window.localStorage.getItem('X-Litemall-Admin-Token')
      }
    }).then(res => {
      // console.log(res.data.data)
      this.level1 = res.data.data.list
    })
  },
  methods: {
    handleTabChange(t) {
      console.log(t.name)
      switch (t.name) {
        case 'L1':
          this.ruleForm = {
            name: '',
            desc: '',
            iconUrl: '',
            picUrl: '',
            level: "L1",
          }
          this.rules = {
            name: [{ required: true, message: '请输入名称', trigger: 'blur' },],
            picUrl: [{ required: true, message: '图片不能为空', trigger: 'change' }]
          }
          break
        case 'L2':
          this.ruleForm = {
            pid: '',
            name: '',
            desc: '',
            iconUrl: '',
            picUrl: '',
            level: "L2",
          }
          this.rules = {
            pid: [{ required: true, message: '请选择父分类', trigger: 'change' }],
            name: [{ required: true, message: '请输入名称', trigger: 'blur' },],
            picUrl: [{ required: true, message: '图片不能为空', trigger: 'change' }]
          }
          break
      }
    },
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.$axios({
            method: 'POST',
            url: ' http://localhost:8080/admin/category/create',
            headers: {
              'X-Litemall-Admin-Token': window.localStorage.getItem('X-Litemall-Admin-Token')
            },
            data: {
              ...this.ruleForm
            }
          }).then(res => {
            // console.log(res)
            if (res.data.errno === 0) {
              this.$router.replace({
                name: 'category-list'
              })
            }
          }).catch(e => {
            console.log(e)
          })
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
    handleAvatarSuccess(res, file) {
      this.ruleForm.picUrl = res.data.url;
    }
  }
}
</script>

<style>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}

.avatar-uploader .el-upload:hover {
  border-color: #409EFF;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
.avatar {
  width: 178px;
  height: 178px;
  display: block;
}
</style>

2.添加路由访问

{
    path: '/category/create',
    name: 'category-create',
    component: () => import('../views/CategoryCreate')
  },

九、添加分类详情页

image-20230104153448306

1.views/ 文件夹创建 `CategoryDetail.vue

2.添加路由访问

{
    path: '/category/:id',
    name: 'category-detail',
    component: () => import('../views/CategoryDetail')
  },

十、添加分类编辑页

image-20230104153502524

1.views/ 文件夹创建 CategoryEdit.vue

<template>
  <layout>
    <el-card class="box-card">
      <div slot="header" style="display: flex; justify-content: space-between">
        <span>分类编辑</span>
      </div>
      <el-tabs type="border-card" v-model="levelVal" @tab-click="handleTabChange">
        <el-tab-pane label="一级类目" name="L1">
          <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
            <el-form-item label="名称" prop="name">
              <el-input v-model="ruleForm.name" placeholder="请输入名称"></el-input>
            </el-form-item>
            <el-form-item label="图片" prop="picUrl">
              <el-upload
                class="avatar-uploader"
                action="http://localhost:8080/admin/storage/create"
                :headers="headers"
                :show-file-list="false"
                :on-success="handleAvatarSuccess"
              >
                <img v-if="ruleForm.picUrl" :src="ruleForm.picUrl" class="avatar">
                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
              </el-upload>
            </el-form-item>
            <el-form-item label="描述" prop="desc">
              <el-input type="textarea" v-model="ruleForm.desc"></el-input>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
            </el-form-item>
          </el-form>
        </el-tab-pane>
        <el-tab-pane label="二级类目" name="L2">
          <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
            <el-form-item label="父分类" prop="pid">
              <el-select v-model="ruleForm.pid" placeholder="请选择父分类">
                <el-option
                  v-for="item of level1"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
            <el-form-item label="名称" prop="name">
              <el-input v-model="ruleForm.name" placeholder="请输入名称"></el-input>
            </el-form-item>
            <el-form-item label="图片" prop="picUrl">
              <el-upload
                class="avatar-uploader"
                action="http://localhost:8080/admin/storage/create"
                :headers="headers"
                :show-file-list="false"
                :on-success="handleAvatarSuccess"
              >
                <img v-if="ruleForm.picUrl" :src="ruleForm.picUrl" class="avatar">
                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
              </el-upload>
            </el-form-item>
            <el-form-item label="描述" prop="desc">
              <el-input type="textarea" v-model="ruleForm.desc"></el-input>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
            </el-form-item>
          </el-form>
        </el-tab-pane>
      </el-tabs>
    </el-card>
  </layout>
</template>

<script>
export default {
  name: "CategoryEdit",
  data() {
    return {
      headers: {
        'X-Litemall-Admin-Token': window.localStorage.getItem('X-Litemall-Admin-Token')
      },
      ruleForm: {
        pid: '',
        name: '',
        desc: '',
        iconUrl: '',
        picUrl: '',
        level: "L2",
      },
      rules: {
        pid: [{ required: true, message: '请选择父分类', trigger: 'change' }],
        name: [{ required: true, message: '请输入名称', trigger: 'blur' },],
        picUrl: [{ required: true, message: '图片不能为空', trigger: 'change' }]
      },
      level1: [],
      levelVal: 'L2',
    }
  },
  created() {
    this.$axios({
      url: 'http://localhost:8080/admin/category/l1',
      headers: {
        'X-Litemall-Admin-Token': window.localStorage.getItem('X-Litemall-Admin-Token')
      }
    }).then(res => {
      // console.log(res.data.data)
      this.level1 = res.data.data.list
    })

    this.$axios({
      method: 'GET',
      url: 'http://localhost:8080/admin/category/read?id='+this.$route.params.id,
      headers: {
        'X-Litemall-Admin-Token': window.localStorage.getItem('X-Litemall-Admin-Token')
      }
    }).then(res => {
      console.log(res)
      this.levelVal = res.data.data.level
      this.ruleForm = res.data.data
      switch (this.levelVal) {
        case 'L1':
          this.rules = {
            pid: [{ required: true, message: '请选择父分类', trigger: 'change' }],
            name: [{ required: true, message: '请输入名称', trigger: 'blur' },],
            picUrl: [{ required: true, message: '图片不能为空', trigger: 'change' }]
          }
          break
        case 'L2':
          this.rules = {
            name: [{ required: true, message: '请输入名称', trigger: 'blur' },],
            picUrl: [{ required: true, message: '图片不能为空', trigger: 'change' }]
          }
          break
      }
    })
  },
  methods: {
    handleTabChange(t) {
      switch (t.name) {
        case 'L1':
          this.rules = {
            name: [{ required: true, message: '请输入名称', trigger: 'blur' },],
            picUrl: [{ required: true, message: '图片不能为空', trigger: 'change' }]
          }
          break
        case 'L2':
          this.rules = {
            pid: [{ required: true, message: '请选择父分类', trigger: 'change' }],
            name: [{ required: true, message: '请输入名称', trigger: 'blur' },],
            picUrl: [{ required: true, message: '图片不能为空', trigger: 'change' }]
          }
          break
      }
    },
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.$axios({
            method: 'POST',
            url: ' http://localhost:8080/admin/category/update',
            headers: {
              'X-Litemall-Admin-Token': window.localStorage.getItem('X-Litemall-Admin-Token')
            },
            data: {
              ...this.ruleForm
            }
          }).then(res => {
            // console.log(res)
            if (res.data.errno === 0) {
              this.$router.replace({
                name: 'category-list'
              })
            }
          }).catch(e => {
            console.log(e)
          })
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    handleAvatarSuccess(res, file) {
      this.ruleForm.picUrl = res.data.url;
    }
  }
}
</script>

<style scoped>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}

.avatar-uploader .el-upload:hover {
  border-color: #409EFF;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
.avatar {
  width: 178px;
  height: 178px;
  display: block;
}
</style>

2.添加路由访问

{
    path: '/category/edit/:id',
    name: 'category-edit',
    component: () => import('../views/CategoryEdit')
  },

十一、添加分类编辑删除功能

image-20230104153521560

handleDelete(row) {
      this.$confirm('此操作将永久删除该分类, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.$axios({
          method: 'POST',
          url: 'http://localhost:8080/admin/category/delete',
          headers: {
            'X-Litemall-Admin-Token': window.localStorage.getItem('X-Litemall-Admin-Token')
          },
          data: {
            ...row
          }
        }).then(res => {
          this.getList()
          this.$message({
            type: 'success',
            message: '删除成功!'
          });
        }).catch(e => {
          console.log(e)
        })
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除'
        });
      });
    }

十二、添加用户信息展示

1.修改 store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import axios from "axios";
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    userinfo: {
      avatar: require('../assets/logo.png')
    }
  },
  getters: {
  },
  mutations: {
    SET_USERINFO(state, data) {
      state.userinfo = data
    }
  },
  actions: {
    getUserinfo({commit}) {
      axios({
        url: 'http://localhost:8080/admin/auth/info',
        headers: {
          'X-Litemall-Admin-Token': localStorage.getItem('X-Litemall-Admin-Token')
        }
      }).then(res => {
        // console.log(res)
        commit('SET_USERINFO', res.data.data)
      })
    }
  },
  modules: {
  }
})

2.修改 Layout.vue

<script>
export default {
  name: "Layout",
  created() {
    this.$store.dispatch('getUserinfo')
  },
  methods: {
    handleLogout() {
      this.$confirm('此操作将退出登录, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.$axios({
          url: 'http://localhost:8080/admin/auth/logout',
          headers: {
            'X-Litemall-Admin-Token': localStorage.getItem('X-Litemall-Admin-Token')
          }
        }).then(res => {
          this.$router.replace({
            name: 'login'
          })
          localStorage.removeItem('X-Litemall-Admin-Token')
          this.$message({
            type: 'success',
            message: '退出登录成功!'
          });
        }).catch(e => {
          console.log(e)
        })
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消退出登录'
        });
      });
    }
  }
}
</script>

十三、添加导航守卫

router.beforeEach((to, from, next) => {
  if (to.name !== 'login' && !localStorage.getItem('X-Litemall-Admin-Token')) {
    next({ name: 'login' })
  } else {
    next()
  }
})