desktop开发指南
引用块内容
desktop 简介
- desktop是基于base的一个独立的app。
- 通俗的讲desktop就是后台管理系统。
- desktop还有以下特点:
- desktop提供了常用的后台操作实现机制,这大大节省了你后台开发的时间。
- desktop提供一个带有权限和工作流的操作环境。
- desktop作为ecos的一个基础app一般不单独存在,是其他app操作流程的载体。
- 安装desktop后,访问后台,会出现如下登录界面:
desktop.xml 说明
- desktop.xml是后台菜单“显示”文件。
- 装了desktop这个app,就能识别其他app目录下的desktop.xml文件。
- 这个文件必须列出所有后台看见或看不见的控制器,否则除非是超级管理员,任何其他非超级管理员desktop用户无法访问这些控制器。
<desktop> <panelgroup id="other" icon="panel-other.png" order="100">其他</panelgroup> ... <adminpanels group="desktop_other" permission="other" controller="admin_member_attr" act="index" display="true"> 会员注册 </adminpanels> ... <permissions> <permission id="shipment" display="true">配送配置</permission> </permissions> ... <workground name="商品" id="b2c.workground.goods" controller="admin_goods" action="index" order="20"> ... <menugroup name="商品管理"> <!--菜单组--> <!-- name 菜单组的名字--> ... <!-- 左边的列表菜单 --> <menu controller="admin_goods_editor" action="add" permission="goods" display="false" order="10">添加商品</menu> <menu controller="admin_goods_editor" action="index" permission="goods" display="false" order="10">商品列表</menu> ... </menugroup> ... </workground> ... </desktop>
- desktop.xml不一定包含上面的所有标签,在符合语法规则的前提下,可以根据自己的需求任意增减标签。
语法规则以及各个标签和标签属性含义如下:
- desktop 每个 desktop.xml 的根标签
panelgroup 控制面板里的组其属性含义如下:
id 控制面板里组的唯一标识 icon 控制面板里组的图标,路径默认的起点是 app 下的 static ,假如此 desktop.xml 是 desktop 这个app的,则图标的路径会自动解析成/app/desktop /static/panel-other.png order: 组的显示顺序
adminpanel 控制面板里的项属性含义如下
group: 指属性那个控制面板组,其值是panelgroup的id属性值加app前缀,例如group="desktop_other"
permission: 指此控制面板里的项属性那个权限(下面讲到权限)
controller: controller和action(构成点此控制面板项时的去处,如果action属性未写则默认值为index)
action: controller和action(构成点此控制面板项时的去处,如果action属性未写则默认值为index)
display: 是否显示在控制面板组里,true为显示,false为不显示
permissions 权限被包括在它里面
permissions
- 包含在permissions标签里
id: 权限的唯一标识,全局唯一
display: 新建角色时,是否显示在角色权限列表里,true为显示,false为不显示
workground 工作区
- 可以包含多个menugroup,看下【desktop图例】其属性有:
name: 【1区】显示出来的label
id: (workground的唯一标识)
controller: 构成点此workground tab时的去处,如果action属性未写则默认值为index
action: 构成点此workground tab时的去处,如果action属性未写则默认值为index
order: 此workground在tab列表中的排序
menugroup 是workground里面的列表组
- 工作区里面的列表组;可以包含多个menu,看【desktop图例】 【2区】
name : 列表组的名称
menu 是 menugroup 工作组里的列表项
- 看【desktop图例】为【3区】提供菜单,除了拥有跟workground一样的属性外,此标签还有另外三个属性:
permission: 权限,为标签permission里的id属性的值 display: 是否显示,有些控制器里的方法是不必显示成菜单的,比如得到post数据保存商品的控制器等,这时需把display设成false的 params: 在url中传值,菜单上的访问链接将加上params的参数例如: <menu controller="admin_notebook" action="index" params="view:1|schema:2">留言编辑列表</menu> <!-- 点击留言编辑列表 得到的URL地址为: --> http://localhost/book/index.php/shopadmin/#app=notebook&ctl=admin_notebook&act=index&view=1&schema=2
desktop图例
- 一个典型的desktop的列表页包含了哪些内容,如下图所示:
接下来我们看各个区里的内容是如何增删及展示出来的
- 【1、2、3区】都是根据app下的desktop.xml产生,如何填充,可以参照desktop.xml的介绍试试看
- 【5区】顶部菜单产生是靠往服务id为desktop_menu里注册服务实现
- 在service.xml里添加
<service id="desktop_menu">
<class>maidivh_service_view_menu</class>
</service>
<!-- 注意 maidivh_service_view_menu 这个类是在 maidivh/lib/service/view/menu.php下的类 -->
# 例子
class maidivh_service_view_menu
{
function function_menu(){
/* 这个方法只能定义到前台 */
// $shop_base = app::get('sit')->router()->gen_url(array('app' => 'sit', 'ctl' => 'default'));
// $html[]=" <a href='$shop_base' target='_blank'>留言板</a>";
/* 下面这个方法能直接定义到后台 */
$html[]="<a href='index.php?app=maidivh&ctl=admin_maidivh&act=index' target='_blank'>".留言板."</a>";
return $html;
}
}
说明
:desktop_menu 的服务,必须定义名字为function_menu的方法,它的返回值即为【5区】的菜单项
4区finder详细解释
finder图
- 下面对finder各个区的内容的来龙去脉做解释:
- 我们平时做任何web应用大概都少不了后台管理功能,
- 这之中最常看到的大概就是:数据列表,对数据进行单条查看,删除,搜索列表数据。
- finder就是做这样工作的,要做到这些事情只需简单的给一个方法传几个参数而已。
# 例如
function index(){
$this->finder('b2c_mdl_goods',array(
'title'=>app::get('b2c')->_('商品列表'),
'actions'=>array(array('label'=>app::get('b2c')->_('添加商品'),
'href'=>'index.php?app=b2c&ctl=admin_goods_editor&act=add','target'=>'_blank'),),
'use_buildin_set_tag'=>true,
'use_buildin_filter'=>true,
'use_buildin_export'=>true,
'allow_detail_popup'=>true,
'use_view_tab'=>true,
'base_filter'=>array('order_refer'=>'local','disabled'=>'false'), //对tab数据进行过滤筛选
'finder_aliasname'=>'xxxx',
));
}
后台
的控制器必须继承 desktop_controller,继承后才有finder方法,下面介绍下 finder 方法的几个参数:
@ 第一个参数是字符串,(上例中是b2c_mdl_goods),是model里的class名,它决定了finder列表的数据源,默认情况下是b2c_mdl_goods类里的
getlist方法返回的数据
@ 第二个参数是数组,这个数组内涵相当丰富,解释如下:
title: 【图 finder】中的【1区】显示出来的内容
actions: 【图 finder】【2区】里的内容除了显示内置的操作以外(use_buildin_set_tag,use_buildin_filter这些是控制项),
还可以自定义添加新操作,参照上面格式。
- 内置的操作的控制项,其值为
true
时,显示此内置项。完整的内置操作及含义如下(可到desktop_finder_builder_view类里查看):
use_buildin_new_dialog: # 是否显示新建操作 use_buildin_set_tag: # 是否显示设置标签操作 use_buildin_recycle: # 是否显示删除操作 use_buildin_export: # 是否显示导出操作 use_buildin_import: # 是否显示导入操作 use_buildin_tagedit: # 是否显示标签管理操作 base_filter: # 对tab数据进行过滤筛选,参照上面格式 max_actions => int() # defaut: 7 finder面板支持最大的action数量
- 下面是 finder[2区]里面的内容,控制项
use_view_tab: # 是否显示finder中的tab(如果有),有无需看控制器中是否有_views方法。 use_buildin_filter: # 是否使用高级筛选 【图 finder】【6区】需要在 dbschema 里面定义 filetertype = true;条件 use_buildin_refresh: # 是否显示刷新操作(高级筛选旁) use_buildin_setcol: # 是否显示列配置 use_buildin_selectrow: # 是否显示每条记录前的复选按钮 allow_detail_popup: # 是否显示查看列中的弹出查看图标(【图 finder.png】4区第二个图标) finder_aliasname: # 此finder的别名,用于保存此finder的
下面对几个重点区域内容的填充做详细讲解
增加finder列表的自定义列
- 查看列 (分为下列查看和弹出查看,如图
- 自定义列(也可称函数列),可以通过一种方法扩展
- 普通列(数据表里有的字段,也即dbschema里有的字段)
【图 finder】【5区】的两列操作和标签都不是dbschema里的字段,还能显示出来是因为他们是自定义列.
实现自定义列
- 注册一个service,其id是desktop_finder.xxx,xxx是finder方法的第一个参数.
- 上例中是b2c_mdl_goods,最终结果在相应的应用中services.xml中如下样子:
<service id="desktop_finder.b2c_mdl_goods"> <class>b2c_finder_goods</class> </service> <!--注:*该 class 在b2c(app)/lib/finder/goods.php-->
# 类 class b2c_finder_goods{} b2c_finder_goods 类里有两种方法,两种属性,属性和方法成对出现: # 第一种 属性以detail_开头,对应的方法也以detail_开头 例如: var $detail_basic = '详细列表'; function detail_basic($gid){ ... // 这个是获取 base_render() 的基类对象 $render = app::get('b2c')->render(); // 获取b2c_mdl_item 的model $oItem = kernel::single("b2c_mdl_item"); // 获取字段 $items = $oItem->getList('item_subject, item_posttime, item_email,item_content,item_imageid',array('item_id'=>$id),0,1); // 加载数据 $render->pagedata['item']=$items[0]; $render->display('admin/itemdetail.html'); } # 属性detail_basic是作为列头显示的, # 方法detail_basic的返回值是点击查看里出现的内容 # [如果有多个detail_开头的方法,则显示第一个里面的内容] # 第二种:属性以column_开头,对应的方法也以column_开头 var $column_editbutton = '编辑'; public function column_editbutton($row) { return '<a href="index.php?app=maidivh&ctl=admin_default&act=edit&id='.$row['item_id'].'">编辑</a>'; } # 属性column_editbutton是作为列头显示的, # 方法column_editbutton的返回值是每行此列的显示内容, # 方法column_editbutton的参数是当前行的数组
finder自定义列初始化宽度
- 在finder自定义列中设置
...
var $column_try = '测试';
var $column_try_width = 100;
public function column_try($row)
{
return '====';
}
...
效果图:
finder 列表初始化排序
- 在 dbschema 列属性中加order 属性
array(
'item_subject' =>
array (
'type' => 'varchar(100)',
'in_list'=>true,
'is_title'=>true,
'default_in_list'=>true,
'label'=>'第一列',
'order'=>10, # 注意这里
),
'item_content' =>
array (
'label' => '第三列',
'in_list'=>true,
'default_in_list' => true,
'order'=>30, # 注意这里
'type' => 'text',
),
'item_posttime' =>
array (
'in_list'=>true,
'default_in_list' => true,
'label' => '第二列',
'order'=>20, # 注意这里
'type' => 'archar',
),
)
高级筛选
- 高级筛选中的搜索项大部分来自dbschema中,
- 搜索类型[单选或下拉或输入关键词]也定义在dbschema
除此之外,高级筛选中的搜索项还可以通过注册service扩展。
- id为extend_filter_xxx,xxx为finder方法的第一个参数,例如:
<service id="extend_filter_b2c_mdl_memmbers">
<class>b2c_finder_extend_memmbers</class>
</service>
b2c_finder_extend_members 类如下:
class b2c_finder_extend_members{
function get_extend_colums(){
$db['members']=array (
'columns' =>array (
'refer_id' =>array (
'type' => 'varchar(200)',
'required' => true,
'default' => 0,
'label' => '首次来源ID',
'width' => 75,
'editable' => true,
'filtertype' => 'yes',
'filterdefault' => true,
'in_list' => true,
'default_in_list' => true,
),
'refer_url' =>array (
'type' => 'varchar(200)',
'required' => true,
'default' => 0,
'label' => '首次来源URL',
'width' => 75,
'editable' => true,
'filtertype' => 'yes',
'filterdefault' => true,
'in_list' => true,
'default_in_list' => true,
)));
return $db;
}
}
# class里必须包含get_extend_colums方法,
# 它的返回值跟dbschema里的一样,如果扩展了高级搜索,
# 一般需要在model里重定义_filter方法,以便使用上扩展过滤字段
# 如果你是联表查询的话 你还的 重写 model searchOptions()方法
b2c_finder_extend_members 类如下:
class b2c_finder_extend_members{
function get_extend_colums(){
$db['members']=array (
'columns' =>array (
'refer_id' =>array (
'type' => 'varchar(200)',
'required' => true,
'default' => 0,
'label' => '首次来源ID',
'width' => 75,
'editable' => true,
'filtertype' => 'yes',
'filterdefault' => true,
'in_list' => true,
'default_in_list' => true,
),
'refer_url' =>array (
'type' => 'varchar(200)',
'required' => true,
'default' => 0,
'label' => '首次来源URL',
'width' => 75,
'editable' => true,
'filtertype' => 'yes',
'filterdefault' => true,
'in_list' => true,
'default_in_list' => true,
)));
return $db;
}
}
# class里必须包含get_extend_colums方法,
# 它的返回值跟dbschema里的一样,如果扩展了高级搜索,
# 一般需要在model里重定义_filter方法,以便使用上扩展过滤字段
# 如果你是联表查询的话 你还的 重写 model searchOptions()方法
快捷搜索 searchOptions()
- 有两个地方影响快捷搜索,dbschema中定义的字段中,searchtype这一项,
- 还有就是联表查询 需要重写 searchOptions() 与 _filter() 方法
# 在detail的model中重写2大方法
function searchOptions(){
$arr = parent:: searchOptions();
$columns = array();
$columns['total_name'] = ('用户'); /** 添加用户名搜索随意定义只要不和表字段冲突就行 **/
return array_merge($columns, $arr);
}
# 搜索过滤
public function _filter($filter, $tableAlias = null, $baseWhere = null)
{
/* $filter['total_name'] 自动接受上面定义的字段传输过来的数据 */
if ($filter['total_name']) {
/* 首先根据你的值直接到他所在的表查询 member表 */
$mdl_member = app::get('pam')->model('members');
$member_id = $mdl_member->getRow('member_id', array('login_account|has' => $filter['total_name']));
/* 通过total表与member的关联获取total的id */
$mdl_total = app::get('sms')->model('total');
$total_id = $mdl_total->getRow('id', array('member_id' => $member_id['member_id']));
unset($filter['total_name']);
/* 最终通过detail表与total的关联得到数据展现在中 */
$filter['total_id'] = $total_id['id'];// 如果想要查看请 使用查看 ajax请求返回数据的方式
}
return parent::_filter($filter, $tableAlias = null, $baseWhere = null);
}
finder里面的tag(选项卡)
- 在你的前台或者后台的控制器中重写 _view 方法
$goods_mdl = $this->app->model('item');
$tags = array(
0=>array('label'=>app::get('b2c')->_('全部'),'optional'=>false,'filter'=>"",'addon'=>1,'href'=>'xxx.xxx','finder'=>'xxxx'),
#...0-7个选项卡
7=>array('label'=>app::get('b2c')->_('已作废'),'optional'=>false,'filter'=>array('status'=>'dead'),'addon'=>1,'href'=>'xxx.xxx'),
);
foreach ($tags as $key => $value) {
//生成tags所需参数
$sub_menu[]=array(
'label'=>app::get('maidivh')->_($value['label']),
'optional'=>true,
'filter'=>$value['filter'],
'addon'=>$goods_mdl->count($value['filter']),
'href'=>'index.php?app=maidivh&ctl=admin_maidivh&act=index&view='.$key
);
}
return $sub_menu;
}
/*
label: tab的标题文字
optional: 此tab是否可选
filter: 此tab的过滤条件
addon: 此过滤条件下有多少条记录
href: 此tab的链接地址
*/
回收站
如果finder方法第二个参数中使用了use_buildin_recycle,
则此finder列表的actions区就有了内置的删除按钮,
有时需要在删除前和删除后做一些检测工作,
比方记录不准删除,或删除记录时需删除资源文件等。
实现这一功能机制是在model里定义pre_recycle[删除前执行]和suf_recycle[删除后执行]方法
桌面(widgets 窗口小挂件)
- 登录后台首先看到的界面,其内容由各个app通过service注册进来,
- 里面每一块都是一个widgets,下面是b2c的service.xml里桌面内容相关的一段
<service id="desktop.widgets">
<class>b2c_desktop_widgets_workcount</class>
<class>b2c_desktop_widgets_stats</class>
<class>b2c_desktop_widgets_exstatistics</class>
</service>
- 每个class就是一个widgets,每个class里面的内容为以下格式[以b2c_desktop_widgets_workcount为例]:
class b2c_desktop_widgets_workcount implements desktop_interface_widget{
function __construct($app){
$this->app = $app;
$this->render = new base_render(app::get('b2c'));
}
function get_title(){
return app::get('b2c')->_("统计分析");
}
function get_html(){
...
return $render->fetch('desktop/widgets/workcount.html');
}
function get_className(){
return " valigntop";
}
function get_width(){
return "l-1";
}
/*
函数说明
function get_title(): desktop widgets标题
function get——html(): desktop widgets 内容
function get_className(): 给desktop widgets 区块添加class name
function get_width(): 返回值为l-1显示在左侧,值为l-2显示在左侧
*/
个性化定制
多个查看标签页
- 通过上图我们可以看出,我们可以为查看功能添加多个选项卡,每个选项卡可以有自身独立的显示页面。
- 该功能实现起来也非常简单。(我们就拿我们之前的做的notebook这个项目做为实例讲解。)实现过程如下:
-首先,定位实现“查看”功能的文件:app/maidivh/lib/finder/item.php。
var $detail_edit2 = '详细列表2'; # 这是第二个选项卡里里面的内容,其实和第一个没什么区别
function detail_edit2($id){
$render = app::get('notebook')->render();
$oItem = kernel::single("notebook_mdl_item");
$items = $oItem->getList('item_subject, item_posttime, item_email',
array('item_id' => $id), 0, 1);
$render->pagedata['item'] = $items[0];
$render->display('admin/itemdetail.html');
}
# 以上代码仅仅是为了实例需要,没有什么实际的意义。在function中我们可以根据自己的需求进行处理。
对finder下的数据的modify(修改)处理
- 我们通过一个“实例”来说明我们要做什么。
- 我们通过model文件里加modifier方法来给email这个字段标红。
- 我们先来看一下操作之前效果图:
定位文件app\maidivh\model\item.php
# 添加以下代码:
/*modifier的命名规则是modifier_ColumnName(ColumnName是表对应dbshema中的字段名字)。*/
public function modifier_item_email($row)
{
return "<span style='color:red'>".$row."</span>";
}
效果如图
也可以但单独哪个的颜色变掉
# strstr()第一次出现,并返回字符串的剩余部分:
public function modifier_item_email($row){
//修改后代码
if (strstr($row,'905530550')){
return "<span style='color:red'>".$row."</span>";
}else{
return $row;
}
//修改后代码
}
如图:
为后台首页添加自己的logo(modify操作)
如果我们要添加自己的logo图片我们要做如下操作:
- 在app\maidivh\services.xml中添加如下代码:
...
<service id="desktop_controller_content.desktop.default.index">
<class>maidivh_ctl_admin_out</class>
</service>
...
- 在 madivh/admin/out.php 写入一下代码
class maidivh_ctl_admin_out extends base_controller implements desktop_interface_controller_content
{
public function modify(&$html, &$obj)
{
$arr = "<a herf=\"index.php?ctl=dashboard&act=index\">ECstore</a>";
$html =str_replace($arr,"",$html); // 把 $html 中的$arr字符转化成 "";
var_dump($html);
//替换logo
$logoimg = "<img src=\"http://service.shopex.cn/images/fail.gif\" alt=\"ShopEx CERT INFO\" />";
$logoimgM = "<img src=\"http://www.shopex.cn/images/alllogo/alllogo_19.gif\" height=\"40px\"/>";
$html = str_replace($logoimg, $logoimgM, $html);
}
}
注
:我们也可以通过modify这个方法来修改其他后台页面信息。
- 【方法二】我们也可以通过修改数据库的方式来修改后台logo文字。具体操作见【后台页面的banner和logo修改】
- 【方法三】我们还可以通过修改/config/deploy.xml来修改后台登陆banner、logo等信息。
在finder区域添加一个虚拟列
- 先要注册services,代码如下:
<services>
<service id="desktop_finder.notebook_mdl_item">
<class>maidivh_finder_item</class>
</service>
</services>
- 找到app\maidivh\lib\finder\目录下的item.php添加如下代码:
<?php
...
# 增加一个虚拟列
var $column_edit2 = '测试列';
public function column_edit2($row){
return $row['item_subject'];
}
...
<?php
...
# 增加一个虚拟列
var $column_edit2 = '测试列';
public function column_edit2($row){
return $row['item_subject'];
}
...
按照条件改变后台的留言板的列表行数据颜色
- 首先我们要注册一个services,在services.xml添加如下代码:
...
<service id="desktop_view_helper">
<class>maidivh_view_helper</class>
</service>
...
...
<service id="desktop_view_helper">
<class>maidivh_view_helper</class>
</service>
...
finder中的多表链接显示(也就是多表关联)
- 同样的我们还是先来看效果:
- 我们看到上图中用户ID这个字段下面显示的是纯文字的名字而非数字,
- 实际上这个字段数据是通过dbschema多表链接来完成的。
- 首先我们在创建maidivh_item表的时候member_id字段设置如下:
'member_id'=>array(
'type'=>'table:member@maidivh', // 数字类型
'label'=>'用户名ID',
'in_list'=>true, //是否显示在列配置中,默认为false
'in_default_list'=>true, //默认在desktop列表中是否显示
'required'=>true, // 是否必填
/*
在这里我们有必要解释一下:'type' => 'table:member@maidivh',
这句代码的意思是:
在本表中的user_id这个字段的类型与maidivh_member这个表中的主键的字段一致。
*/
),
- 既然我们之前已经将member_id这个字段链接到maidivh_member中的主键,接下来就要设置要链接查询的字段。
- 将这个要显示的字段加上属性:
'is_title'=>true
,不加上关联不上:
'member_user'=>array('type'=>'varchar(100)','is_title'=>true,'required' => true, ),
注
:如果被链接的表(本例中的maidivh_membe表)没有字段被设置属性为'is_title'=>true,
那么系统默认
将主键的下一个字段为输出数据。- 如果要在主表中(本例中的
maidi_item
表)显示多个字段(默认只能显示链接表的一个字段数据)信息,可以通过services注册虚拟字段来实现。
后台页面的banner和logo修改
- 请查看文档
- 我下载的版本里面找不到banner/logo 的key
快速搜索区域自定义搜索名称
- 通过图片我们知道可以看出我们是要对后台界面的快速搜索区域的名称进行更改。具体实现如下:
- 打开对应,该finder区域的model文件(在这里我们以app/maidivh/model/item.php为例进行操作)加入如下函数
# 自定义快速搜索字段名称
function searchOption(){
$arr = parent::searchOption(); # 得到dbschema里定义searchtype的字段
$columns = array();
$columns['item_subject']='自定义1'; # 将搜索字段自定义名称
$columns['item_posttime']='自定义2';
$columns['item_email']='自定义3';
return array_merge($arr , $columns); # 合并数组,保证所有属性为searchtype的字段都显示
}
- 刷新页面后效果如下: