本文编写于 600 天前,最后修改于 600 天前,其中某些信息可能已经过时。

Fastadmin踩坑日记-2.魔改表格

经过日夜的查找,终于可以慢慢的上手这个了,踩了几个坑,记录一下

坑1-前台无法显示emoji字符

最早的几个emoji是2个字节,在utf8默认的双字节编码中是可以正常显示的,比如那个红色感叹号❕,到后来emoji逐步成为了4个字符,原本的utf8_gerneral_ci字符集就没法显示了,必须手动设置

显示emoji需要几个条件

  1. 数据库字符集为utf8mb4,mysql里面是utf8mb4_gerneral_ci

  2. 字段字符集为ut8mb4,mysql里面是utf8mb4_gerneral_ci

  3. 后台连接数据库设置的时候也要选择ut8mb4,也就是在db.php类似这个文件里

    例如

之前一直没有成功,原来是条件3没有满足,导致前台一直看到的是而不是emoji如????(左边是一个emoji,但显示不出来,原来typecho也是要改的。。。),改了3之后终于成功了

坑2-Bootstrap-table显示图像或自定义渲染显示

这个就比如说,数据库里有一个gender字段存储用户的性别,0是男,1是女

渲染到BT表格(Bootstrap-table,下同)上的时候想要直接显示男或女,

或者你想自定义这个表格内容的css,就需要用到BT的formatter渲染器

首先需要完成踩坑日记1的curd生成表格的操作,然后到前台页面的对应js中

位置为public/assets/js/[backend|frontend]这个根据你的这个bt表格在前台后台决定/<插件名>/<表>.js

找到对应的字段如

加入一个formatter即可,如果是本页的可以放在页面下面的api当中,然后字段就可以对应的渲染了,然后注意,坑来了

本页的控制器是Controller,而非Table,所以要访问这个渲染器,一定是Controller.api.formatter.xxxxx,不是Table.api.formatter.xxxx

比较常用的formatter有

Table.api.formatter.icon //渲染成图标按钮

Table.api.formatter.image //渲染成单张图片

Table.api.formatter.images //渲染成多张图片

Table.api.formatter.content //内容自动截取

Table.api.formatter.status //渲染成状态

Table.api.formatter.normal //渲染成label

Table.api.formatter.toggle //渲染成开关

Table.api.formatter.url //渲染成文本框链接

Table.api.formatter.search //渲染成搜索链接

Table.api.formatter.addtabs //渲染成打开新选项卡链接

Table.api.formatter.dialog //渲染成弹窗链接

Table.api.formatter.flag //渲染成标志

Table.api.formatter.label //渲染成标志

Table.api.formatter.datetime //渲染成日期时间

Table.api.formatter.operate //渲染成操作栏按钮

Table.api.formatter.buttons //渲染成按钮组

最终效果如下

坑3-点击按钮无法设置为弹窗

还是在位置为public/assets/js/[backend|frontend]这个根据你的这个bt表格在前台后台决定/<插件名>/<表>.js这里的classname写成btn-dialog即可设置为弹窗,url即为弹窗对应的控制器

在控制器里面对应输出相应的页面即可,可以参照下图,下图输出的是渲染后的view/index.html

坑4-弹窗内的更新事件完成后不会自己关闭

我这是非主流方法,因为什么callback方法都试过了,没反应,无法让弹窗数据与父级窗口交互,自己摸索出一套办法

要点1. 表单必须是ajax

Ajax表单提交才会触发右上角的小窗口提示更新成功或失败,如果是form里的submit则会全局页面刷新显示成功

要点2.学会使用parent来控制父级窗口

在弹窗内,parent表示的就是父级窗口,这样就可以访问进行相关的操作

在弹窗内可以执行下面的js代码实现数据交互

parent.Toastr.success(data)         //父级成功弹窗
parent.Toastr.error(data)           //父级失败弹窗
parent.Layer.closeAll()         //父级关闭所有弹窗,也就是弹窗可以关闭自己

这是更新与微信订阅推送的ajax方法,更新推送完和父级进行数据交互的案例

//弹窗内执行的js文件
<script>
    function submit() {
      var url = 'mypchelper/orders/update'
      var formData = $('#myForm').serialize()
      //序列化为 a=1&b=2这样的序列
      Fast.api.ajax({
        url: url,
        type: 'POST',
        data: formData,
        success: function(data) {
          parent.Toastr.success(data.msg)
          switch (data.data.errcode) {
            case 0:
              parent.Toastr.success('消息已经推送至用户手机')
              break
            case 40003:
              parent.Toastr.error('推送错误:touser字段openid为空或者不正确')
              parent.Toastr.error('错误信息:' + data.data.errmsg)
              break
            case 40037:
              parent.Toastr.error('推送错误:订阅模板id为空不正确')
              parent.Toastr.error('错误信息:' + data.data.errmsg)
              break
            case 43101:
              parent.Toastr.error(
                '推送错误:用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系'
              )
              parent.Toastr.error('错误信息:' + data.data.errmsg)
              break
            case 47003:
              parent.Toastr.error(
                '推送错误:模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错'
              )
              parent.Toastr.error('错误信息:' + data.data.errmsg)
              break
            case 41030:
              parent.Toastr.error(
                '推送错误:page路径不正确,需要保证在现网版本小程序中存在,与app.json保持一致'
              )
              parent.Toastr.error('错误信息:' + data.data.errmsg)
              break
          }
          parent.Layer.closeAll()
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
          Toastr.error('网络错误,请稍后重试!')
          console.log('提交请求的错误信息:' + errorThrown + XMLHttpRequest)
        }
      })
    }
  </script>

