界面效果 :


分析表单布局的概念:
第一小节,我们已经介绍过布局的概念,这里以图再说明如下:

(假如对维度不进行分组)

(上图中,分组的情况有7个布局 (不含页面、视点相关布局))
页面、视点可以看成另外隐藏在表单背后的维度的行、列(不是准确描述)。
其余参考我前面写的:非税olap分析平台的设计与实现(一)--数据仓库模型中,相关内容。
整体而言,查询分为三个层次,1)布局类型上的查询、2)特定布局类型、特定组上的查询 3)特定布局查询
获取特定表单 特别布局类型上的List<List<List<DimMember>>> getFormMbrsAsLayoutGroup(formId,layoutType...);
获取特定表单 特别布局类型 特定组 List<List<DimMember>> tempResult = getFormMbrsAsLayout(formId,layoutType,groupId......);
特定布局上的成员的查询:
这样分2种情况:
List<DimMember> layoutMbrs =getMemberWithQueryType(...) 这种情况不展开查询函数;
List<DimMember> layoutMbrs =getMembersByQueryType(...) 这种情况展开查询函数;
实现(由顶层到细节):
1、取得行/列上的集合:
List<List<List<DimMember>>> dimMembers_col = getFormMbrsAsLayoutGroup(form, MConstants.LAYOUT_COL, true);
2、取得一个布局类型的组上的集合,这里布局类型指的是行、列、页面、视点,关键参数为 form、维度类型、组号:
List<List<DimMember>> tempResult = getFormMbrsAsLayout(form,
layoutType, groupPosition, isOpenQueryType);
3、取得一个具体布局类型上 特定组的的布局,由具体布局,获得dimession,.另外一个方法是:考虑到所有组的维度dimesssion 是否也可以根据行列类型,获取所有组的dimession,关键参数为 form、维度类型、组号
List<CFormLayout> getAxisFormLayoutList(int formId,
int groupPosition, int layoutType)
4、上一步获取到的是 :一个特定表单、特定布局类型、特定组上布局信息,考虑到在一个表单 的特定布局上,所有组的类 布局信息是一样的,应该也有其他办法获取相关数据。现在遍历上一步获取到的布局信息,获取一个组内的所有维度成员信息。
List<List<DimMember>> mbrs = new ArrayList<List<DimMember>>();
......
List<DimMember> layoutMbrs = new ArrayList<DimMember>();
for (int i = 0; i < layouts.size(); i++) {
//取得一个特定布局(特定组、特定组内特定的布局)的成员集合List<DimMember>
layoutMbrs = getLayoutMbrs(form.getCubeId(), layouts.get(i),
isOpenQueryType);
mbrs.add(layoutMbrs);
}
5、获取一个布局上,具体维度成员集合:
layoutMbrs = getLayoutMbrs(form.getCubeId(), layouts.get(i),
isOpenQueryType);
这一步,逻辑略复杂。
首先是要获取特定布局上一个维度成员的集合,然后对这个结果进行相关处理::
List<CFormMbr> formMbrs = getFormLayoutMembersByLayoutId(layout.getId());
6、getFormLayoutMembersByLayoutId方法中,通过调用getFormLayoutMembersByLayoutId获取维度成员集合,getFormLayoutMembersByLayoutId方法的实现:
/**
* 通过布局ID获取布局成员列表
*
* @param layoutId
* @return
*/
@Override
public List<CFormMbr> getFormLayoutMembersByLayoutId(int layoutId) {
List<CFormMbr> cFormMbrs=new ArrayList<CFormMbr>();
//方法一 :从数据库当中获取数据
//CFormMbr fomMmeber = new CFormMbr();
// fomMmeber.setLayoutId(layoutId);
// Example<CFormMbr> example = Example.of(fomMmeber);
// List<CFormMbr> formMembers=formMbrMapper.findAll(example);
// return formMembers;
//方法二:从缓存当中获取这个数据
try {
//cFormMbrs=(List<CFormMbr>)cacheHelp.getObjectInCache(Constants.C_FORMMbr,new C_FormMbr_layoutId_PK(false),layoutId);
cFormMbrs=(List<CFormMbr>)cacheHelp.getObjectInCache(CFormMbr.class,new C_FormMbr_layoutId_PK(false),layoutId);
} catch (CacheException e) {
e.printStackTrace();
}
return cFormMbrs;
}
7.getFormLayoutMembersByLayoutId方法中,对特定布局的成员信息(CFormMbr),进行遍历处理:
for (int i = 0; i < formMbrs.size(); i++) {
boolean isCompare = false; //?
DimMember member = null;
memberId = formMbrs.get(i).getMemberId();
queryType = formMbrs.get(i).getQueryType();
if (isOpenQueryType) { //展开的情况
if (formMbrs.get(i).getType() == MConstants.FORM_MBR_TYPE_VAR) {
//todo
.....
} else {
tempMembers = memberEval.getMembersByQueryType(queryType, memberId, cubeId, layout.getDimId());
if (tempMembers != null && tempMembers.size() > 0) {
Iterator<DimMember> memberIterator = tempMembers.iterator();
while (memberIterator.hasNext()) {
DimMember beAddM = memberIterator.next();
boolean isHas = false;
for (DimMember temp : members) {
if (temp.getMemberId() == beAddM.getMemberId()) {
isHas = true;
break;
}
}
if (isHas) {
memberIterator.remove();
}
}
}
members.addAll(tempMembers);
}
} else { //closed node
.....
}
......
}
8.下面,我们只讨论节点是全展开,且是普通节点的情况,关键是下面的的方法,根据memberId 和 query Type,返回相应节点集合,如这个节点quey type是“子代包含”则把相应节点全部返回。
tempMembers = memberEval.getMembersByQueryType(queryType, memberId, cubeId, layout.getDimId());
9.memberEval.getMembersByQueryType方法中,首先要把CFormMbr转为DimMember对象:
DimMember curMember=getMemberInDimByMemberId(memberId, dimId);
具体实现(方法一:从数据库中查的方式):
/**
* 获取维度成员
* @param memberId
* 维度成员ID
* @param dimId
* 维度ID
* @return
*/
@Override
public DimMember getMemberInDimByMemberId(int memberId, int dimId) {
DimMember object=null;
CDimension dimension = getDimInstanceById(dimId);
int obj_type=dimension.getObjType();
switch (obj_type) {
case MConstants.DIM_ACCOUNT:
object =accountMapper.getOne(memberId);
break;
case MConstants.DIM_ENTITY:
object =entityMapper.getOne(memberId);
break;
case MConstants.DIM_YEAR:
object =yearMapper.getOne(memberId);
break;
case MConstants.DIM_PERIOD:
object =periodMapper.getOne(memberId);
break;
case MConstants.DIM_VERSION:
object =versionMapper.getOne(memberId);
break;
case MConstants.DIM_SCENARIO:
object=scenarioMapper.getOne(memberId);
break;
case MConstants.DIM_CURRENCY:
object =curencyMapper.getOne(memberId);
break;
}
return object;
}
从缓存中查的方式:略
构建formGrid,最后重要形成三个数组,一个描述行标表头部分,一个描述列表头部分,一个描述表格数据区部分。行标题背后是行维,列表头后面是列维,表格区背后是事实数据。
前端表单模型定义:
constructor(props) {
super(props)
this.state = {
formFolders: [], //表单文件夹 左边表单数的数据
selectedFolderId: -1,
selectedFoldName: "", //文件夹名称 新增加的时候 设置为""
modifyFoldName: "", //传给编辑框--文件夹名称
modalTitle: '',
isModalVisible: false,
selectedFormId: -1, //选中的表单id 弃用?
selectedFormIds: [], //选中的表单id 数组 存放的是key
isModalVisibleForFold: false, //文件夹
status: -1, //
forms: [], //表单
isModalVisibleForForm: false, //表单编辑
isModalVisibleForSelDimMbr: false,//选择维度成员
//下面是表单详细
//formId:-1, 用selectedFormId代替
formId: 0, //传递到Modal当中
formName: "", //报表名称
reportDesc: "", //报表描述
remark: "", //说明
scale: 2,
// dims:[],
colDims: [],
rowDims: [],
pageDims: [],
viewDims: [],
dims_option: [],
dims_all: [],
rowLayouts: [],
colLayouts: [],
pageLayouts: [],
viewLayouts: [],
// colMembers:[],
// rowMembers:[]
treeData_forSelDimMbr: [], //供选择的维度成员
targetFormLayout_Mbrs: [], //target dimession member
selectedMbr: {}, //选中的维度成员
selectedFunId: 0, //选中的方法
funList: [
'成员',......
],
layout_type: 0, //表单tab
group_no: 0,//布局的组编号
index_layoutMbr: 0, //index_dims 一个具体布局的index 这个名字没起好
formDefStatus: -1 //表单状态呢 0 是编辑 1是新增
}
}
创建一个表单要完成的工作:
根据前端数据, 设置List<List<List>>
搞个modal 创建弹出
保存成功后,是从后端刷还是从前端直接增加数据?我是从后端重新查询一次。
涉及的实体:
- c_form
- 布局:CFormLayout
CFormMbrC_Description
为简化问题 暂时不管别名
表单的类别管理:
用antd 树实现:
{
"code": "200",
"data": {
"id": "-1",
"text": "全部",
"state": {
"opened": true
},
"checked": true,
"children": [
{
"key": 40441,
"id": "40441",
"text": "数据分析",
"title": "数据分析",
"checked": false,
"children": [
{
"key": 40442,
"id": "40442",
"text": "湖北非税",
"title": "湖北非税",
"checked": false,
"parentId": "40441",
"hasParent": true,
"hasChildren": false,
"btn": false,
"levelnum": 0
}
],
"parentId": "0",
"hasParent": false,
"hasChildren": true,
"btn": false,
"levelnum": 0
}
],
"parentId": "",
"hasParent": false,
"hasChildren": true,
"btn": false,
"levelnum": 0
}
}
校验:校验所有维度 是否在表单中存在
Description 表中,就id及string,表的描述及编制说明都用这个表,但id不同,form对象需要记住这2个id
getForms(formfolderId)
前端传给后端的是dimId,不是dimession.
/**
* 以下常量对应com_FORMLAYOUT.LAYOUT_TYPE
*/
static public final int LAYOUT_VP = 0; //视点
static public final int LAYOUT_PAGE = 1; //页面
static public final int LAYOUT_ROW = 2; //行
static public final int LAYOUT_COL = 3; //列
表单设计相关动作:
行维度删除:
1、 删除state当中的值
2、添加到可选择的维度集合
dims_option:[] 这个当中包含可选择的维度
删除维度用以下方法:
const delDim=(dimId)=>{
console.log("delete dimession id:"+dimId);
}
<List.Item
actions={[<Button key="list-dimession-delete"
icon={<DeleteRowOutlined/>}
shape="circle"
onClick={delDim(item.id)}
/>]}>{item.name}
</List.Item>
查询表单返回(getForm(formId))
{
"code": "200",
"data": {
"id": 600148,
"cubeId": 0,
"name": "测试表单001",
"desc": "表单描述测试001",
"remark": "说明",
"scale": 0,
"folderId": 0,
"rowLayout": {
"dims": [
111,
112
],
"layouts_xy": [
{
"position": 0,
"groupName": "",
"theLayouts": [
{
"position": 0,
"theLayoutName": "",
"formLayout_Mbrs": [
{
"id": 600076,
"memberId": 0,
"queryType": 0,
"type": 0,
"name": "jj001"
}
]
},
{
"position": 0,
"theLayoutName": "",
"formLayout_Mbrs": [
{
"id": 420201,
"memberId": 0,
"queryType": 0,
"type": 0,
"name": "黄石市市本级"
}
]
}
]
}
]
},
"colLayout": {
"dims": [
113,
114
],
"layouts_xy": [
{
"position": 0,
"groupName": "",
"theLayouts": [
{
"position": 0,
"theLayoutName": "",
"formLayout_Mbrs": [
{
"id": 60198,
"memberId": 0,
"queryType": 0,
"type": 0,
"name": "2012年"
}
]
},
{
"position": 0,
"theLayoutName": "",
"formLayout_Mbrs": [
{
"id": 60004,
"memberId": 0,
"queryType": 0,
"type": 0,
"name": "3月"
}
]
}
]
}
]
},
"pageLayout": {
"dims": [
116,
117
],
"layouts_pv": [
{
"position": 0,
"theLayoutName": "",
"formLayout_Mbrs": [
{
"id": 60256,
"memberId": 0,
"queryType": 0,
"type": 0,
"name": "实际"
}
]
},
{
"position": 0,
"theLayoutName": "",
"formLayout_Mbrs": [
{
"id": 60250,
"memberId": 0,
"queryType": 0,
"type": 0,
"name": "战略制定"
},
{
"id": 60251,
"memberId": 0,
"queryType": 0,
"type": 0,
"name": "编制版本"
}
]
}
]
},
"viewLayout": {
"dims": [
115
],
"layouts_pv": [
{
"position": 0,
"theLayoutName": "",
"formLayout_Mbrs": [
{
"id": 60163,
"memberId": 0,
"queryType": 0,
"type": 0,
"name": "人民币"
}
]
}
]
},
"colDims": [
{
"id": 113,
"name": "年",
"objType": 3,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 3,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 2
},
{
"id": 114,
"name": "期间",
"objType": 4,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 4,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 2
}
],
"rowDims": [
{
"id": 111,
"name": "科目",
"objType": 1,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 0,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 1
},
{
"id": 112,
"name": "组织",
"objType": 2,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 2,
"lastModifyTime": "2018-06-12T14:35:09.000+0000",
"usedIn": 7,
"type": 1
}
],
"pageDims": [
{
"id": 116,
"name": "场景",
"objType": 6,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 6,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 1
},
{
"id": 117,
"name": "版本",
"objType": 7,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 7,
"lastModifyTime": "2018-06-18T14:14:47.000+0000",
"usedIn": 7,
"type": 1
}
],
"viewDims": [
{
"id": 115,
"name": "币别",
"objType": 5,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 5,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 1
}
],
"description": 0,
"setFormShuoMingId": 0,
"approval": 0,
"showway": 0
}
}
去掉rowLayout 下的dims[] 相关元素方法:
遍历layouts_xy下每个组theLayouts 的特定位置上的布局,其实是根据删除dimession的index确认的数据。
位置(position):
up、down
1、必须有position信息,up、down
移动位置:
//移动位置
const moveUp_dim = (index) => {
if (index === 0)
{
message.info("已经首部,不能再上移。")
return;
}
let _rowDims = this.state.rowDims;
let _rowLayouts = this.state.rowLayouts;
[_rowDims[index - 1], _rowDims[index]] = [_rowDims[index], _rowDims[index - 1]];
_rowLayouts.map(g => {
[g.theLayouts[index - 1], g.theLayouts[index]] = [g.theLayouts[index], g.theLayouts[index - 1]];
});
this.setState({rowDims: _rowDims, rowLayouts: _rowLayouts})
}
动态添加组的办法:
在xy中直接增加一组数据
- 加入一个组
- 组下面加布局信息
select_dim_mbr pareameter is:
选择的维度成员值添加到右边的集合当中:

