# 一、创建商城管理员

由于我们在前面已经教大家创建了商城超级管理员,因此现在创建商城管理员,就非常简单了。

# 1. 路由

app/router/admin/shop.js

module.exports = app => {
    const { router, controller } = app;
    ...

    //删除管理员功能
    router.get('/shop/admin/shopmanager/:id/delete', controller.admin.shopManager.delete);
    //修改管理员状态功能
    router.post('/shop/admin/shopmanager/:id/update_status',controller.admin.shopManager.updateStatus);
    //修改管理员界面
    router.get('/shop/admin/shopmanager/edit/:id', controller.admin.shopManager.edit);
    //修改管理员数据
    router.post('/shop/admin/shopmanager/:id', controller.admin.shopManager.update);
    // 创建管理员界面
    router.get('/shop/admin/shopmanager/create', controller.admin.shopManager.create);
    //创建管理员提交数据
    router.post('/shop/admin/shopmanager', controller.admin.shopManager.save);
    //管理员列表页面
    router.get('/shop/admin/shopmanager', controller.admin.shopManager.index);
    
    
};

# 2. 控制器

app/controller/admin/shop_manager.js

'use strict';

const Controller = require('egg').Controller;

class Shop_managerController extends Controller {
  //创建管理员界面
  async create() {
    const { ctx,app } = this;
    // 渲染模版前先拿到所有角色
    let data = await ctx.service.shopManager.dropdown_rolelist();
    data.shift();//删除数组第一个元素
    // console.log('拿到所有角色', data);return;
    //渲染公共模版
    await ctx.renderTemplate({
        title: '创建管理员',//现在网页title,面包屑导航title,页面标题
        tempType: 'form', //模板类型:table表格模板 ,form表单模板
        form: {
          //提交地址
          action: "/shop/admin/shopmanager",
          //  字段
          fields: [
            {
                label: '请选择一个管理角色',
                type: 'dropdown', //下拉框
                name: 'role_id',
                default: JSON.stringify(data),
                placeholder: '请选择',
            },
            {
              label: '管理员账号',
              type: 'text',
              name: 'username',
              placeholder: '请输入管理员账号',
              // default:'默认值测试', //新增时候默认值,可选
            },
            {
              label: '管理员密码',
              type: 'password',
              name: 'password',
              placeholder: '请输入管理员密码',
            },
            {
              label: '管理员头像',
              type: 'file',
              name: 'avatar',
            },
          ],
        },
        //新增成功之后跳转到哪个页面
        successUrl: '/shop/admin/shopmanager',
      });
  }
  //创建管理员提交数据
  async save() {
    //一般处理流程
    //1.参数验证
    this.ctx.validate({
      username: {
        type: 'string',  //参数类型
        required: true, //是否必须
        // defValue: '', 
        desc: '管理员账号' //字段含义
      },
      password: {
        type: 'string',
        required: true,
        // defValue: '', 
        desc: '管理员密码'
      },
      avatar: {
        type: 'string',
        required: false,
        defValue: 'https://thinkphp-all.oss-cn-hangzhou.aliyuncs.com/public/67b3001b2aedd.png', 
        desc: '管理员头像'
      },
      role_id: {
        type: 'int',
        required: true,
        // defValue: '',
        desc: '角色id'
      },
    });
    //先判断一下管理员账号是否存在,不存在在写入数据库
    //2.写入数据库
    //3.成功之后给页面反馈

    //  let params = this.ctx.request.body; 
    //  console.log(params); //{ username: 'admin', password: '123456', avatar: '' }
    let { username, password, avatar, role_id } = this.ctx.request.body;
    // let manager = await this.app.model.Manager.findOne({where: {username}});
    if (await this.app.model.ShopManager.findOne({ where: { username } })) {
       return this.ctx.apiFail('该管理员已经存在');
    }
    //否则不存在则写入数据库
    const res = await this.app.model.ShopManager.create({
      username,
      password,
      avatar,
      role_id
    });
    this.ctx.apiSuccess(true);
  }
  //管理员列表页面
  async index() {}
  //修改管理员界面
  async edit() {}
  //修改管理员数据
  async update() {}
  //修改管理员状态功能
  async updateStatus() {}
  //删除管理员功能
  async delete() {}
}

module.exports = Shop_managerController;

# 3. 服务

