XXX取快递后台管理系统-项目总结

248 阅读4分钟

项目初始化

1. vue-router

默认路由使用 redirect, 例如:

{ path: "/", name: "OrderManage", redirect: "/orderManage", }

在写 redirect 的时候,可以省略 component 配置,因为它从来没有被直接访问过,所以没有组件要渲染。唯一的例外是嵌套路由:如果一个路由记录有 children 和 redirect 属性,它也应该有 component 属性。

import Vue from 'vue'
import store from "../store";
import VueRouter from "vue-router";

Vue.use(VueRouter)

const routes = [
  {
    path: "/",
    name: "Main",
    component: () => import("../views/Main.vue"),
    children: [
      {
        path: "/",
        name: "OrderManage",
        redirect: "/orderManage",
      },
      {
        path: "/orderManage",
        name: "OrderManage",
        component: () => import("../views/OrderManage.vue"),
      },
      {
        path: "/userManage",
        name: "UserManage",
        component: () => import("../views/UserManage.vue"),
      },
      {
        path: "/administrate",
        name: "Administrate",
        component: () => import("../views/Administrate.vue"),
      },
    ],
  },
  {
    path: "/login",
    name: "Login",
    component: () => import("../views/Login.vue"),
  },
];

const router = new VueRouter({
  routes,
});

//登录拦截
router.beforeEach(function (to, from, next) {
  let userInfo = store.state.admin;
  if (userInfo) {
    next();
  } else {
    if (to.path === "/login") {
      next();
    } else {
      next({ path: "/login" });
    }
  }
});

export default router

2. mock

要注意mock的写法

import Mock from "mockjs";

Mock.mock(/\/orderList.*/, "get", {
  pagination: {
    pageno: 3, //当前页
    pagesize: 20, // 当前页的大小
    pages: 12, // 总共多少页 一般不用 total/size
    total: 123, // 总条数
  },
  data: [
    {
      orderID: "1236",
      type: "0",
      express: "京东自营",
      custom: "小明",
      posterNumber: "2-2-1101",
      address: "大学9号宿舍楼",
      money: "3",
      status: "0",
      admin: { name: "@cname", id: "@id" },
      createAt: "@date",
      updateAt: "@date",
    },
  ],
});

Mock.mock(/\/userList.*/, "get", {
  pagination: {
    pageno: 4, //当前页
    pagesize: 20, // 当前页的大小
    pages: 12, // 总共多少页 一般不用 total/size
    total: 123, // 总条数
  },
  data: [
    {
      userID: "12456789",
      name: "小明",
      address: "大学9号宿舍楼",
      createAt: "@date",
      updateAt: "@date",
    },
    
  ],
});

Mock.mock("/adminList", "get", {
  pagination: {
    pageno: 4, //当前页
    pagesize: 20, // 当前页的大小
    pages: 12, // 总共多少页 一般不用 total/size
    total: 123, // 总条数
  },
  data: [
    {
      account: "admin",
      password: "2133224",
    },
    {
      account: "admin2",
      password: "2133224",
    },
    {
      account: "admin3",
      password: "2133224",
    },
    {
      account: "admin4",
      password: "2133224",
    },
  ],
});

Mock.mock("/login", "post", {
  message: "登陆成功!",
  success: true,
});

Mock.mock("/deleteOrder", "post", {
  message: "删除成功!",
  success: true,
});
Mock.mock("/deleteAdmin", "post", {
  message: "删除成功!",
  success: true,
});

Mock.mock(/\/admin.*/, "put", {
  message: "更新成功",
  success: true,
});

3. axios统一拦截配置

import axios from 'axios';

// 创建axios实例
const service = axios.create({
  // baseURL: process.env.BASE_API, // api的base_url
  timeout: 60000 // 请求超时时间,1分钟
});