要点3. Controller内isAjax()函数对应处理ajax的数据请求

if ($this->request->isAjax()) { //form提交ajax请求
        $result = false;
        $params = $this->request->post("r/a"); //前端页面内form的input name为r[对应字段名]
    if ($params) {
      $id = $params['id'];    //获取数据赋值 
    }
  //然后sql方法处理即可
  ............sql process.............
  if ($result !== false) {
     $res = $this->temMsg($openid, $problem, $orderreply);   //我这里封装了一个微信推送的函数
     $this->success('更新成功', null, $res);    //把res数据传给前端
     //$this -> redirect('mypchelper/Orders/index', 5, '页面跳转中...'); //这个别试,会在弹窗内跳转
  } else {
    $this->error(__('No rows were updated'));
  }
}

相关可详见自定义弹窗页面,Ajax提交后,当前弹窗不关闭?

还有就是其他人的主流办法,分享自定义按钮btn-dialog在关闭弹窗后刷新父窗体 刷新 关闭 按钮 callback 自定义

坑5 Fastadmin Bootstrap-table表格访问多表数据(联合查询多表)(thinkphp关联模型)

有时候我们会遇到这种情况

数据库一张表要和别的表联动输出数据,这个时候就需要在取数据的时候下功夫

官方给了个例子,在controller/example/relationmodel.php内,可以看着改,不过我们自己还是来写一遍

我们举个例子

举个例子

数据库

现在有orders以及users

orders表内有id_users字段和users表内的id字段是一个玩意儿

也就是orders.id_users字段是一个外键

输出的时候我们要吧orders和users的信息一起输出,这个在sql里面非常容易实现,但现在是要用fastadmin的php方法实现,所以我们先看控制器

控制器初始化

在controller/demo/orders.php内主动构造index方法,而非继承使用backend本身封装的方法

一定要有$this->relationSearch = true;表明是多表联合查询,然后载入需要的附属表$this->searchFields = "users"; //需要的表

这里容易搞混,因为users是表而Users是数据层Model,我们需要使用with()函数来预载入附属表users对应的model,即

$total = $this->model  //载入本controller对应的model这里也就是orders的model
                ->with("Users") //预载入对应附属表的model
                ->where($where)
                ->order($sort, $order)
                ->count();
$list = $this->model
                ->with("Users")
                ->where($where)
                ->order(['processstatus' => 'ASC', $sort => $order])
                ->limit($offset, $limit)
                ->select();
$result = array("total" => $total, "rows" => $list);

然后到对应的model中,追加belongTo()函数,对应参数为belongsTo(表名,原表的id,外接表id,我也不知到,连接类型[左连接、右连接啥的]),那我们这里就是如下

public function users()
    {
        return $this->belongsTo('Users', 'id_users', 'id', [], 'LEFT')->setEagerlyType(0);
        //对应表
    }

然后还需要在前端js设置一下输出,位置为public/assets/js/[backend|frontend]这个根据你的这个bt表格在前台后台决定/<插件名>/<表>.js

这里field要设置成附表.字段如我这里的几个字段,后面的表头title如果想要原生输出,位置在application/[admin看是不是后台文件]/lang/<插件名/表.php>里面,修改即可

下面是加入了需要输出的附表字段后的部分字段

//orders.js中的bootstrap-table渲染字段
{ field: 'users.name', title: __('机主姓名') }, //对应model中的字段
{ field: 'users.mobile', title: __('机主手机') },
{
    field: 'users.QQ',
    title: __('机主QQ'),
    formatter: Controller.api.formatter.tolink
},
 { field: 'users.address', title: __('宿舍地址') },
 { field: 'pcmodel', title: __('Pcmodel') },
 { field: 'problem', title: __('Problem') },

之后前端就可以正常显示联合查询后的数据了

附上完整的controller/index

//在controller/demo/orders.php内
/**
     * 查看
     */
    public function index()
    {
        $this->relationSearch = true;
        $this->searchFields = "users"; //需要的表
        if ($this->request->isAjax()) {
            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
            $total = $this->model
                ->with("Users") //预载入对应的model
                ->where($where)
                ->order($sort, $order)
                ->count();
            $list = $this->model
                ->with("Users")
                ->where($where)
                ->order(['process' => 'ASC', $sort => $order])
                ->limit($offset, $limit)
                ->select();
            $result = array("total" => $total, "rows" => $list);

            return json($result);
        }
        return $this->view->fetch();
    }

参考:FastAdmin多表联查

坑6-换了用户就无法访问自定义的模块

记得在controller头部设置

protected $noNeedRight = ['*'];  //无需权限即可访问

当然官方也给了说明

基类解析

/**
 * 无需登录的方法,同时也就不需要鉴权了
 * @var array
 */
protected $noNeedLogin = [];  //里面放对应的模块如'add','edit'之类

/**
 * 无需鉴权的方法,但需要登录
 * @var array
 */
protected $noNeedRight = [];

/**
 * 权限Auth
 * @var Auth 
 */
protected $auth = null;

类似案例

权限管理为啥没有权限访问

官方参考资料

一张图解析FastAdmin中的弹出窗口的功能

一张图解析FastAdmin中的表格列表的功能

https://www.jianshu.com/p/7c90cddb583f