app/service/shop_manager.js

'use strict';

const Service = require('egg').Service;

class Shop_managerService extends Service {
    //下拉框获取所有角色
    async dropdown_rolelist() {
        let data = await this.rolelist();
        return data;
    }
   //获取所有角色-去掉超级管理员
   async rolelist() {
    const { ctx, app } = this;
    // 渲染模版前先拿到所有角色
    let data = await ctx.app.model.Role.findAll({
        where: {
            status: 1
        },
        attributes: ['id', 'name', 'desc', 'status'],
    });
    data = JSON.parse(JSON.stringify(data));
    // data.unshift({ id: 0, name: '一级分类', pid: 0, status: 1, value: 0 });
    data = data.map(item => {
        return {
            ...item,
            value: item.id
        }
    });
    // console.log('所有角色', data);
    return data;
}
}

module.exports = Shop_managerService;

# 二、商城管理员列表

字体图标,查看:https://fontawesome.dashgame.com/ (opens new window)

# 1. 后台左侧菜单

data/root.json

[
  ...
  {"id":17,"pid":0, "name": "商城", "icon": "fa fa-shopping-cart", "url": "" },
  {"id":18,"pid":17, "name": "角色管理", "icon": "fa fa-users", "url": "/shop/admin/role" },
  {"id":19,"pid":17, "name": "管理员管理", "icon": "fa fa-user-circle-o", "url": "/shop/admin/shopmanager" }
]

# 2. 控制器

app/controller/admin/shop_manager.js

  // 管理员列表页面
  async index() {
    const { ctx, app } = this;
    //分页:可以提炼成一个公共方法page(模型名称,where条件,其他参数options)
    let data = await ctx.page('ShopManager',{},{
        include: [
            { 
                model: app.model.Role, 
                attrbutes: ['id', 'name','desc','status'],
            }
        ],
    });

    // console.log('拿到管理员列表', JSON.parse(JSON.stringify(data)));
    
    //渲染公共模版
    await ctx.renderTemplate({
      title: '管理员列表',//现在网页title,面包屑导航title,页面标题
      data,
      tempType: 'table', //模板类型:table表格模板 ,form表单模板
      table: {
        //表格上方按钮,没有不要填buttons
        buttons: [
          {
            url: '/shop/admin/shopmanager/create',//新增路径
            desc: '新增管理员',//新增 //按钮名称
            // icon: 'fa fa-plus fa-lg',//按钮图标
          }
        ],
        //表头
        columns: [
          {
            title: '管理员账号',
            // key: 'username',
            render(item) {
              const type = item.super == 1 ? '超级管理员' : '普通管理员';
              return `
                <h2 class="table-avatar">
                  <a href="#" class="avatar avatar-sm mr-2">
                      <img
                          class="avatar-img rounded-circle"
                          src="${item.avatar}"
                          alt="User Image"></a>
                      <a href="#"> ${item.username}
                      <span>${item.role.name}</span></a>
                </h2>
                `;
            },
          },
          {
            title: '可用状态',
            key: 'status',
            width: 200,//可选
            class: 'text-center',//可选
            hidekeyData: true,//是否隐藏key对应的数据
            render(item) {
              console.log('可用状态里面每个item', item);
              let arr = [
                { value: 1, name: '启用' },
                { value: 0, name: '禁用' },
              ];
              let str = `<div class="btn-group btn-group-${item.id}">`;
              for (let i = 0; i < arr.length; i++) {
                str += `<button type="button" class="btn btn-light" data="${item.status}"
                      value="${arr[i].value}"
                      @click="changeBtnStatus('status','btn-group-${item.id}',${arr[i].value},${i},${item.id},'/shop/admin/shopmanager','ShopManager')">${arr[i].name}</button>`;
              }
              str += `</div>`;
              return str;
            }
          },
          {
            title: '操作',
            class: 'text-right',//可选
            action: {
              //修改
              edit: function (id) {
                return `/shop/admin/shopmanager/edit/${id}`;
              },
              //删除
              delete: function (id) {
                return `/shop/admin/shopmanager/${id}/delete`;
              }
            }
          },
        ],
      },
    });
  }

# 3. 模型

app/model/shop_manager.js

