安装
推荐使用 composer 来安装
输入:
Copy composer require --prefer-dist ethercap/yii2-ledap "dev-master"
或在 composer.json
文件中加入如下内容:
Copy "ethercap/yii2-ledap": "dev-master"
使用说明
简介
本代码主要用到如下框架:
起步
复制layout。在复制后,你可以编辑layout的内容。
Copy cp vendor/ethercap/yii2-ledap/src/gii/layout.php xxx/views/layouts/main.php
代码自动生成
代码生成器与gii/crud的使用方法一致:
Copy php yii gii/ledapCrud --controllerClass="\frontend\controllers\TestController" --modelClass="common\models\Test" --searchModelClass="\frontend\forms\TestSearch"
在运行后,你能够在浏览器中访问这些页面。生成的代码如下:
xxController : 处理http请求的yii2 Controller.
views/xx/*.php : 模板文件,用来渲染html.
views/xx/*.api : Api接口文件,用来渲染接口.
xx/web/xx/*.js : 模板文件中对应的js文件,它会被在view中渲染出,并带有一个hash串。
生成的文件结构如下所示:
全局组件
1. loading
Copy <!-- 在.js中注册变量isLoading-->
<div class="page-loading-container" v-if="isLoading">
<div class="page-loading">加载中…</div>
</div>
2. toast
toast是对vue-bootstrap的toast 进行了封装,做了一些默认的配置及支持了html.
Copy // 弹出默认的toast
this .$toast ( "hello world" );
// toast支持html,vm代表当前的vue component
this .$toast ( '<span class="text-danger" @click="vm.alert()">hello world</span>' )
// toast配置,具体参数详见 https://bootstrap-vue.js.org/docs/components/toast
this .$toast ( "hello world" , {
variant : 'error/danger/info...' ,
title : 'BootstrapVue Toast' ,
autoHideDelay : 5000 ,
});
//toast 与下面基本等价,下面不支持直接写html
this . $bvToast .toast (message , options);
3. alert
alert是对vue-bootstrap的modal 进行了封装,做了一些默认的配置及支持。
Copy //弹出alert
this .$alert ( "hello world" );
//弹出alert,支持html,vm代表当前的vue component
this .$alert ( '<span class="text-danger" @click="vm.alert()">hello world</span>' );
this .$alert ( "hello world" , {
variant : 'error/danger/info...' ,
title : 'Confirmation' ,
size : 'sm' ,
buttonSize : 'sm' ,
okVariant : 'success' ,
headerClass : 'p-2 border-bottom-0' ,
footerClass : 'p-2 border-top-0' ,
centered : true
}) .then (() => {
console .log ( "ok" );
}) .catch (() => {
console .log ( "error" );
});
//alert与下面的调用基本等价
this . $bvModal .msgBoxOk (message , options);
4. confirm
confirm是对vue-bootstrap的modal 进行了封装,做了一些默认的配置及支持。
Copy //弹出confirm
this .$confirm ( "hello world" );
//弹出alert,支持html,vm代表当前的vue component
this .$confirm ( '<span class="text-danger" @click="vm.alert()">hello world</span>' );
this .$confirm ( "hello world" , {
variant : 'error/danger/info...' ,
title : 'Confirmation' ,
size : 'sm' ,
buttonSize : 'sm' ,
okVariant : 'success' ,
headerClass : 'p-2 border-bottom-0' ,
footerClass : 'p-2 border-top-0' ,
centered : true
}) .then (() => {
console .log ( "ok" );
}) .catch (() => {
console .log ( "error" );
});
//alert与下面的调用基本等价
this . $bvModal .msgBoxConfirm (message , options);
列表页
列表页的js有一个dataProvider来控制整个页面(注意:它与php的DataProvider并不是同一个东西)。
1.输入时刷新数据
我们可以在form-item上加一个事件侦听,这样当我们输入时,下面的表格可以自动搜索并刷新。
Copy <form-item :model="dp.searchModel" attr="name" @input="refresh('')"></form-item>
2. 一些DataProvider的API
Copy // 生成DataProvider
this .dp = ledap . App .getWebDataProvider ({
httpOptions : {
url : '/test/xxx' ,
params : 'ddd' ,
}
// 这个可以不填。填了之后,数据会以model.id进行判重,防止重复的数据展示
primaryKey: 'id' ,
// 可以不填,代表两个请求间的最小间隔,这在suggestion, http请求上极为有用。
// 0 代表无间隔
timeWait : 600 ,
});
// dataprovider同四部分组成。
// searchModel代表dp的搜索参数
console .log ( this . dp .searchModel)
// 数据
console .log ( this . dp .models);
// 列表页的排序器
console .log ( this . dp .sort);
// 列表页的分页器
console .log ( this . dp .pager);
// 修改参数并刷新
this . dp . searchModel .name = 'xxx' ;
this . dp .refresh ();
// 也可以用如下的函数达到同样的效果
this . dp .changeParams ({name : "xxx" });
// 修改页码
this . dp .changePage (page)
this . dp .nextPage ();
this . dp .prePage ();
// 我们有时候希望实现h5类似的上拉刷新和下拉刷新效果
this . dp .refresh ( "header" );
this . dp .refersh ( "footer" );
// 我们可能希望排序
this . dp .sort = "id, -name" ;
this . dp .refresh ();
// 我们也可以直接通过函数达到同样的效果
this . dp .setSort ( "id,-name" );
// 本地排序(不走http请求)
this . dp .sortModels ( "name" , asc = true );
// 可以通过isLoading来判断当前的加载状态
this . dp .isLoading
// 我们也可以通过事件来做一些事情。
this . dp .on ( ledap . WebDataProvider . EVENT_BEFOREGETDATA , function (){
});
this . dp .on ( ledap . WebDataProvider . EVENT_AFTERGETDATA , function (){
});
3. grid
grid是一个ledap组件,可以通过传入columns和dataProvider来生成表格
Copy columns : {
'id' ,
{
'attribute' : 'id' ,
'label' : 'ID' ,
//如果设置了sort,label是可以点击来排序的。
'userSort' : true ,
} ,
{
'attribute' : 'name' ,
'value' : function () {
// vm是当前的vue Component
// 在模板中我们能使用value,model, index, attribute 和 dataProvider
return '<a @click="vm.xxx(model)"></a>' ;
} ,
'format' : 'html' ,
}
}
如果你不满意默认的内容,你可以通过Vue Scoped slot来修改内容。组件会传输四个变量到scoped slot中:
model . 当前的model, 代表grid的一行。
column . 当前的colomn, 代表grid中的一列。
index . model在dp.models中的索引。dp.model[index] == model。
value . cloumn与model计算出来的结果。
Copy <grid class="table" :data-provider="dp" :columns="columns">
<template v-slot:label="p">
<th class="xxx">{{p.value}}{{p.model}}{{p.index}}{{p.column}}</th>
</template>
<template v-slot:default="p">
<td class="xxx">{{p.value}}{{p.model}}{{p.index}}{{p.column}}</td>
</template>
</grid>
你也可以使用dataProvider自己画view或使用list,而不是使用grid。
Copy <input type="xxx" v-model="dp.searchModel.name" @input="dp.refresh('')" />
<div v-for="model in dp.models">
<div class="">{{model.id}}</div>
<div class="xxx">{{model.img}}</div>
</div>
<div class="pager">
<div>total:{{dp.pager.currentPage}}|{{dp.pager.totalCount}}</div>
<a @click="dp.prePage()">PrePage</a>
<a @click="dp.nextPage()">NextPage</a>
</div>
View&Update&Create
在页面中有type这个参数,可以被用来控制页面的显示。它可以是view, update或create。
1.model
model与yii2的form Model 对应。基本用法如下:
Copy //生成一个model
let model = ledap . App .getModel (data);
// 修改model的值
model .name = "xxx" ;
// 获取model某个属性的label
model .getAttributeLabel ( "name" );
// 获取model某个属性的hit
model .getAttributeHint ( "name" );
// 获取model某个属性的error
model .getErrors ( "name" );
// 获取model某个属性的第一条错误
model .getFirstError ( "name" );
// 获取model的所有错误
model .getErrors ();
// 校验model的值.如果不正确,会返回false.
// 我们可以使用getErrors来展示错误
model .validate ();
2.detail
detail组件与grid组件类似。我们可以使用columns来展示一个detail,它的写法也与grid一致。
Copy columns : {
'id' ,
{
'attribute' : 'id' ,
'label' : 'ID' ,
//if use sort, we can click header to sort table
'userSort' : true ,
} ,
{
'attribute' : 'name' ,
'value' : function () {
// vm refer to current vue component
// value,model, index, attribute and dataProvider can be use in the template
return '<a @click="vm.xxx(model)">test</a>' ;
} ,
'format' : 'html' ,
}
}
与grid一样,我们也可以使用scoped slot来修改默认的页面。
3. form-item
当我们想要展示form时,form-item非常重要。它由四部分组成:
we cant use like this:
Copy <!--
常规输入框
validator: 什么时候去校验数据
tag: 组件的tag,默认为div
-->
<form-item :model="model" attr="name" :validator="['input', 'blur', 'focus']" :tag="div">
</form-item>
<!-- 我们可以使有scoped slot来修改默认的页面-->
<form-item :model="model" attr="name">
<template v-slot:label="p">
<label> {{p.model.getAttributeLabel(p.attr)}}{{p.model.isRequired(p.attr) ? '*' : ''}}</label>
</template>
<template v-slot="p">
<baseinput type="password" v-bind="p"></baseinput>
</template>
<template v-slot:error="p">
<p v-show="p.showError">{{p.showError}}</p>
</template>
</form-item>
<!-- 一些其它输入框 -->
<!--
groupinput 需要DictValidator。你应该在yii2的model中添加如下的rule来使用:
['name', \ethercap\common\validators\DictValidator::className(), [0 => 'xxx', 1=> 'xxx', ...]],
另外:dropdown也需要DictValidator.
ajax select 你可以参见下一节。
-->
<form-item :model="model" attr="name">
<template v-slot="p">
<groupinput v-bind="p"></groupinput>
</template>
</form-item>
<form-item :model="model" attr="name">
<template v-slot="p">
<dropdown v-bind="p"></dropdown>
</template>
</form-item>
有时,我们想用其它第三方写的vue组件,所以,我在这个包里写了一个示例。我使用了 vue2-datepicker 来示例.
Copy <? php
// DatePickerAsset中只是写了js的地址,我们也可以直接register来引入js
// $this->registerJs("http://xxxx.js", ['depends' => '\ethercap\ledap\assets\VueAsset'])
\ ethercap \ ledap \ assets \ DatePickerAsset :: register ( $this ) ;
?>
< form - item : model = "model" attr = "time" >
< template v - slot = "p" >
< date - picker v - model = "p.model[p.attr]" : value - type = "'format'" ></ date - picker >
</ template >
</ form - item >
另外,我们需要在Vue中注册这个组件:
Copy Vue .component ( "date-picker" , DatePicker .default);
其它组件
1.select2 & SearchAction
有时,我们需要一个类似于select2的ajax suggestion。这个包也为你提供了:
SearchAction: 一个 php Action 来处理搜索的请求.
Copy <? php
namespace frontend \ controllers ;
use Yii ;
class xxController extends Controller
{
// ....
public function actions ()
{
return [
'search' => [
'class' => \ ethercap \ ledap \ actions \ SearchAction :: className (),
'processQuery' => function ($model) {
$query = xxx :: find () -> select ( [ 'id' , 'name as text' ] ) -> asArray () ;
if ($model -> id) {
return $query -> where ( [ 'id' => $model -> id] ) ;
}
if ($model -> keyword){
$query -> andWhere ( [ 'like' , 'name' , $model -> keyword] ) ;
}
return $query;
} ,
// 接口返回的数据字段。默认如下,可以不写。
$dataConfig => [
'id' ,
'text'
]
] ,
];
}
}
after all this config, we have an api "/xx/search" to search data from database.
在这些配置完成后,我们就拥有了一个api——"/xx/search"来从数据库中搜索数据。
select2 component: 一个vue组件来发起http请求。
Copy <form-item :model="model" attr="search">
<template v-slot="p">
<select2 v-bind="p" :data-provider="dp"></select2>
</template>
</form-item>
Copy {
data : {
dp : App . ledap .getWebDp ({
httpOptions : {
"url" : '/xx/search'
}
});
}
}
2.upload & UploadAction
我们在php中引入js等依赖
Copy <? php
ethercap \ ledap \ assets \ UploadAsset :: register ( $this ) ;
在页面调用组件上传即可。
Copy < form-item class = "form-group" :model="model" attr = "attr" >
< template v-slot = "p" >
< uploader post-action = 'http://xxx.com/upload' v-model = "p.model[p.attr]" ></ uploader >
</ template >
</ form-item >
后端可以通过很简单的方式来处理, 文件在$_FILE中。
在数据库中,该数据对应的结构为:
Copy $model->attr=json_encode([
['name'=>'xxxx', 'url' => 'xxx'],
['name'=>'xxxx', 'url' => 'xxx'],
]);
高阶应用
自己实现vue组件/Ledap组件
在页面中,如果有一些元素总是重复,我们可以书写成组件方便复用,我们可以直接书写Vue组件,参见vue组件 。
ledap组件跟vue组件没有本质区别,但是引入了继承,下面我们来看一个例子:
我们一直使用formItem来做为form的包装器,现在我们想做一个formItem1, 但是把label和input的位置对调,我们可以这样做:
Copy //我们定义了一个叫form-item1的组件,它继承于form-item,除了template不同,其它的所有都与form-item一样(data, props, methods等)
ledap . App .getTheme () .addComponent ({
name : 'form-item1' ,
template : `<component :is="tag" class="form-group" :class="{'has-error':showError}">
<div class="col-sm-9">
<slot :model="model" :attr="attr" :validate="validate" :inputListeners="inputListeners">
<input class="form-control" :name="attr" :value="model[attr]" :placeholder="model.getAttributeHint(attr)" v-on="inputListeners" />
</slot>
<slot name="error" :model="model" :attr="attr" :showError="showError">
<p v-show="showError" class="help-block">{{showError}}</p>
</slot>
</div>
<slot name="label" :model="model" :attr="attr">
<label class="col-sm-3 control-label"> {{model.getAttributeLabel(attr)}}{{model.isRequired(attr) ? '*' : ''}}</label>
</slot>
</component>` ,
} , 'form-item' );
这样,我们可以在页面上看到效果了:
Copy <!-- 注意,别忘记在js上注册ledap.App.getTheme().register(['form-item1'], Vue) -->
<form-item1 :model="model" :attr="xxx"> </form-item1>
改用其它css框架
Tips
所有你使用的组件都需要先注册。App.register(['xxx'], Vue);
你可以通过修改AppAsset来替换全局模板或http请求
这个包,前后端并不是完全分离的。如果你想要使用npm来实现完全分离,直接使用ledap前端框架即可。