根据选择的节点构造对象ojbect,
把object存放到state。
目标是:
this.state.targetFormLayout_Mbrs
let _o={
id:this.state.selectedMbr.id,
name:this.state.selectedMbr.name,
queryType:this.state.selectedFunId
}
右边必须构建对象!!!
let _o={
id:this.state.selectedMbr.id,
name:this.state.selectedMbr.name,
queryType:this.state.selectedFunId
}

当你能够点击ok的时候,你应该已经知道:
- 布局类型(行、列等)
- 组号
- layoutMbr 的index
(维度id 已知道 但是这里没啥用吧)
删除 :就是从数组当中删除 ,然后重新setstate

点击确定时候,其实是把target 重新赋予给......
点击选择按钮时,时了解gourp_no 等数值的。
创建表单:
//创建表单
openFormModalForCreating = () => {
if (this.state.selectedFolderId === undefined || this.state.selectedFolderId === ""
|| this.state.selectedFolderId == "-1" || this.state.selectedFolderId == -1
|| this.state.selectedFolderId == null) {
message.info("请选择一个节点!")
return
}
getDims().then(
(res) => {
this.setState({
formId: 0,
formName: "",
remark: "",
reportDesc: "",
scale: 2,
// formId:res.data.id,
colDims: [],
rowDims: [],
pageDims: [],
viewDims: [],
dims_option: res.data, //
rowLayouts: [{
"position": 0,
"groupName": "",
"theLayouts": []
}], //组的集合
colLayouts: [{
"position": 0,
"groupName": "",
"theLayouts": []
}], //组的集合
pageLayouts: [], //页面
viewLayouts: [], //view
isModalVisibleForForm: true,
formDefStatus: 1 //new create status
});
},
(error) => {
console.log("get response failed!");
}
);
};
dims:
{
"code": "200",
"data": [
{
"id": 111,
"name": "科目",
"objType": 1,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 0,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 1
},
{
"id": 112,
"name": "组织",
"objType": 2,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 2,
"lastModifyTime": "2018-06-12T14:35:09.000+0000",
"usedIn": 7,
"type": 1
},
{
"id": 113,
"name": "年",
"objType": 3,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 3,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 2
},
{
"id": 114,
"name": "期间",
"objType": 4,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 4,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 2
},
{
"id": 115,
"name": "币别",
"objType": 5,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 5,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 1
},
{
"id": 116,
"name": "场景",
"objType": 6,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 6,
"lastModifyTime": "2018-06-12T14:19:41.000+0000",
"usedIn": 7,
"type": 1
},
{
"id": 117,
"name": "版本",
"objType": 7,
"parent": 0,
"generation": 0,
"hasChildren": 0,
"description": 0,
"queryType": 0,
"formulaType": 0,
"position": 7,
"lastModifyTime": "2018-06-18T14:14:47.000+0000",
"usedIn": 7,
"type": 1
}
]
}
ps:一个维度可以应用于多个cube,cube可以理解为分析模型,数据集市
ps:Ant Design - 可展开表中 Data 必须携带 key
构建与解析OLAP分析表单:维度布局与数据查询
本文详细介绍了如何构建和解析在线分析处理(OLAP)分析表单,包括布局概念、查询层次、布局类型的查询、成员获取以及表单数据的处理。在表单设计中,涉及行、列、页面和视点布局,通过不同的维度成员组合形成复杂的数据结构。同时,文章提到了前端和后端交互,如数据校验、维度成员的增删以及表单状态的管理。此外,还涵盖了缓存机制、数据验证以及表单创建和编辑的相关操作。
3317

被折叠的 条评论
为什么被折叠?