...
module.exports = app => {
  ...
  // 模型关联关系
  ShopManager.associate = function (models) {
      // 关联角色 反向一对多(一个角色可以有多个管理员,角色对于管理员是一对多的关系,反过来管理员属于角色belongsTo,就是反向一对多)
      ShopManager.belongsTo(app.model.Role);
  }

  return ShopManager;
}

# 三、商城管理员修改、删除、修改可用状态

app/controller/admin/shop_manager.js

# 1. 控制器代码

// 修改管理员界面
  async edit() {

    const { ctx, app } = this;
    const id = ctx.params.id;
    let data = await app.model.ShopManager.findOne({
      where: {
        id
      }
    });
    if (!data) {
      return ctx.apiFail('该管理员不存在');
    }
    data = JSON.parse(JSON.stringify(data));
    delete data.password;
    // console.log(data);

    // 渲染模版前先拿到所有角色
    let role = await ctx.service.shopManager.dropdown_Rolelist();
    // console.log('拿到所有角色', role); return;
    role.shift();//删除数组第一个元素
    
    //渲染公共模版
    await ctx.renderTemplate({
      id,
      title: '修改管理员',//现在网页title,面包屑导航title,页面标题
      tempType: 'form', //模板类型:table表格模板 ,form表单模板
      form: {
        //修改管理员提交地址
        action: '/shop/admin/shopmanager/' + id,
        //  字段
        fields: [
          {
            label: '请选择一个角色',
            type: 'dropdown', //下拉框
            name: 'role_id',
            default: JSON.stringify(role),
            placeholder: '请选择',
          },
          {
            label: '管理员账号',
            type: 'text',
            name: 'username',
            placeholder: '请输入管理员账号',
            // default:'默认值测试', //新增时候默认值,可选
          },
          {
            label: '管理员密码',
            type: 'password',
            name: 'password',
            placeholder: '请输入管理员密码',
          },
          {
            label: '管理员头像',
            type: 'file',
            name: 'avatar',
          },
        ],
        //修改内容默认值
        data,
      },
      //修改成功之后跳转到哪个页面
      successUrl: '/shop/admin/shopmanager',
    });

  }
  // 修改管理员数据功能
  async update() {
    const { ctx, app } = this;
    //1.参数验证
    this.ctx.validate({
      id: {
        type: 'int',
        required: true,
        desc: '管理员id'
      },
      username: {
        type: 'string',  //参数类型
        required: true, //是否必须
        // defValue: '', 
        desc: '管理员账号' //字段含义
      },
      password: {
        type: 'string',
        required: false,
        // defValue: '', 
        desc: '管理员密码'
      },
      avatar: {
        type: 'string',
        required: false,
        // defValue: 'https://thinkphp-all.oss-cn-hangzhou.aliyuncs.com/public/67b3001b2aedd.png',
        desc: '管理员头像'
      },
      role_id: {
        type: 'string',
        required: false,
        // defValue: '',
        desc: '角色id'
      },
    });

    // 参数
    const id = ctx.params.id;
    const { username, password, avatar, role_id } = ctx.request.body;
    // 先看一下管理员是否存在
    let data = await app.model.ShopManager.findOne({ where: { id } });
    if (!data) {
      return ctx.apiFail('该管理员记录不存在');
    }
    // if (data && data.super == 1) {
    //   return ctx.apiFail('超级管理员不能修改');
    // }
    //存在,由于管理员的账号具有唯一性,你不能修改账号的时候,修改成存在的账号
    const Op = this.app.Sequelize.Op;//拿Op,固定写法
    if (await app.model.ShopManager.findOne({
      where: {
        username,
        id: {
          [Op.ne]: id
        }
      }
    })) {
      // return ctx.pageFail('该管理员账号已经存在,不能修改成该管理员账号', 404);
      return ctx.apiFail('该管理员账号已经存在,不能修改成该管理员账号');
    }
    // 修改数据
    if(data.super == 1){
      let manager = await app.model.Manager.findOne({
        where:{
           username:data.username
        }
      });
      if(manager && manager.super == 1){
         manager.username = username;
         if(password){
           manager.password = password;
         }
         await manager.save();
      }
    }
    data.username = username;
    if (password) {
      data.password = password;
    }
    if (avatar) {
      data.avatar = avatar;
    }

    if (role_id && data.super != 1) {
      data.role_id = role_id;
    }

    await data.save();
    // 给一个反馈
    ctx.apiSuccess('修改成功');
  }
  // 修改管理员状态功能
  async updateStatus() {
    const { ctx, app } = this;
    //1.参数验证
    this.ctx.validate({
      id: {
        type: 'int',
        required: true,
        desc: '管理员id'
      },
      status: {
        type: 'int',  //参数类型
        required: true, //是否必须
        // defValue: '', 
        desc: '角色状态', //字段含义
        range: {
          in: [0, 1]
        },
      },
    });
    // 参数
    const id = ctx.params.id;
    const { status } = ctx.request.body;
    // 是否存在
    const data = await app.model.ShopManager.findOne({ where: { id } });
    if (!data) {
      return ctx.apiFail('管理员不存在');
    }
    // 修改数据
    data.status = status;

    await data.save();
    // 给一个反馈
    ctx.apiSuccess('修改管理员状态成功');
  }
  // 删除管理员功能
  async delete() {
    const { ctx, app } = this;
    const id = ctx.params.id;
    const data = await app.model.ShopManager.findOne({ where: { id } });
    if (data && data.super == 1) {
      return ctx.apiFail('超级管理员不能删除');
    }
    await app.model.ShopManager.destroy({
      where: {
        id
      }
    });
    //提示
    ctx.toast('删除成功', 'success');
    //跳转
    ctx.redirect('/shop/admin/shopmanager');
  }

