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

news2024/11/16 5:35:01

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);
        },

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java——IO流(一)-(1/8):File、IO流概述、File文件对象的创建(介绍、实例演示)

目录 File IO流概述 File文件对象的创建 介绍 实例演示 File 存储数据的方案 变量 double money 9999.5 数组 int[] age new int[100];对象 Student s new Student()集合 List<Student> students new ArrayList<>()…

【数据分析基础】实验二 Python程序流程控制、函数设计与使用

实验目的 熟悉选择结构嵌套时代码的缩进与对齐。理解带 else 子句的循环结构执行流程和条件表达式 value1 if condition else value2 的用法。熟悉使用循环和异常处理机构对用户输入进行约束的用法。掌握Python程序中进行选择、循环流程控制的语句、语法&#xff0c;熟练运用选…

中电金信:产教联合共育人才 AFAC2024金融智能创新大赛启动

当前&#xff0c;人工智能技术正在蓬勃发展&#xff0c;引领着各行各业迈向智能化的新纪元&#xff0c;特别是在金融科技领域&#xff0c;伴随人工智能技术的不断迭代与突破&#xff0c;金融服务的边界也在不断拓展&#xff0c;传统的金融业态正经历着深刻的变革与重塑。 与此同…

crossover软件安装程序怎么安装 Crossover for Mac切换Windows系统 crossover软件怎么样

CrossOver Mac版是专为苹果电脑用户打造的一款实用工具&#xff0c;这款工具主要方便用户在Mac上运行windows系列的应用程序&#xff0c;用户不需要安装虚拟机就可以实现各种应用程序的直接应用&#xff0c;并且可以实现无缝集成&#xff0c;实现跨平台的复制粘贴和文件互通等&…

【漏洞复现】海康威视综合安防管理平台 多处 FastJson反序列化RCE漏洞

0x01 产品简介 海康威视综合安防管理平台是一套“集成化”、“智能化”的平台,通过接入视频监控、一卡通、停车场、报警检测等系统的设备。海康威视集成化综合管理软件平台,可以对接入的视频监控点集中管理,实现统一部署、统一配置、统一管理和统一调度。 0x02 漏洞概述 由于…

系统思考—啤酒游戏沙盘

10个智商120的‮组人‬成‮团的‬队&#xff0c;大‮的家‬集体智‮是商‬多少&#xff1f; 在‮期长‬辅‮各导‬种‮业企‬的‮程过‬中&#xff0c;我‮经们‬常‮察观‬到&#xff0c;虽‮每然‬个‮门部‬都‮力努‬解决‮己自‬的问题&#xff0c;但‮司公‬整体的‮收应…

长文预警:九头蛇的进化——Tesla AutoPilot 纯视觉方案解析

九头蛇的进化&#xff1a;Tesla AutoPilot 纯视觉方案解析 前言 本文整理自原文链接&#xff0c;写的非常好&#xff0c;给了博主很多启发&#xff0c;投原创是因为平台机制&#xff0c;希望能被更多人看到。 嘿嘿&#xff0c;漫威粉不要打我←_←不是Hail Hydra&#xff0c…

彩虹易支付最新版源码

源码简介 彩虹易支付最新版源码&#xff0c;更新时间为5.1号 2024/05/01&#xff1a; 1.更换全新的手机版支付页面风格 2.聚合收款码支持填写备注 3.后台支付统计新增利润、代付统计 4.删除结算记录支持直接退回商户金额 安装环境 1.PHP版本>7.4 2.Mysql数据库 安装教…

83页 | 2024数据安全典型场景案例集(免费下载)

以上是资料简介和目录&#xff0c;如需下载&#xff0c;请前往星球获取&#xff1a;

Pico4 MR Unity零基础开发之获取手柄按键

一、导入示例资源 1、打开Package Manager面板&#xff0c;导入示例资源。 2、打开示例场景&#xff0c;方面后面测试。 二、打开 XRI Default Input Actions 三、设置XRI Default Input Actions 面板参数 1、点击号新增一项&#xff0c;重命名为Pico 2、新增并重命名Action …

JimuReport 积木报表 v1.7.52 版本发布,免费的低代码报表

项目介绍 一款免费的数据可视化报表工具&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完…

毫米波雷达阵列天线设计综合1(MATLAB仿真)

1 天线设计目标 毫米波雷达探测目标的距离、速度和角度&#xff0c;其中距离和角度和天线设计相关性较强。天线增益越高&#xff0c;则根据雷达方程可知探测距离越远&#xff1b;天线波束越窄&#xff0c;则角度分辨率越高&#xff1b;天线副瓣/旁瓣越低&#xff0c;则干扰越少…

[office] excel怎么设置图表格式- excel中chart tools的使用方法 #笔记#经验分享#其他

excel怎么设置图表格式? excel中chart tools的使用方法 excel怎么设置图表格式&#xff1f;excel中的数据可以制作成图表&#xff0c;在出啊如图表以后可以再图表总使用命令对其格式进行设置&#xff0c;下面我们就来看看excel中chart tools的使用方法&#xff0c;需要的朋友可…

libgdx ashley框架的讲解

官网&#xff1a;https://github.com/libgdx/ashley 我的libgdx学习代码&#xff1a;nanshaws/LibgdxTutorial: libgdx 教程项目 本项目旨在提供完整的libgdx桌面教程&#xff0c;帮助开发者快速掌握libgdx游戏开发框架的使用。成功的将gdx-ai和ashley的tests从官网剥离出来,并…

position的sticky与fixed 及固定上部分页面 使用举例

fixed&#xff08;固定定位&#xff09; 生成绝对定位的元素&#xff0c;相对于浏览器窗口进行定位。元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。可通过z-index进行层次分级。 sticky position属性中最有意…

【LLM】度小满金融大模型技术创新与应用探索

note 从通用大模型到金融大模型金融大模型的训练技术创新金融大模型的评测方法创新金融大模型的应用实践创新总结&#xff1a;金融大模型迭代路径 一、轩辕大模型 二、垂直大模型训练 1. 数据准备 数据质量是模型效果的保障。首先数据要丰富&#xff0c;这是必备的条件。我们…

OpenAI新成果揭秘语言模型神经活动:稀疏自编码器的前沿探索

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

尝试使用blazor(一)吐槽blazor,未开始之前,先吐为敬

为什么要写一点关于blazor的文章呢?其实是没什么人看的&#xff0c;我知道blazor目前在国内使用的人数&#xff0c;恐怕一辆大巴车都坐不满。非常冷门&#xff0c;我刚用blazor遇到问题&#xff0c;花钱找人解决&#xff0c;找了国内几个著名的平台&#xff0c;几乎没人会blaz…

【Redis】Redis主从复制(一)————主从搭建

目录 背景主从复制主从复制的工作流程主从复制的优点 配置 redis 主从结构复制配置文件&#xff0c;修改配置主从结构启动 redis 服务备注 查看复制状态 背景 单节点服务器的问题问题&#xff1a; 可用性&#xff1a;如果这个机器挂了&#xff0c;意味着服务就中断了.性能&am…

探索智慧林业系统的总体架构与应用

背景&#xff1a; 随着人们对森林资源保护和管理的重视&#xff0c;智慧林业系统作为一种新兴的林业管理手段&#xff0c;正在逐渐受到广泛关注和应用。智慧林业系统的总体架构设计与应用&#xff0c;将现代信息技术与林业管理相结合&#xff0c;为森林资源的保护、管理和利用…