// request拦截器
service.interceptors.request.use(
  (config) => {
    // Do something before request is sent
    // if (store.getters.token) {
    //   config.headers['Authorization'] = getToken(); // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
    // }
    return config;
  },
  (error) => {
    Promise.reject(error);
  }
);

// respone拦截器
service.interceptors.response.use(
  (response) => {
    /**
     * 下面的注释为通过response自定义code来标示请求状态,当code返回如下情况为权限有问题,登出并返回到登录页
     * 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
     */

    return Promise.resolve(response);
  },
  (error) => {
    return Promise.reject(error);
  }
);

export default service;

1. 登录页

有admin 和 普通用户的区别,通过账号区分,admin多一个管理员管理功能

将登录信息存储在vuex中

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    admin: ''
  },
  mutations: {
    setAdmin(state, payload) {
      state.admin = payload;
    }
  },
});

解决vuex页面刷新数据丢失问题,vuex的持久化,使用localstorage

export default new Vuex.Store({
  state: {
    admin: localStorage.getItem('admin') || ''
  },
  mutations: {
    setAdmin(state, payload) {
      state.admin = payload;
      if (payload) {
        localStorage.setItem('admin', payload);
      } else {
        localStorage.removeItem('admin');
      }
    }
  },
});

使用computed 和 v-if 来判断账号是否是admin管理员账号

<el-menu-item v-if="isAdmin" index="/administrate">
   <template slot="title">
      <i class="el-icon-connection"></i>
      <span>管理员管理</span>
   </template>
</el-menu-item>
          
computed: {
    isAdmin() {
      return this.$store.state.admin === 'admin';
    }
  },

2. orderManage 用户订单管理页

将快递类型和订单状态单独写为一个类:

const posterType = {
  0: "京东自营",
  1: "申通",
  2: "圆通",
  3: "顺丰",
  4: "邮政",
  5: "菜⻦裹裹",
};
const orderStatus = {
  0: "未支付",
  1: "已支付",
  2: "已取件",
  3: "已配送",
  4: "已取消",
};

将axios请求接口方法进行封装

    /**
     * 请求数据
     */
    getOrderList() {
      let obj = {};
      Object.keys(this.form).forEach(item => {
        if (this.form[item] !== '') {
          obj[item] = this.form[item];
        }
      })
      this.loading = true;
      this.$axios.get("/orderList", {
        params: obj
      })
      .then((res) => {
        this.orderList = res.data.data.map((item) => {
          item.type = posterType[item.type];
          return item;
        });
        this.orderList = res.data.data.map((item) => {
          item.status = orderStatus[item.status];
          return item;
        });
        this.pagination = res.data.pagination;
      })
      .catch((err) => {
        console.log(err);
      }).finally(e=> {
        setTimeout(e => {
          this.loading = false;
        }, 1000);
      });
    },

要注意:无论是分页还是删除,都需要使用接口请求,而不是只在页面进行分页和删除


   //处理表格的index
   indexMethod(index){
      return (this.pagination.pageno - 1) * this.pagination.pagesize + index + 1
    },
    //分页
    handleSizeChange(val) {
      this.form.pagesize = val;
      this.getOrderList();

      console.log(`每页 ${val} 条`);
    },
    handleCurrentChange(val) {
      this.form.pageno = val;
      this.getOrderList();

       console.log(`当前页: ${val}`);
    },
    //删除功能
    handleDelete(index, row) {
      this.$confirm(`确认是否删除订单号为${row.orderID}的记录?`, "删除", {
        confirmButtonText: "确定",
      }).then(() => {
        this.$axios.post("/deleteOrder", {
            orderID: row.orderID,
          })
          .then((res) => {
            if (res && res.data.success) {
              this.$message.success("删除成功");
              this.orderList.splice(index, 1);
              this.getOrderList();
            } else {
              this.$message.error("删除失败");
            }
          });
      });
    }

userManage 用户管理页

与订单管理页大同小异(序号、分页),不需要考虑订单状态和快递类型

