fastadmin/thinkPHP5.0的框架使用注意事项

0.主要链接

一张图解析表格

  1. 数据表规划一定要做好,省的做的时候很乱,一会要改一下,就特别麻烦

  2. 在线命令生成crud的时候一定不要填写自定义控制器名,要让他自己生成,否则后面你要修改东西还需要再找.默认的永远能知道在哪里

  3. 在线命令生成的时候,可以试着删除一下(不会成功),但是能看到他实际上修改了那些文件,你修改内容的时候就知道应该去哪个文件里面去修改了

  4. 数据表名称,建议都用fa开头然后一级菜单,然后表的语义名称,比如 fa_dataproduct,后面就完全连接起来的小写,这样自动生成也能生成成功,要不然老是会有问题

  5. crud的时候关联外键只关联一对一的值,其他值如果显示数据量太大,同时也没法实现

  6. thinkphp 外键关联,belongsTo 或者其他的,使用with查询时,要注意查询时传入的参数与model里面的方法名字一致
    `class ConversationModel extends Model
    {

    protected $table = ‘fa_conversation’;

    public function toUser()
    {
    return $this->belongsTo(‘UserModel’, ‘to_id’, ‘id’)->field(‘id,mobile,nickname,avatar’);
    }

}

/**
 *
 *查询当前用户所有的聊天会话列表
 */
public function list()
{

    $this->success('查询成功',
        ConversationModel::with('toUser')->where('from_id', $this->auth->getUser()->id)
            ->order('updatetime desc')
            ->limit(200)->select());
}

`
6. 专家和厂家都是用户的不同组,另外一个表bind里面同时有expert_id和enterprise_id,这种复杂显示

数据查询的时候我们要把专家和厂家都关联到,但是crud 只能关联一次user表,所以需要我们另外添加一个关联查询,with里面多加了一个expert关联

 public function index()
    {
        //当前是否为关联查询
        $this->relationSearch = true;
        //设置过滤方法
        $this->request->filter(['strip_tags', 'trim']);
        if ($this->request->isAjax()) {
            //如果发送的来源是Selectpage,则转发到Selectpage
            if ($this->request->request('keyField')) {
                return $this->selectpage();
            }
            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
             $list = $this->model
                ->with(['user','expert'])
                ->where($where)
                ->order($sort, $order)
                ->paginate($limit);

            foreach ($list as $row) {

                $row->getRelation('user')->visible(['mobile', 'nickname']);
                $row->getRelation('expert')->visible(['mobile', 'nickname']);
            }

            $result = array("total" => $list->total(), "rows" => $list->items());

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

这个关联要求在model里面添加一个方法,如下,user是自动生成的,expert我们也照着写一个

  public function user()
    {
        return $this->belongsTo('User', 'enterprise_id', 'id', [], 'LEFT')->setEagerlyType(0);
    }

    public function expert()
    {
        return $this->belongsTo('User', 'expert_id', 'id', [], 'LEFT')->setEagerlyType(0);
    }

到此,查出的数据已经满足了
然后列表的字段如下:field里面添加上我们自己跌的expert.mobil和expert.nickname,当然在lang文件夹下面把多语言适配的部分加上去

  // 初始化表格
            table.bootstrapTable({
                url: $.fn.bootstrapTable.defaults.extend.index_url,
                pk: 'id',
                sortName: 'id',
                columns: [
                    [
                        {checkbox: true},
                        {field: 'id', title: __('Id')},
                        {field: 'user.mobile', title: __('User.mobile'), operate: 'LIKE'},
                        {field: 'user.nickname', title: __('User.nickname'), operate: 'LIKE'},
                        {field: 'expert.mobile', title: __('Expert.mobile')},
                        {field: 'expert.nickname', title: __('Expert.nickname')},
                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                    ]
                ]
            });

这样列表显示就没有问题了

然后需要让添加和编辑页面也能用,先说添加,首先改装一下添加页面,然后再controller里面自己定义一个方法,给这个页面里面的用户选择弹出框和专家选择弹出框,返回专门的数据即可

add.html中改装如下:

<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">

    <div class="form-group">
        <label class="control-label col-xs-12 col-sm-2">{:__('Enterprise_id')}:</label>
        <div class="col-xs-12 col-sm-8">
            <input id="c-enterprise_id" data-rule="required" data-source="bind/enterprises" data-show-field="name" class="form-control selectpage" name="row[enterprise_id]" type="text" value="">
        </div>
    </div>
    <div class="form-group">
        <label class="control-label col-xs-12 col-sm-2">{:__('Expert_id')}:</label>
        <div class="col-xs-12 col-sm-8">
            <input id="c-expert_id" data-rule="required" data-source="bind/experts" data-show-field="name" class="form-control selectpage" name="row[expert_id]" type="text" value="">
        </div>
    </div>
    <div class="form-group layer-footer">
        <label class="control-label col-xs-12 col-sm-2"></label>
        <div class="col-xs-12 col-sm-8">
            <button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
        </div>
    </div>
</form>

这个时候发现add页面中获取专家和企业的列表时,他是在data-sourse这个字段中指定请求的接口的.所以我们改成自己的接口,然后controller里面添加一个方法,对应这个接口,返回我们需要的数据,代码如下,这样多久得到了我们想要的数据了,注意一下上面的data-show-field指定的字段,我们的接口中必须存在.这里是用concat连接出来的一个值


    function experts(int $pageNumber, int $pageSize,$q_word=[]): Json
    {
        $list = User::field(['id','concat(nickname,"|",mobile)'=>'name'])->where('nickname|mobile','like','%'.$q_word[0].'%')->where('group_id', 2)->paginate($pageSize,false,['page'=>$pageNumber]);
        $result = array("total" => $list->total(), "rows" => $list->items());
        return \json($result);
    }
    function enterprises(int $pageNumber, int $pageSize,$q_word=[]): Json
    {
        $list = User::field(['id','concat(nickname,"|",mobile)'=>'name'])->where('nickname|mobile','like','%'.$q_word[0].'%')->where('group_id',4 )->paginate($pageSize,false,['page'=>$pageNumber]);
        $result = array("total" => $list->total(), "rows" => $list->items());
        return \json($result);
    }

同理,再编辑页面也用同样的方法改装一下,打开页面请求的时候报错,不要慌,看看他请求的接口,我们不存在所以需要加上,同时他的参数我们可以看到主要的是searchKey searchValue KeyField keyValue,而且其实这两个请求都是请求的同一个数据表user,所以我们实际只需要定义一个接口就可以满足他的请求了.
在这里插入图片描述

在bind里面我们定一个一个方法如下发:当然在edit.html里面data-sourse绑定的接口也换成我们定义的bind/user

    function user(int $keyValue): Json
    {
        $row = User::field(['id','concat(nickname,"|",mobile)'=>'name'])->find($keyValue);
        return \json($row);
    }

但是这个返回是否正确?不一定,我们不知道前端需要什么数据,写了先试试,不合适再改
在这里插入图片描述
对比官方的返回数据为这样的,我们也改装成这样的给他返回就可以了,我的修改过的接口如下

  function user(int $keyValue): array
    {
        $list = User::field(['id', 'concat(nickname,"|",mobile)' => 'name'])->where('id', $keyValue)->limit(1)->select();
        return ['list' => $list, 'total' => 1];
    }

,结果如下,完全可以了在这里插入图片描述

1.hasOne和belongsTo

这两种方法都可以应用在一对一关联上,但是他们也是有区别的:

hasOne(‘关联模型’,‘外键’,‘主键’);

belongsTo(‘关联模型’,‘外键’,‘关联主键’);

最主要的区别就在于:谁是主,谁是从:

比如有A和B两张表

A表字段:id name B_id

B表字段:id name

这样A表有B表的外键字段B_id,当在A表所对应的模型就应该用belongsTo去关联B表,A表就是从属于B。反之B表则用hasOne ,B为主,里面有一个A

2.Backend比较好用的几个东西,非常爽

    /**
     * 快速搜索时执行查找的字段
     * 不同的子类里面要搜索不同的数据,就可以修改这个值,比如改为id,name,mobile就可以同时搜索这三个
     */
    protected $searchFields = 'id';

    /**
     * 是否是关联查询
     */
    protected $relationSearch = false;

    /**
     * 是否开启数据限制
     * 支持auth/personal
     * 表示按权限判断/仅限个人
     * 默认为禁用,若启用请务必保证表中存在admin_id字段
     */
    protected $dataLimit = true;

    /**
     * 数据限制字段
     * my 数据限制字段为jigou_id这样开启之后所有的用户就只能看到自己机构的数据,而不是全部数据,加上这个之后所有的子类都会有这个规则,这就很牛了.当然如果你不想让某一个表里面用这个规则,可以重新这个字段,甚至把$dataLimit=false关闭这个功能
     */
    protected $dataLimitField = 'jigou_id';
    

3.table里面的列表查询的时候filter筛选某一类

queryParams是请求之前的回调,可在这修改请求的参数,这样每一个请求都会加上这个参数,当然其他参数也可以在这里加上

   table.bootstrapTable({
                url: $.fn.bootstrapTable.defaults.extend.index_url,
                pk: 'id',
                sortName: 'user.id',
                search:false,
                queryParams: (params) => {
                    console.log(params);//打印出来看到底有些啥参数呢
                    params.filter = JSON.parse(params.filter);
                    params.filter.group_id=4;
                    params.filter=JSON.stringify(params.filter);
                    return params;
                },
                ...
                }

4. 自定义selectpage请求的参数

给selectpage的input加上一个特殊的data-params=‘{“custom[group_id]”:“1”}’ ,这样selectpage请求数据列表的时候就会把这个过滤条件加上,只请求到对应的数据,而不是全部数据

 <div class="form-group">
        <label class="control-label col-xs-12 col-sm-2">{:__('User_id')}:</label>
        <div class="col-xs-12 col-sm-8">
            <input id="c-user_id" data-rule="required" data-source="user/user/index" data-field="nickname" data-params='{"custom[group_id]":"1"}' class="form-control selectpage" name="row[user_id]" type="text" value="">
        </div>
    </div>

5.时间最好都用bigint否则需要配置如下

在对应的model里面添加

protected $dateFormat = 'Y-m-d H:i:s';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';

6. 菜单一键生成做了啥,权限怎么控制的?

菜单一键生成是根据controller里面的public方法生成的auth_rule(记录就保存在这个表里面),每一个public方法都对应一个rule,然后这个方法的权限就可以给对应的用户组配置,只用经过这个权限检查的方法才能被对应的用户调用,否则权限不足
其中方法的注释内容就是权限里的名称,当然可以自己去auth_rule表里面修改名称

同时如果自己手写的文件,也可以通过菜单生成来实现权限控制

7.FormBuilder表单生成器

用php代码可直接渲染form表单,说明如下
一张图解析FastAdmin中的FormBuilder表单生成器
所以,我们可以在接口中先得到自己想要渲染的数据,然后$this->assign一下,绑定到view中,然后再使用php的Form::xxx方法去渲染即可

8.自定义接口关联view页面和js文件

自定义一个接口比如 /bind/my

一般来说bind就对应一张表,也是一个controller,里面必然有index,add,edit等方法(可能在父类中),这是框架提供的,但如果我们自己定义一个方法呢?

  • 首先自己定义一个my方法
  • 然后对应的view里面需要添加my.html
  • 第3.在asset/js里面的bind.js里面要添加一个my:function(){
    Form.api.bindevent($(“form[role=form]”));},这里面的bindevnet不一定要用form的,也可以是其他的,根据自己自定义页面元素而定 最后你需要一键生成菜单,让他读出你的方法,这样就可以使用权限控制了,参考 第6条菜单一键生成做了啥,权限怎么控制的?
  • 最后,如果my.html页面是一个form表单,那么就需要实现my方法的post请求,直接提交数据,省的再开发一个接口了

9. table中改变字段的值,button改变url值

在对应的js文件中,找到column,在这里面修改,例如

columns: [
                    [
                        {checkbox: true},
                        {field: 'id', title: __('Id')},
                        {field: 'admin_id', title: __('Admin_id')},
                        {field: 'clazz_id', title: __('Clazz_id')},
                        {field: 'createtime', title: __('Createtime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},
                        {field: 'hours', title: __('Hours'), operate:'BETWEEN'},
                        {field: 'jigou_id', title: __('Jigou_id')},
                        {field: 'note', title: __('Note'), operate: 'LIKE'},
                        {field: 'clazz.name', title: __('Clazz.name'), operate: 'LIKE'},
                        {field: 'admin.nickname', title: __('Admin.nickname'), operate: 'LIKE'},
                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate,
                            buttons: [
                                {
                                    name: 'mybutton',
                                    text:(row)=>row.student_ids,
                                    icon: 'fa fa-check',
                                    classname: 'btn btn-xs btn-success btn-dialog',
                                    url: 'attend/mybutton?id={id}',//这里可以直接嵌入字段内容
                                    visible: function (row, index) {
                                        return true;
                                    },
                                    extend: 'data-toggle="tooltip"'
                                }
                            ]
                        }
                    ]
                ]
            });

其中buttons下面的text就可以控制显示的文字,icon控制图标,等等,不一而足

10. icon 支持https://fontawesome.dashgame.com/ 所有图标

11. 表格怎么强制筛选

index_url: ‘course/index/type/1’ + location.search,这里面的type 1就会传入到请求的参数中,后端需要根据这个type进行返回对应的数据即可

  // 初始化表格参数配置
            Table.api.init({
                extend: {
                    index_url: 'course/index/type/1' + location.search,
                    add_url: 'course/add',
                    edit_url: 'course/edit',
                    del_url: 'course/del',
                    multi_url: 'course/multi',
                    import_url: 'course/import',
                    table: 'course',
                }
            });

更高级的方法,filter其实是最好的实现方式,filter的优点是只需要改前端,后端不需要改(如果是已有的列里面筛选,有其他高级条件的话还是需要改后端的)
示例如下:
queryParams里面我们获取到filter和op,强行改变他的值.这样就能保证获取到的数据一定是我们筛选过的了,这个方法只需要改前端,不需要改后端,实在是上上之选啊

  // 初始化表格
            table.bootstrapTable({
                url: $.fn.bootstrapTable.defaults.extend.index_url,
                pk: 'id',
                sortName: 'id',
                queryParams: (params) => {
                    let filter = JSON.parse(params.filter);
                    filter.des = '机器';
                    params.filter = JSON.stringify(filter);
                    let op = JSON.parse(params.op);
                    op.des = 'LIKE';
                    params.op = JSON.stringify(op);
                    return params;
                },

12.★★★★★关联信息查询最佳实现

一张表一个controller,一个js文件,多个view,controller里面多个方法,对应js里面的每个方法,同时对应各个view

如果主表格里面有多个其他记录,就增加按钮,点击打开查看关联列表,

关联列表里面可以关联增加其中关联的列表和关联增加页面需要controller和js要对应提供方法,view要对应增加

这样不管是多对多还是什么,都可以适配,自由度也比价高

fastadmin对于一些简单的关联查询还是可以的.稍微高级一点的就比较捉急了,而且如果引用了别的界面上的js或者view,当那个界面发生调整的时候就需要适配我这边,问题无群无尽.

可以有比较偷懒的办法就是多对多关联的时候可以生成中间表的crud.然后把他生成的内容复制到对应的地方就可以了.能省点功夫.ok,这确实是最佳实现方案,其他的方案都太辣鸡了

如果有特殊情况,比如关联查询的信息比较多.使用fastadmin或者thinkphp并不能解决的情况下,自己创建一个接口,单独实现,原有的index接口不要动人家的

综上所述:
1.一张表一个controller,一个js文件,有特殊情况需要多个页面的,自己另外定义方法,js文件里面添加controller和js里面添加,同时view和language也要有所修改(一般发生在关联表中,不同主表需要不同的操作)
2.特殊情况自己创建接口,不要占用系统提供的index,edit,add等接口
3.中间变生成的记录往往可以给多个主表使用,js的url中可以加上 /student_id/1这样中间表生成的table就会筛选出对应的学生的记录,而不是显示全部

13 多对多成功查询案列,不知道怎么指定中间表字段

class Student extends Model
{
    // 表名
    protected $table = 'student';
    // 定义和班级的多对多关联关系
    public function classes()
    {
        return $this->belongsToMany(Clazz::class, 'studentclazz', 'student_id', 'clazz_id');//这里可以用field指定student表的字段,但是不能指定中间表字段
    }
}

//查询时
 $students = Student::with(['classes' => function ($query) {
            $query->select('clazz.id', 'clazz.name', 'studentclazz.created_at');
        }])->select();
        return json_encode($students);
       //或者直接
       $students = Student::with(['classes'])->select();
       // 指定字段
       $students = Student::field('id')->with(['classes'])->select();
       //关联查询数量
        $students = Student::field('id')->withCount(['classes'=>'num'])->select();

14. 一对多成功案列,支持指定字段

class Clazz extends Model
{
    // 表名
    protected $table = 'clazz';
    public function teachers(){
        return $this->hasMany(Teacher::class)->field('id,clazz_id');
    }
}
//查询
$clazzs= Clazz::with(['teachers'])->select();

15.belongsto成功案例

class Teacher extends Model
{
    // 表名
    protected $table = 'fa_teacher';

    public function clazz()
    {
        return $this->belongsTo(Clazz::class)->field('id');//id必须有,否则没法关联查询
    }
}
//查询
$teachers= Teacher::with(['clazz'])->select();

16.★★★外键id要加索引(数据较多时)

尤其多对多,其实数据量一般会比较大.都应该加索引
一对多可能有时候数据不多,但大部分情况下也应该加索引

17.with查询要排除字段用feild

注意feild一定要放在with语句之前,最好放在最前面,否则有bug,一直报错

18.联合模型查询配置方法

controller里面一定要开启联合查询
js文件中配置field如下

{field: 'branch.name', title: __('分所名'), operate: 'LIKE %...%', placeholder: '请输入'},

19.controller里面关联搜索

关联搜索不开启的情况下where语句或者其他查询语句中很容易出现in where clause is ambiguous问题
这是因为默认不会关联搜索,如果多个表中有两个相同的字段就会混淆,只需要开启关联搜素即可,系统会自动加入表名

class Admin extends Backend
{
    protected $relationSearch=true;
    ......

20.view里面关联选择,然后搜索

在view里面添加一段代码

<script id="categorytpl" type="text/html">
    <div class="row">
        <div class="col-xs-12">
            <div class="form-inline" data-toggle="cxselect" data-selects="group,admin">
                <select class="group form-control" name="branch_id" data-url="auth/admin/cxselect?type=branch"></select>
                <!--这里data-query-name会自动获取branch_id所在的select的选中值,挺牛的哈哈-->
                <select class="admin form-control" name="department_id" data-url="auth/admin/cxselect?type=department"
                        data-query-name="branch_id"></select>
                <!--加入这两个input之后会在提交时自动带上operate选项,非常棒-->
                <input type="hidden" class="operate" data-name="branch_id" value="="/>
                <input type="hidden" class="operate" data-name="department_id" value="="/>
            </div>
        </div>
    </div>
</script>

在js里面的table配置里面添加一个field,并且指定template为view里面的id="categorytpl"的script内容

    field: 'department', title: "分所|部门", searchList: function (column) {
                                return Template('categorytpl', {});
                            }, formatter: function (value, row, index) {
                                return '无';
                            }, visible: false
                        },

21.关联查询完整示例

一对多,一对多的关联写入看thinkphp5.1的教程即可

//在主表里面,建立一对多关联关系.注意topet,管理的外键topet_id需要指定,如果正常关联这个外键可以自动生成,外键名称不是标明_id时,就需要特别指定才可以,这个表达方式需要注意
class Msg extends \think\Model
{
    protected $table = 'fa_msg';
    public function pet()
    {
        return $this->belongsTo(Pet::class,'pet_id');
    }
    public function topet()
    {
        return $this->belongsTo(Pet::class,'topet_id');
    }
}

//return $q->withField('id,avatar,name');过滤出需要的字段,用这个方法

 $list = \app\api\model\Msg::with(['pet' => function ($q) {
            return $q->withField('id,name,avatar');
        }])->with(['topet' => function ($q) {
            return $q->withField('id,name,avatar');
        }])
            ->where("(pet_id=$pet_id and topet_id=$topet_id) or (pet_id=$topet_id and topet_id=$pet_id)")
            ->page($page, 20)
            ->order('id desc')
            ->select();

注意,withField里面必须有id(关联的主键),否则无法完成关联查询

下面是多对多示例,第一个参数是对应的model,第二个参数是中间表名称(中间表不需要有model类),中间表要求有主表(post)的post_id,还要有次表的tag_id,这种标准命名方法,后面的参数就可以省略掉,


class Post extends \think\Model
{
    protected $table = 'fa_post';

    public function tags()
    {
        return $this->belongsToMany(Tag::class, 'posttag');
    }
}
//多对多查询时,with直接查询即可,不需要特殊处理了
$list = \app\api\model\Post::
                with(['tags'])
                ->order('likenum desc')
                ->page($page, 20)
                ->select();

关联查询时,子表中条件查询,直接在子查询里面增加条件

//user的model里面有如下多对多关联
 public function offices()
    {
        return $this->belongsToMany(Office::class,'useroffice');
    }
//查询时,我们对中间表的条件筛选放在with预载入的子查询里面即可
 \app\api\model\User::with(
            ['offices' => function ($q) use ($office_id) {
                return $q->where('office_id', $office_id ? '=' : '>', $office_id);
            }])
            ->select();

22.对于时间字段要用bigint,并且createtime,updatetime,自动管理

23 开发的时候直接上服务器

开发一点就提交一点,要不然一旦服务器上有啥问题,很难找到,最后会特别麻烦,即使配置的环境完全一致,还是很难找到问题,

24 用户表分两个用户组,要分别看不同的组数据,表格和菜单都用两套

controller复制一份改名字,内容不变

view复制一份改名字,根据需求显示字段

js复制一份改名字,根据需要改字段

js里面添加内容(修改queryParams的过滤条件即可)

// 初始化表格
table.bootstrapTable({
    url: $.fn.bootstrapTable.defaults.extend.index_url,
    pk: 'id',
    sortName: 'user.id',
    queryParams: function (params) {
        let filter = JSON.parse(params.filter);
        filter.group_id = 2;
        params.filter = JSON.stringify(filter);
        let op = JSON.parse(params.op);
        op.group_id = '=';
        params.op = JSON.stringify(op);
        return params;

    }
    .....

25.自己新建菜单注意事项

首先如果是一个页面,就选择菜单,如果是菜单里面的子功能,比如添加,删除,就选择不是菜单,这样下面不需要选择tab页或者dialog等
如果选择菜单,显示在tab页就要选择tab页
另外重要的一点是:写规则路径时,前面没有/,比如user/doctor/index,有/就错了

26.新建数据表的时候,尽量把不同功能的表都单独放,不要聚合使用,比如收藏和点赞,逻辑基本完全一致,但是不同的功能,这种就不要放一起,后台管理系统也容易做,一行代码生成即可,否则后期改起来后患无群啊,除非不得已.否则坚决不能混在一张表里面,这次吃了大亏了

27.对于判断是与否的问题,比如评论表里面,条目可能是评论也可能是回复,这种情况下,不要更加他的父id是否存在来判断,应该多加一个字段,表示是否为回复,更加直观,逻辑更加清楚,数据量虽然增加了,但是逻辑不会搞混乱.否则可能后续功能升级的时候,逻辑会越来越乱,最后完全理不清楚了.宁可增加数据库的体积也不要把思路搞混乱了.

逻辑应该越简单越好,不要为了节约体积而导致逻辑的复杂性

28.同一个model/数据表生成多个菜单

同一个表可以生成多个curd菜单,只需要改一下自定义控制器名称即可,适合于同一个表在不同逻辑使用的情况,
同时可以选择列表中显示的字段
可以设置忽略的字段,忽略后在edit和add页面也不会显示这个字段,非常的方便
add和edit里面如果要隐藏一个字段,并且给他设置默认值,直接把生成的那个字段项注释掉,自己写一个同样name的input,设置value为默认值即可,太牛叉了

29.前期开发的时候数据可以直接同步到线上

但是上线之后只能同步数据结构,任何数据都不要同步,而是手动在线上再操作一次,否则一旦造成问题,那是灾难性的.实际上控制权限时,应该给程序员的数据表权限设置的小一点.只有管理者才能做这个同步数据库结构的操作.

30.自己写的弹框,提交之后如何自动关闭窗口

如下自己写了几个按钮,点击后打开弹框,但是提交后不会自动关闭和刷新

  {
                            field: 'buttons',
                            width: "120px",
                            title: __('按钮组'),
                            table: table,
                            events: Table.api.events.operate,
                            buttons: [
                                {
                                    name: 'reducehours',
                                    text: __('课时扣减'),
                                    title: __('课时扣减'),
                                    classname: 'btn btn-xs btn-primary btn-dialog',
                                    icon: 'fa fa-list',
                                    url: 'student/reducehours',
                                    callback: function (data) {
                                        //关闭弹窗
                                        Layer.alert("接收到回传数据:" + JSON.stringify(data), {title: "回传数据"});
                                    }
                                },
                                {
                                    name: 'addhours',
                                    text: __('课时增加'),
                                    title: __('课时增加'),
                                    classname: 'btn btn-xs btn-primary btn-dialog',
                                    icon: 'fa fa-list',
                                    url: 'student/addhours',
                                    callback: function (data) {
                                        Layer.alert("接收到回传数据:" + JSON.stringify(data), {title: "回传数据"});
                                    }
                                }, {
                                    name: 'list',
                                    text: __('增减记录'),
                                    title: __('增减记录'),
                                    classname: 'btn btn-xs btn-primary btn-dialog',
                                    icon: 'fa fa-list',
                                    url: 'hourslog/index?student_id={id}'
                                },
                            ],
                            formatter: Table.api.formatter.buttons
                        },

实际上只需要在对应的js文件里面添加如下代码:
在var Controller = { 这个里面添加即可

 reducehours:function () {
            Controller.api.bindevent();
        },
        addhours:function () {
            Controller.api.bindevent();
        },

31.改装添加弹出框

需要实现的功能是:1.添加时需要带上班级(grade_id),否则不允许添加,也就是原来的添加逻辑就变了2.get请求时,需要把本班级的人员都显示查出来,供用户勾选,同时提交时,把勾选的所有学员id上传,要对这些学员的数据做修改
controller里面修改如下

    public function add()
    {
        $grade_id = $this->request->param('grade_id');
        if ($grade_id) {
            //有班级id,则获取班级下的学生
            $stuGrades = \app\admin\model\StuGrade::with('stu')->where('grade_id', $grade_id)->select();
            if ($this->request->isGet()) {
                $this->view->assign('stuGrades', $stuGrades);
                return parent::add();
            } else {
                //这里需要自己做批量添加上课记录的操作,同时要给每一个学员扣除掉1个课时
                $stu_ids = $this->request->param('row.stu_id/a');
                Db::startTrans();
                $success = 0;
                try {
                    foreach ($stu_ids as $id) {
                        $data = $this->request->post();
                        $data['row']['stu_id'] = $id;
                        \app\admin\model\StudyRecord::create($data['row']);
                        \app\admin\model\Stu::update(['class_num' => ['dec', 1]], ['id' => $id]);
                    }
                    Db::commit();
                    $success = 1;
                } catch (\Exception $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                }
                $success ? $this->success('添加成功') : $this->error('添加失败');

            }
        }
        $this->error('请在班级管理中添加');
    }

在view里面修改如下
关键点在于判断如果存在stuGrades变量,就是有学员列表,把学员列表显示出来(用下拉选择框),否则就显示单个学员的添加功能

 {if condition="isset($stuGrades)"}
    <div class="form-group">
        <label class="control-label col-xs-12 col-sm-2">{:__('学员列表')}:</label>
        <div class="col-xs-12 col-sm-8">
            <select id="c-type_list" class="form-control selectpicker" multiple="" name="row[stu_id][]">
                {foreach name="stuGrades" item="vo"}
                <option value="{$vo.stu_id}" {in name="key" value="" }selected{/in}>{$vo.stu.name}</option>
                {/foreach}
            </select>

        </div>
    </div>
    {else/}
    <div class="form-group">
        <label class="control-label col-xs-12 col-sm-2">{:__('Stu_id')}:</label>
        <div class="col-xs-12 col-sm-8">
            <input id="c-stu_id" data-rule="required" data-source="stu/index" class="form-control selectpage"
                   name="row[stu_id]" type="text" value="">
        </div>
    </div>
    {/if}

最重要的在于js文件中的修改
在add方法下添加的js代码都会在add.html里面进行操作,当然也可以在add.html里面去修改,但是可能会有点乱,就都写在这里了
注意在init里面,我们吧add_url 也加上location.search,这样添加请求的时候,就会把之前带的参数也携带上,这样才能传到controller里面,这一步非常关键

            Table.api.init({
                extend: {
                    index_url: 'study_record/index' + location.search,
                    add_url: 'study_record/add' + location.search,
                    edit_url: 'study_record/edit',
                    del_url: 'study_record/del',
                    multi_url: 'study_record/multi',
                    import_url: 'study_record/import',
                    table: 'study_record',
                }
            });
//其他代码省略...
        add: function () {
            function getQueryParam(param) {
                var searchParams = new URLSearchParams(window.location.search);
                return searchParams.get(param);
            }

            var grade_id = getQueryParam('grade_id');
            //在form里面插入一个input,name为grade_id,value为grade_id
            if (grade_id) document.getElementById('add-form').append("<input type='hidden' name='grade_id' value='" + grade_id + "'>" );
            Controller.api.bindevent();
        }

32.弹出框里面只显示当前学员的缴费记录列表,添加时默认选中当前学员id

1.再stu.js里面添加按钮,点击弹出一个列表框

                        {
                            field: 'buttons',
                            width: "120px",
                            title: __('按钮组'),
                            table: table,
                            events: Table.api.events.operate,
                            buttons: [
                                             {
                                    name: 'charge',
                                    text: __('缴费记录'),
                                    title: __('缴费记录'),
                                    classname: 'btn btn-xs btn-primary btn-dialog',
                                    icon: 'fa fa-list',
                                    url: 'charge/index?stu_id={id}',
                                }
                                               ],
                            formatter: Table.api.formatter.buttons
                        }

2.在charge.js里面,修改add 的url,同时对add方法进行修改
在init里面修改addurl

 Table.api.init({
                extend: {
                    index_url: 'stu_grade/index' + location.search,
                    add_url: 'stu_grade/add'+location.search,
                    edit_url: 'stu_grade/edit',
                    del_url: 'stu_grade/del',
                    multi_url: 'stu_grade/multi',
                    import_url: 'stu_grade/import',
                    table: 'stu_grade',
                }
            });

在add方法里面修改,添加自动修改stu_id

 add: function () {
            function getQueryParam(param) {
                var searchParams = new URLSearchParams(window.location.search);
                return searchParams.get(param);
            }
            var id = getQueryParam('stu_id');
            if (id) document.getElementById('c-stu_id').value = id;
            Controller.api.bindevent();
        },

33.tab更换默认选中项

在列表加载前,重新修改类型为目标tab,因为这个js只加载一次,所以只会在第一个次加载时才会选中目标tab

    var Controller = {
        index: function () {

            // 初始化表格参数配置
            Table.api.init({
                extend: {
                    index_url: 'stu/index' + location.search,
                    add_url: 'stu/add'+location.search,
                    edit_url: 'stu/edit',
                    del_url: 'stu/del',
                    multi_url: 'stu/multi',
                    import_url: 'stu/import',
                    table: 'stu',
                }
            });
            let urlSearch = new URLSearchParams(location.search);
            if( !urlSearch.has('is_has') ) {

                urlSearch.set('is_has', '1');
                location.search = '?'+urlSearch.toString()
            }
            var table = $("#table");
            ...

34. api应用下controller 里面使用validate进行参数验证

重写这个方法

  protected function validate($data = [], $validate = '', $message = [], $batch = false, $callback = null)
    {
        !$data && $data = $this->request->param();
        $res = parent::validate($data, $validate, $message, $batch, $callback); // TODO: Change the autogenerated stub
        if ($res !== true) $this->error('error', $res,-1);
    }

然后我们再自己的controller里面就可以使用这个方法了,直接调用,如果验证不通过,会自动触发参数错误的返回值,并且返回的code为-1,方便前端统一处理
api应用中因为方法很多,又不是所有的方法都需要验证,所以验证器的名字最好是controller名+方法名,这样在需要验证的时候使用即可.

不过我还是觉得这个验证有些费劲,不过准确性比较高,后期看情况用一下吧.
admin里面要多用这个来限制用户提交的数据,更加方便

35.联动下拉选择框的使用

 <div class="form-inline col-xs-12 col-sm-8" data-toggle="cxselect" data-selects="articletype_pid,articletype_id">
            <select class="articletype_pid form-control" name="row[articletype_pid]" data-url="articletype/list"></select>
            <select class="articletype_id form-control" name="row[articletype_id]" data-url="articletype/list" data-query-name="pid"></select>
 </div>

需要注意的地方
data-selects=“articletype_pid,articletype_id” 这里的articletype_pid,articletype_id 要与里面的select 的class名字相同才可以,并且不能出现特殊字符
data-query-name=“pid” 这里规定了传入的参数键名
链接返回值必须是json,并且code=1,data里面有name和value,name是显值(比如name字段),value是id值(字段)
例如;

    public function list($pid = ''): \think\response\Json
    {
        if (!$pid) {
            $pid = 5607;
        }
        $list = $this->model
            ->field('id as value,name')
            ->where('parent_id', $pid)
            ->select();
        $this->result($list, 0, 'ok', 'json');
    }

36.列表里面显示统计数据,比如总金额

在controller里面加入对应字段,然后返回

//返回剩余次数和总次数
            $lefttimes = $this->model->where($where)->where('expiretime', '>', time())
                ->sum('lefttimes');
            $total = $this->model->where($where)->where('expiretime', '>', time())
                ->sum('total');
            $result = array("total" => $list->total(), "rows" => $list->items(), 'ext' =>
                [
                    'lefttimes' => $lefttimes,
                    'total' => $total
                ]
            );
         return json($result);

view里面加入对应的界面元素

..省略上面的代码

                        <a href="javascript:;" class="btn btn-default" style="font-size:14px;color:dodgerblue;">
                            <span class="extend">
                                剩余次数:<span id="left">0</span>
                                总次数:<span id="total_times">0</span>
                            </span>
                        </a>
                    </div>
                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
                           data-operate-edit="{:$auth->check('recharge/edit')}"
                           data-operate-del="{:$auth->check('recharge/del')}"
                           width="100%">
                    </table>

js文件里面对界面进行渲染


            //...之前的代码省略,当表格数据加载完成时
            table.on('load-success.bs.table', function (e, data) {
                //这里可以获取从服务端获取的JSON数据
                console.log(data);
                //这里我们手动设置底部的值
                $("#left").text(data.ext.lefttimes);
                $("#total_times").text(data.ext.total);
            });
            // 为表格绑定事件
            Table.api.bindevent(table);
        },

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/699296.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Shell脚本和变量

文章目录 Shell脚本shell的解释器Shell的作用Shell脚本的构成Shell的执行方式 重定向操作变量变量的类型&#xff1a;变量名的规范变量值的规范整数运算 &#xff0b; &#xff0d; / %小数运算 小数运算 Shell脚本 脚本就是可运行的代码的集合&#xff0c;脚本语言&#xff…

清华出品,开源最强,我又出手了(全网首发!)

清华出品的ChatGLM-6B自开源那刻起&#xff0c;GLM系列的每一次更新都受到了业界的热切关注。尤其是ChatGLM第3代开源之后&#xff0c;其强大和适配性让很多人惊叹&#xff0c;之后大家对GLM的第4代模型充满了期待。终于&#xff0c;今天它来了&#xff01;我要为大家介绍的是这…

RAG与知识库搭建

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务&#xff0c;并且需要GPU资源&#xff0c;可以考虑使用UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&…

阿里云百炼开发AI大模型详解

AI项目功能设想描述文档 随着AI发展越来越迅速&#xff0c;各行各业都需考虑如何将AI结合到自己的产品中&#xff0c;目前国内大部分的AI问答网站&#xff0c;都是基于Open AI实现的&#xff0c;但是如何需要运用到企业产品中那我们考虑的因素就会比较多 将ChatGpt移植到企业中…

电流的本质是什么

话说很久以前&#xff0c;科学发现纯靠人眼识别。有一天&#xff0c;泰勒斯(古希腊哲学家&#xff0c;被称为科学的祖师爷)一时手痒&#xff0c;拿着琥珀与皮毛摩擦。 结果他发现那种半透明的小石头&#xff0c;居然产生了吸引小物体的魔力。 面对这个现象&#xff0c;老泰开始…

leetcode-04-[24]两两交换链表中的节点[19]删除链表的倒数第N个节点[160]相交链表[142]环形链表II

一、[24]两两交换链表中的节点 重点&#xff1a;暂存节点 class Solution {public ListNode swapPairs(ListNode head) {ListNode dummyHeadnew ListNode(-1);dummyHead.nexthead;ListNode predummyHead;//重点&#xff1a;存节点while(pre.next!null&&pre.next.next…

正大国际期货:原油投资出现亏损的五种心理弱点

在原油投资当中&#xff0c;有五种心理弱点最容易导致投资者失败&#xff0c;必须注意加以克服! 盲目胆大心理。 有些投资者从未认真系统地学习过投资理论技巧&#xff0c;也没有经过任何模拟训练&#xff0c;甚至连最起码的投资基础知识都不明白&#xff0c;就贸然的进入原油…

假期已结束,大家都开始上班了吗

千行赏金APP&#xff1a;一站式悬赏任务平台详解 一、功能特点 千行赏金APP&#xff0c;作为一个综合性的悬赏任务平台&#xff0c;其功能特点突出&#xff0c;为用户提供了丰富的体验。首先&#xff0c;用户可以在平台上发布各类任务&#xff0c;如填写问卷、参与调研、试玩游…

轻松掌握两种方法,教你jpg图片怎么改大小kb

在数字时代&#xff0c;我们经常需要在网络上传、分享或存储图片&#xff0c;而其中一项常见的挑战就是将图片的文件大小调整到符合特定要求。对于JPEG格式的图片&#xff0c;改变其大小&#xff08;以KB为单位&#xff09;是一种常见的需求&#xff0c;特别是在网络上传限制或…

华为OD技术面试-统计全1子矩形-2024手撕代码真题

题目描述: 给你一个 m x n 的二进制矩阵 mat ,请你返回有多少个 子矩形 的元素全部都是 1 。 示例 1: 输入:mat = [[1,0,1],[1,1,0],[1,1,0]] 输出:13 解释: 有 6 个 1x1 的矩形。 有 2 个 1x2 的矩形。 有 3 个 2x1 的矩形。 有 1 个 2x2 的矩形。 有 1 个 3x1 的矩形。…

新火种AI|摊上事儿了!13名OpenAI与谷歌员工联合发声:AI失控可能导致人类灭绝...

作者&#xff1a;小岩 编辑&#xff1a;彩云 2024年&#xff0c;OpenAI的CEO Sam Altman就没有清闲过&#xff0c;他似乎一直走在解决麻烦的路上。最近&#xff0c;他的麻烦又来了。 当地时间6月4日&#xff0c;13位来自OpenAI和Google Deep Mind的现任及前任员工联合发布了…

【OCPP】ocpp1.6协议第4.5章节Firmware Status Notification的介绍及翻译

目录 4.5、固件状态通知Firmware Status Notification-概述 Firmware Status Notification 消息 Firmware Status Notification 请求消息 Firmware Status Notification 响应消息 使用场景 举例 FirmwareStatusNotification 请求示例 处理 FirmwareStatusNotification …

Python YOLOv5 7.0 基于深度学习的口罩检测识别系统

目录 1&#xff0c;演示视频和资源下载 1.1 演示视频 1.2 资源下载 2&#xff0c;数据集 3&#xff0c;代码 3.1 带 PyQt5 UI 的检测程序&#xff0c;基于YOLOv5 7.0 3.1.1 根据训练结果进行检测 3.1.2 自动保存每张图片/每帧的检测结果 3.1.3 筛选查看每张图片/每帧检…

【小白学Python】自定义图片的生成(一)

目录标题 安装Pillowdemo代码初次代码计划 个人需要&#xff0c;基于文字生成图片。 除了AI外&#xff0c;对于简单的图片&#xff0c;Python在这方面也非常擅长。 我算是一个Python小白&#xff0c;除了业余时尝试过Python基本语法的练习&#xff0c;从未真正使用过Python。…

存储协议入门-UPIU简介

写在前面:本文参考UFS jedec3.1,本文思维导图如下,详细内容关注微信:存储协议探索 ​ 1. UPIU架构 UPIU是UFS协议中的一种数据包格式,用于传输应用层的各种请求和命令,UFS采用的是客户-服务端架构,UFS主机(客户)主动发起请求,UFS设备(服务)执行后并返回相应状态。…

物联网8大协议介绍及对比

一.物联网主流协议介绍 1.MQTT 协议 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;即消息队列遥测传输。 MQTT 协议最初是在 1999 年由 IBM 公司开发的&#xff0c;用于将石油管道上的传感器与卫星相连接。2014 年正式成为 OASIS 开放标准。 MQTT 使用…

57.WEB渗透测试-信息收集- 端口、目录扫描、源码泄露(5)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;56.WEB渗透测试-信息收集- 端口、目录扫描、源码泄露&#xff08;4&#xff09; 下载网站…

程序员们,如何预防大龄危机?

困境 在中国&#xff0c;程序员到了35岁&#xff0c;基本是一个坎&#xff0c;如果你还是通过常规的招聘网或者猎头去找工作&#xff0c;能拿到offer的比例相对低&#xff0c;因为跟新手程序员比&#xff0c;你没有明显的优势。 论薪资&#xff1a; 大龄程序员的薪资要求应该…

OSI七层网络参考模型

一、物理层 我们要发送出去的数据在计算机里只不过是无数的0和1&#xff0c;0或1就叫做比特&#xff0c;物理层就是把这些比特用不同的媒介传输出去&#xff0c;可以用电、光或者其他形式的电磁波来表示和传输信号&#xff0c;数据从网络接口出去以后&#xff0c;会经过不同的网…

DeepSpeed Huggingface模型的自动Tensor并行、kernel注入、训练阶段的优化版kernel

推理阶段。 在后台&#xff0c;1. DeepSpeed会把运行高性能kernel(kernel injection)&#xff0c;加快推理速度&#xff0c;这些对用户是透明的&#xff1b; 2. DeepSpeed会根据mp_size来将模型放置在多个GPU卡上&#xff0c;自动模型并行&#xff1b; import os import torch …