强大的后端(desktop)

since : %%date(%Y-%m-%d) $Rev$

desktop简介

desktop提供一个带有权限和工作流的操作环境,一个网站一般分为前台和后台,它提供了常用的后台操作实现机制,这大大节省了你后台开发的时间。desktop作为ecos的一个基础app一般不单独存在,是其他app操作流程的载体。ecstore是基于ecos的开源商城系统,亦依赖desktop,它使用了绝大部分desktop的功能,建议使用desktop开发新的应用时,多参照下ecstore,为便于理解,本手册亦以ecstore为例讲解。

此手册目标人群

开发者:开发基于ecos的新应用或扩展已有应用功能时,需开发后台功能的会用到desktop

desktop提供了下列功能:

  • 权限管理
  • 回收站
  • 显示app提供的菜单项或控制面板项
  • finder功能

desktop.xml说明

装了desktop这个app,就能识别其他app目录下的desktop.xml文件,这个文件必须列出所有后台看见或看不见的控制器,否则除非是超级管理员,任何其他非超级管理员desktop用户无法访问这些控制器。desktop.xml的结构大概是这样的:

<desktop>

	<panelgroup id="other" icon="panel-other.png" order="100">其他</panelgroup>
	...
	<adminpanel group="desktop_other" permission="other" controller='admin_member_attr' action='index' display='true'>会员注册项</adminpanel>
	...
	<permissions>
		<permission id="shipment" display='true'>配送设置</permission>
		...
	</permissions>

	<workground name="商品" id="b2c.wrokground.goods" controller="admin_goods" action="index" order="20">
		<menugroup name="商品管理">
		    <menu controller='admin_goods_editor' action='add' permission='goods' display='false' order='10'>添加商品</menu>
		    ...
		</menugroup>
		...
	</workground>
	...
</desktop>