# 2.后台管理员manager在修改超级管理员的时候做一下调整,同时修改一下商城超级管理员账号

app/controller/admin/manager.js

//修改管理员数据功能
  async update() {

    if (this.ctx.session.auth.super == 0 && this.ctx.params.id != this.ctx.session.auth.id) {
      return this.ctx.apiFail('您无权操作此项功能');
    }

    const { ctx, app } = this;
    //1.参数验证
    this.ctx.validate({
      id: {
        type: 'int',
        required: true,
        desc: '管理员id'
      },
      username: {
        type: 'string',  //参数类型
        required: true, //是否必须
        // defValue: '', 
        desc: '管理员账号' //字段含义
      },
      password: {
        type: 'string',
        // required: true,
        // defValue: '', 
        desc: '管理员密码'
      },
      avatar: {
        type: 'string',
        required: false,
        // defValue: '', 
        desc: '管理员头像'
      },
      auth: {
        type: 'string',
        required: false,
        // defValue: '',
        desc: '权限id'
      },
    });

    // 参数
    const id = ctx.params.id;
    const { username, password, avatar, auth } = ctx.request.body;
    // 先看一下管理员是否存在
    let manager = await app.model.Manager.findOne({ where: { id } });
    if (!manager) {
      return ctx.pageFail('该管理员记录不存在');
    }
    //存在,由于管理员的账号具有唯一性,你不能修改账号的时候,修改成存在的账号
    const Op = this.app.Sequelize.Op;//拿Op,固定写法
    if (await app.model.Manager.findOne({
      where: {
        username,
        id: {
          [Op.ne]: id
        }
      }
    })) {
      // return ctx.pageFail('该管理员账号已经存在,不能修改成该管理员账号', 404);
      return ctx.apiFail('该管理员账号已经存在,不能修改成该管理员账号');
    }
    // 修改数据
    if(manager.super == 1){
      let shopmanager = await app.model.ShopManager.findOne({
        where:{
           username:manager.username
        }
      });
      if(shopmanager && shopmanager.super == 1){
         shopmanager.username = username;
         if(password){
           shopmanager.password = password;
         }
         await shopmanager.save();
      }
    }
    manager.username = username;
    if (password) {
      manager.password = password;
    }
    if (avatar) {
      manager.avatar = avatar;
    }

    if (auth) {
      manager.auth = auth;
    }

    await manager.save();
    // 给一个反馈
    ctx.apiSuccess('修改成功');
  }

# 四、商城管理员登录和退出功能(API接口)

# 1. 路由

app/router/admin/shop.js

module.exports = app => {
    const { router, controller } = app;
    ...
    
    //商城管理员登录
    router.post('/shop/login', controller.admin.shopManager.login);
    //商城管理员退出
    router.post('/shop/logout', controller.admin.shopManager.logout);
};

# 2. 控制器

app/controller/admin/shop_manager.js

