页面创建

页面创建


总述

系统中绝大多数功能界面都对应一个功能代码,也叫事务码。事务码是一个字符串,事务码是权限验证的基础,这些事务码都是系统内定的,不可更改。

从开发者角度看,每一个业务功能界面包括两个文件:

  • 一个是XML界面布局文件,负责UI展示
  • 一个是对应的C++功能类,负责处理事件和实现功能

以销售单为例,其对应的XML文件为CDuiPageVa01LS.xml,对应的功能类为CDuiPageVa01LS。

为方便起见,要求XML布局文件名和C++类名一样。

事务码、XML布局、C++类绑定

  1. 通过宏绑定

    开发时,一般是通过CreateTranPage(_T("VA01"),_T("销售订单"))这样的方式启动创建一个功能页面展示出来,第一个参数为事务码,第二个参数为展示出来的描述,如下图:

    通过两个C++宏,完成三者的绑定并实现页面的动态创建:

    1. 在C++类的头文件中添加动态创建声明:SOCDECLARE_DYNCREATE(PageClass)
    2. 在C++类的实现文件中添加动态创建实现:SOCIMPLEMENT_DYNCREATE(PageClass, parentClass, TCODEPARAM, _T("xx\\PageClass.xml"))

    SOCIMPLEMENT_DYNCREATE有四个参数,第一个为C++类名,第二个为其父类名,第三个为事务码,第四个为绑定的XML文件名。

    对于同一个事务码,为了解决如下问题

    • 版本的不同级别界面不同,如T3版本要比T8版本简单,C++类和XML文件都不同,
    • 不同行业界面的C++类不同,如服装行业的类为CDuiPageVax1FZ,而通用行业的类为CDuiPageVax1LS,但使用的都是同一个XML文件

    1111在实现宏的第三个事务码参数TCODEPARAM中,采取三段式结构用来解决上述问题,结构为 TCODE:INDUSTRY:LEVEL,每段含义如下:

    • TCODE:有效的事务码。
    • INDUSTRY:行业代码,如FZ、CM(通用行业)、XC(鞋材行业)、XY(鞋业)等,多个行业之间用空格分开。FZ代表支持颜色尺码的所有行业,包括服装、鞋业、鞋材,鞋业的代码为XY,鞋材的代码为XC,所以FZ包含了XY、XC,如果行业代码设为XY,则只在鞋业版本有效,XC行业无效,软件的行业在编译阶段确定的,也就是调用CGuiVersion::InitGuiVersionEx()时指定了行业和级别。
    • LEVEL:版本级别代码,如V3、V6、V8、T3、T6、T8等,ERP代表 T0-T8 E3-E8等ERP版本,JXC代表V3-V8等进销存版本,多个版本级别之间用空格分开。

    其中后两个字段可以使用通配符*,比如MMF1:*:T3代表任何行业的T3版。MMF1:FZ:*代表服装行业的任何级别版本,其实现逻辑在CDuiTranPageManager::DYNCreatePageControl中。

    注意,如果级别使用了通配符而指定了行业代码,如MMF1:FZ:*,则最好对事务码做一个数据库层面的隔离,比如MMF1只能是ERP系统的事务码,而不是进销存的事务码,如果不从数据库层面进行隔离,那么MMF1:FZ:*可能也会在服装行业的进销存中出现,所以要在实施指南的事务码配置中,把MMF1配置为只在ERP中可见。

    所以,对ERP和进销存的同一个功能:

    • 如果界面差别较大,则采用不同的事务码,比如物料管理MA01和MMF1就分别属于进销存和ERP。
    • 如果界面一样可以使用相同的事务码,比如很多报表。
    • 如果界面不同,又想使用同一个事务码,只能采取如下通过代码绑定的方式,如库存查询MMBE。

    优先级

    一个事务码,因为有不同行业、不同版本,所以可能有多个界面类和XML布局文件对应,如下:

    行号 事务码
    1 MMR1
    2 MMR1:*:T1
    3 MMR1:FZ XC:*
    4 MMR1:FZ:*
    5 MMR1:FZ:V8
    6 MMR1:FZ:V8 T1

    匹配原则是精确优先,比如对于服装行业,3、4、5、6行优先考虑,然后再根据版本级别匹配,如果是T1版,自然选择第6行,如果是T3版呢,则3、4行都是可能项,这时就可能随机选择一个了,为避免出现这种情况,代码中尽量不要出现3、4这样都支持服装行业,但是版本级别是通配符的写法。

    算法

    程序扫描同一个事务码的多个不同表达字符串,根据当前编译的行业按照如下方式优先级降序排列

    • 字符串中行业包含目标行业
    • 字符串中行业为通配符*
    • 字符串中没有指定行业

    通过以上处理,可能会出现三个集合,然后对每个集合根据当前编译的版本级别再次分别按照如下方式优先级降序排列

    • 字符串中版本级别包含目标级别
    • 字符串中版本级别为通配符*
    • 字符串中没有指定版本级别

    通过以上两次排序后,根据编译的行业和版本从上到下扫描,第一个满足条件的就匹配成功。

  2. 通过代码绑定

    页面创建的流程如下:

    用户点击功能按钮 -> 发送事务码给CMainFrame -> 根据事务码找到对应的XML布局文件 -> 根据XML配置文件创建一个C++功能类对象和一个DuiLib::CControlUI对象并将两者绑定在一起以实现消息接收 -> 展示界面

    • 事务码到XML布局文件的映射

    CTrancodeManager的InitTCMap函数中维护了映射关系,如下:

    • 根据XML布局文件绑定C++功能类

    在CTrancodeManager的CreatePageControl函数创建功能类并进行绑定,绑定之后,用户界面的消息事件将转发到C++功能类中:

UI框架


CMainFrame

CMainFrame是一个Windows窗口,是DuiLib的主窗口,后面的界面大都是在该窗口上完成绘制。其对应的XML文件为mainframe.xml。

该类首先初始化一个导航控制器:m_navController.Init(CTrancodeManager::GetInstance(),this,&m_PaintManager,pTL,NULL);。

然后导航控制器push进去第一个展示的登录页面;

登录成功后,在登录的C++类CDuiPanelLogon::OnLogonSucceed()中把主界面push进去显示出来