desktop.xml不一定包含上面的所有标签,各个标签及属性含义如下:

  • desktop

    每个 desktop.xml 的根标签

  • panelgroup

    控制面板里的组其属性含义如下:

    1. id:控制面板里组的唯一标识

    2. icon: 控制面板里组的图标,路径默认的起点是app下的static,假如此desktop.xml是desktop这个app的,则图标的路径会自动解析成/app/desktop/static/panel-other.png

    3. order: 组的显示顺序

  • adminpanel

    控制面板里的项其属性含义如下:

    1. group: 指属性那个控制面板组,其值是panelgroup的id属性值加app前缀,例如group="desktop_other"

    2. permission: 指此控制面板里的项属性那个权限(下面讲到权限)

    3. controller: controller和action(构成点此控制面板项时的去处,如果action属性未写则默认值为index)

    4. action: controller和action(构成点此控制面板项时的去处,如果action属性未写则默认值为index)

    5. display: 是否显示在控制面板组里,true为显示,false为不显示

  • permissions

    权限被包括在它里面

  • permission

    包含在permissions标签里,每一个都是一个权限其属性含义如下:

    1. id: 权限的唯一标识,全局唯一

    2. display: 新建角色时,是否显示在角色权限列表里,true为显示,false为不显示

  • workground

    可以包含多个menugroup,看【图 desktop.png】其属性有:

    1.name: 1区显示出来的label

    2.id:(workground的唯一标识)

    3.controller: 构成点此workground tab时的去处,如果action属性未写则默认值为index

    4.action: 构成点此workground tab时的去处,如果action属性未写则默认值为index

    5.order: 此workground在tab列表中的排序

  • menugroup

    可以包含多个menu,看【图 desktop.png】 2区

  • menu

    看【图 desktop.png】为3区提供菜单,除了拥有跟workground一样的属性外,此标签还有另外两个属性:

    permission: 权限,为标签permission里的id属性的值

    display: 是否显示,有些控制器里的方法是不必显示成菜单的,比如得到post数据保存商品的控制器等,这时需把display设成false的

    先来看下,一个典型的desktop的列表页包含了哪些内容

    【图 desktop.png】

    接下来我们看各个区里的内容是如何增删及展示出来的

  • 1、2、3区都是根据app下的desktop.xml产生,如何填充,可以参照desktop.xml的介绍试试看
  • 5区顶部菜单产生是靠往服务id为desktop_menu里注册服务实现

    service.xml里添加

         <service id="desktop_menu">
            <class>b2c_service_view_menu</class>
        </service>
    

    b2c_service_view_menu内容如下

    class b2c_service_view_menu{
        function function_menu(){
            $shop_base = app::get('site')->router()->gen_url(array('app'=>'site', 'ctl'=>'default'));
            $html[] = "<a href='$shop_base' target='_blank'>浏览商店</a>";
            return $html;
    	
        }
    }
    
    说明:desktop_menu的服务,必须定义名字为function_menu的方法,它的返回值即为5区的菜单项

  • 4区finder,先看下细分图

    【图 finder.png】


    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,
    		'finder_aliasname'=>'xxxx',
                ));
        }
    
    后台的控制器必须继承desktop_controller,继承后才有finder方法,下面介绍下finder方法的几个参数:

  • 第一个参数是字符串,(上例中是b2c_mdl_goods),是model里的class名,它决定了finder列表的数据源,默认情况下是b2c_mdl_goods类里的getlist方法返回的数据
  • 第二个参数是数组,这个数组内涵相当丰富,解释如下:

    title: 【图 finder.png】中的1区显示出来的内容

    actions: 【图 finder.png】2区里的内容除了显示内置的操作以外(use_buildin_set_tag,use_buildin_filter这些是控制项),还可以自定义添加新操作,参照上面格式。

    allow_detail_popup: allow_detail_popup和其下面的其他项,是上面所说的内置的操作的控制项,其值为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: 是否显示标签管理操作

    下面几个控制项不是控制【图 finder.png】2区里的内容

    use_view_tab: 是否显示finder中的tab(如果有),有无需看控制器中是否有_views方法。

    use_buildin_filter: 是否使用高级赛选 【图 finder.png】6区

    use_buildin_refresh: 是否显示刷新操作(高级赛选旁)

    use_buildin_setcol: 是否显示列配置

    use_buildin_selectrow: 是否显示每条记录前的复选按钮

    allow_detail_popup: 是否显示查看列中的弹出查看图标(【图 finder.png】4区第二个图标)

    finder_aliasname: 此finder的别名,用于保存此finder的

    下面对几个重点区域内容的填充做详细讲解

  • 增加finder列表的自定义列

    finder列表的列分为以下三种:

    1.查看列 (分为下列查看和弹出查看)

    2.自定义列(也可称函数列),可以通过一种方法扩展

    3.普通列(数据表里有的字段,也即dbschema里有的字段)

    【图 finder.png】5区的两列操作和标签都不是dbschema里的字段,还能显示出来是因为他们是自定义列,通过下面的步骤实现:

    1.注册一个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>
    
    

    b2c_finder_goods类里有两种方法,两种属性,属性和方法成对出现:

    第一种 属性以detail_开头,对应的方法也以detail_开头 例如:

    	var $detail_basic = '基本信息';
    	function detail_basic($gid){
    		...
    		return $str;
    	}
    

    属性detail_basic是作为列头显示的,方法detail_basic的返回值是点击查看里出现的内容如果有多个detail_开头的方法,则显示第一个里面的内容

    第二种:属性以column_开头,对应的方法也以column_开头

    	var $column_editbutton = '操作';
    	public function column_editbutton($row)
    	{
    		...
    		return $str;
    	}
    

    属性column_editbutton是作为列头显示的,方法column_editbutton的返回值是每行此列的显示内容,方法column_editbutton的参数是当前行的数组。

    补充:自定义列后来又加了一个属性,属性名规则是列变量加_order 例如 var $column_editbutton_order = orderorder 可以是COLUMN_IN_HEAD[放在普通列前面]或COLUMN_IN_TAIL[放在普通列后面]

  • 高级赛选

    高级赛选中的搜索项大部分来自dbschema中,搜索类型单选或下拉或输入关键词也定义在dbschema

    除此之外,高级赛选中的搜索项还可以通过注册service扩展,id为extend_filter_xxx,xxx为finder方法的第一个参数,例如:

        <service id="extend_filter_b2c_mdl_orders">
            <class>b2c_finder_extend_orders</class>
        </service>
    
    

    b2c_finder_extend_orders 类如下:

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

    class里必须包含get_extend_colums方法,它的返回值跟dbschema里的一样,如果扩展了高级搜索,一般需要在model里重定义_filter方法,以便使用上扩展过滤字段

  • 快捷搜索

    有两个地方影响快捷搜索,dbschema中定义的字段中,searchtype这一项,如果有这项则会在快捷搜索里出现,还有就是在model里定义searchOptions函数,返回值的数组就为快速搜索里的项,例如:

        function searchOptions(){
            $arr = parent::searchOptions();
            return array_merge($arr,array(
                    'bn'=>__('货号'),
                    'keyword'=>__('商品关键字'),
                ));
        }
    
  • finder上的tab

    需要在调用了finder的控制器里定义_views方法,_views方法的返回值格式如下:

    $sub_menu = array(
                0=>array('label'=>app::get('b2c')->_('全部'),'optional'=>false,'filter'=>"",'addon'=>1,'href'=>'xxx.xxx','finder'=>'xxxx'),
    	    ...
                7=>array('label'=>app::get('b2c')->_('已作废'),'optional'=>false,'filter'=>array('status'=>'dead'),'addon'=>1,'href'=>'xxx.xxx'),
            );
    
    

    label: tab的标题文字

    optional: 此tab是否可选

    filter: 此tab的过滤条件

    addon: 此过滤条件下有多少条记录

    href: 此tab的链接地址

    回收站

    如果finder方法第二个参数中使用了use_buildin_recycle,则此finder列表的actions区就有了内置的删除按钮,有时需要在删除前和删除后做一些检测工作,比方记录不准删除,或删除记录时需删除资源文件等。实现这一功能机制是在model里定义pre_recycle删除前执行]和suf_recycle[删除后执行方法

    桌面

    登录后台首先看到的界面,其内容由各个app通过service注册进来,里面每一块都是一个wigets,下面是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就是一个wigets,每个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_title(): desktop widgets 内容

    function get_className(): 给desktop widgets 区块添加class name

    function get_width(): 返回值为l-1显示在左侧,值为l-2显示在左侧