//商城管理员登录
  async login() {
    const { ctx, app } = this;
    //1.参数验证
    this.ctx.validate({
      username: {
        type: 'string',  //参数类型
        required: true, //是否必须
        // defValue: '', 
        desc: '管理员账号' //字段含义
      },
      password: {
        type: 'string',
        required: true,
        // defValue: '', 
        desc: '管理员密码'
      },
    });
    // 拿参数
    const { username, password } = ctx.request.body;
    // 判断商城管理员是否存在
    let shop_manager = await app.model.ShopManager.findOne({
      where: {
        username:username,
        status:1,
      },
    });
    if (!shop_manager) {
      ctx.throw(400, '商城管理员不存在或者被禁用');
    }
    // 验证用户密码
    const isMatch = bcrypt.compareSync(password,shop_manager.password);
    if(!isMatch){
       ctx.throw(400, '商城管理员密码错误');
    }
    // 生成唯一token
    shop_manager =  JSON.parse(JSON.stringify(shop_manager));
    // this.app.jwt.sign(shop_manager, this.app.config.jwt.secret);
    let token = ctx.getToken(shop_manager);
    shop_manager.token = token;
    console.log('商城管理员', shop_manager);
    // 加入到缓存中(保证用户提交验证的token来自我们服务器生成的)
    let shop_manager_token =  await this.service.cache.set('shop_manager_' + shop_manager.id, 
                                token, 60 * 60 * 24 * 365 * 100);
    if(!shop_manager_token){
       ctx.throw(400, '登录失败');
    }
    delete shop_manager.password;
    return ctx.apiSuccess(shop_manager);
  }

  //商城管理员退出
  async logout() {
    const { ctx,app } = this;
    let shop_manager = ctx.auth_shop_manager;
    console.log('商城管理员退出', shop_manager);
    let shop_manager_token =  await ctx.service.cache.get('shop_manager_' + shop_manager.id);
    // console.log('商城超级管理员缓存信息', shop_manager_token);
    if(shop_manager_token){
        //清除redis
        await ctx.service.cache.remove('shop_manager_' + shop_manager.id);
    }
    return ctx.apiSuccess(true);
  }

# 3. 中间件配置

config/config.default.js

  ...
  // 对中间件adminAuth进一步配置
  config.adminAuth = {
    ignore: [
      ...
      "/shop/login",
      "/shop/logout",
    ],
  };
  ...
  // 对中间件shopManagerAuth进一步配置
  config.shopManagerAuth = {
    match:[
      '/shop/admin',//只要包含/shop/admin路由的任何页面都生效
      "/shop/logout",
    ],
  };
  ...

# 五、商城管理员列表(API接口)

# 1. 路由

app/router/admin/shop.js

module.exports = app => {
    const { router, controller } = app;
    ...
    //管理员列表页面
    router.get('/shop/admin/shopmanager/:page', controller.admin.shopManager.indexlist);
    router.get('/shop/admin/shopmanager', controller.admin.shopManager.index);
    
    ...
};

# 2. 控制器

app/controller/admin/shop_manager.js

//管理员列表(API功能)
  async indexlist() {
    const { ctx, app } = this;
    //参数
    let keyword = ctx.query.keyword || '';
    let limit = parseInt(ctx.query.limit) || 10;
    //组织查询条件
    let where = {
      status: 1
    };
    const { Op } = app.Sequelize; // 拿到Op对象
    if (keyword) {
      where.username = {
        [Op.like]: '%' + keyword + '%',
      }
    }
    //管理员表
    let shopmanager = await app.model.ShopManager.findAndCountAll({
      /*
      where:{
        status:1,
        username:{
            [Op.like]: '%' + keyword + '%',
        },
      },
      */
      where: where,
      attributes: {
        exclude: ['password']
      },
      order: [
        ['id', 'desc']
      ],
      limit,
      include: [
        {
          model: app.model.Role,
          // attributes: ['id', 'name', 'desc', 'status'],
        }
      ],
    });
    // ctx.body = shopmanager;
    // 获取角色
    let role = await app.model.Role.findAll({
      where: {
        status: 1,
      },
      attributes: ['id', 'name']
    });
    //返回数据
    ctx.apiSuccess({
      list: shopmanager.rows,
      totalCount: shopmanager.count,
      role
    });
  }
更新时间: 2025年4月10日星期四晚上8点30分