将axios请求接口方法进行封装

    /**
     * 请求数据
     */
    getUserList() {
      let obj = {};
      Object.keys(this.form).forEach((item) => {
        if (this.form[item] !== "") {
          obj[item] = this.form[item];
        }
      });
      this.loading = true;
      this.$axios
        .get("/userList", {
          params: obj,
        })
        .then((res) => {
          this.userList = res.data.data
          this.pagination = res.data.pagination;
        })
        .catch((err) => {
          console.log(err);
        })
        .finally((e) => {
          setTimeout((e) => {
            this.loading = false;
          }, 1000);
        });
    },

表格的序号、分页功能

    /**
     * 处理表格的index
     */
    indexMethod(index) {
      return (
        (this.pagination.pageno - 1) * this.pagination.pagesize + index + 1
      );
    },
    // 分页
    handleSizeChange(val) {
      this.form.pagesize = val;
      this.getUserList();
    },
    handleCurrentChange(val) {
      this.form.pageno = val;
      this.getUserList();
    },

administrate 管理员管理页

将axios请求接口方法

mounted() {
    this.$axios
      .get("/adminList")
      .then((res) => {
        this.adminList = res.data.data;
        this.pagination = res.data.pagination;
      })
      .catch((err) => {
        console.log(err);
      });
  }

列表的序号、分页

    //序号
    indexMethod(index) {
      return (
        (this.pagination.pageno - 1) * this.pagination.pagesize + index + 1
      );
    },
    //分页
    handleSizeChange(val) {
      this.pagination.pagesize = val;
    },
    handleCurrentChange(val) {
      this.pagination.currentPage = val;
    },

需要注意的点

密码修改

    /**
     * 弹出修改密码的对话框
     */
    handleEdit(index, row) {
      console.log(index, row);
      this.dialogFormVisible = true
      this.dialogForm.account = row.account
    },
    //对话框的密码编辑
    editPassword(){
      if (this.dialogForm.password !== this.dialogForm.confirmPassword) {
        this.$message.warning('两次输入的密码不一致!');
        return;
      }
      this.$axios.put(`/admin/${this.dialogForm.account}`, {
        password: this.dialogForm.password
      }).then(res => {
        if (res.data.success) {
          this.$message.success('修改成功');
          let index = this.adminList.findIndex(item => item.account === this.dialogForm.account);
          this.adminList[index].password = this.dialogForm.password;
        } else {
          this.$message.success('修改失败');
        }
      }).finally(e => {
        this.dialogFormVisible = false;
      })
   },

删除功能

handleDelete(index, row) {
      this.$confirm(`确认是否删除账号为${row.account}的记录?`, "删除", {
        confirmButtonText: "确定",
      }).then(() => {
        let res = this.adminList.splice(index, 1);
        let msg = "删除成功";
        if (!res) {
          msg = "删除失败";
        }
        this.$message({
          type: "info",
          message: msg,
        });
      });
    },

main.js的配置

import './sass/reset.scss'
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from "./store";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
import "../mock/index.js";
import axios from "axios";

Vue.use(ElementUI);
Vue.config.productionTip = false;
Vue.prototype.$axios = axios;

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

element UI需要关注的点

// Form 表单
model | 表单数据对象 | object 
inline | 行内表单模式 | boolean 
disabled | 是否禁用该表单内的所有组件。若设置为 true,则表单内组件上的 disabled 属性不再生效 | boolean 
prop | 表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的 | string 
label | 标签文本 | string 
required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean 

// Table 表格
data | 显示的数据 | array 
type | 对应列的类型。如果设置了 `selection` 则显示多选框;如果设置了 `index` 则显示该行的索引(从 1 开始计算);如果设置了 `expand` 则显示为一个可展开的按钮 | string 
index | 如果设置了 `type=index`,可以通过传递 `index` 属性来自定义索引 | number, Function(index) 
prop | 对应列内容的字段名,也可以使用 property 属性 | string