关于多分项条件组合问题的解决

由于当前项目的需求,需要一个针对多级分项排列组合的处理程序,工程师由于项目周期比较紧张,无法费时间研究,就甩到我这边了。


整体需求是这样的,首先需要一个多级分项的树形结构(前台页面)前台代码如下。

<html>
<head>
<script src="bootstrap/js/jquery.js"></script>
<script>
//监听li中的添加按钮
$(document).on('click','#itemlist a.itemadd',function(){

	var parent = $(this).parent();
	var newItem = $("<li><input type='checkbox'><span id='title'></span><a class='itemadd' href='#'>+</a>  <a class='itemdel' href='#'>-</a><ul></ul></li>");
	parent.find('ul').first().append(newItem);
	var index = newItem.index() + 1;
	var levelIndex = parent.attr('index') + index;
	newItem.attr('index',levelIndex);
	newItem.find('#title').text('item:'+levelIndex);
});
//监听li中的删除按钮
$(document).on('click','#itemlist a.itemdel',function(){
	var parent = this.parentNode;
	parent.remove();
});
//监听添加根ul的添加按钮
$(document).on('click','#rootadd',function(){
	var newRoot = $("<ul><li index=''><input type='checkbox'><span id='title'>item:</span><a class='itemadd' href='#'>+</a>  <a class='itemdel' href='#'>-</a><ul></ul></li></ul>");
	$('#itemlist').append(newRoot);
	var index = newRoot.index() + 1 +"";
	newRoot.find('li').attr('index', index);
	newRoot.find('#title').text('item:'+index);

});
//监听遍历所有可能性按钮
$(document).on('click','#traversal',function(){
	var items = {};//初始化要回传的数据
	items['returnRequired'] = $('#returnRequired')[0].checked ? 1 : 0;
	items['items'] = {};
	//在itemlist容器中便利所有li对象
	$('#itemlist').children('ul').each(function(index,child){
		//console.info(travesal($(child)));
		items['items'][index] = travesal($(child))[0];//回传数据赋值
	});
	//console.info(items);
	//向服务器提交数据进行处理
	$.post('itemshandler.php', items, function(data, status){
		//to do something here;
		var skusigns = $.parseJSON(data);
		$('#skucount').html(skusigns.length);
		var returnHtml = "<ul>";
		$.each(skusigns,function(index,skusign){
			returnHtml += "<li>"+skusign['skusign']+"</li>";
		});
		returnHtml += "</ul>";
		$('#skulist').html(returnHtml);

	});
});
//递归遍历ul节点
function travesal(obj){
	//console.info(obj);
	var arr = {};//初始化数组
	//判断根节点是否为UL
	if(obj.is('UL')){
		//遍历所有li子元素
		obj.children('li').each(function(index,child){
			arr[index] = {};//初始化数组
			arr[index]['text'] = $(child).children('#title').text();//获取li中的元素并加到数组中去
			arr[index]['required'] = $(child).children('input:checked').length;
			arr[index]['child'] = travesal($(child).children('ul'));//递归下一节点数据
		});
		return arr;
	}
}
</script>
</head>

<body>
<a href="#" id="rootadd">添加项目</a>
是否遍历必选项<input id="returnRequired" type="checkbox">
<a href="#" id="traversal">生成服务项目</a>
<div id="itemlist">

</div>
<span id="skucount"></span>
<div id="skulist">
</div>
</body>
</html>

以上代码作用只是可以前台动态维护属性list结构。

每个LI中有一个checkbox,选中的checkbox在业务中的逻辑为必选项,也就是排列组合的时候必须有他,目标就是将前台收成的树形数据对象post给php代码,由php代码负责将树中的所有必要项与可选项的组合遍历出来,并输出。

以下是php代码

<?php

	$items = $_REQUEST;
	//var_dump($items);
	echo json_encode(getSkuItems($items['items'],$items['returnRequired']));
	/*
	 * 功能描述:将传入的分项列表根据其必选条件递归遍历所有可能组合行程sku标准,并输出数组。
	 * @param: $items 传入的分项节点,数据必须有索引
	 * @param: $returnRequired default false 是否仅返回必选项的组合。
	 * @param: $hasParent default false 传入的分项节点是否有父节点。
	 * @return: array();
	 * by eric
	 */
	function getSkuItems($items, $returnRequired = false, $hasParent = false){
		$hasChild = true;
		$skuItems = array();//初始化sku
		$itemContainer = array();//创建分项容器,用于区分必选与可选项
		//开始遍历分项数据
		foreach($items as $index => $item){

			$itemType = $item['required']? 'requiredItems': 'optionalItems';//判断是否为必选项

			if(isset($item['child'])){//判断是否有子节点
				//递归遍历所有子节点的分项
				foreach(getSkuItems($item['child'], $returnRequired, true) as $skuItem){
					switch($itemType){
						case 'requiredItems':
							//必选项需要在根节点上合并,故单独设置index,结构与可选项不一样
							//在这里可以进行相应skusign的设置,目前使用text代替。
							$itemContainer[$itemType][$index][] = array('skusign' => $item['text'].",".$skuItem['skusign']);
							break;
						case 'optionalItems':
							$itemContainer[$itemType][] = array('skusign' => $item['text'].",".$skuItem['skusign']);
							break;
						default:
							break;
					}
				}
			}else{
				switch($itemType){
					case 'requiredItems':
						$itemContainer[$itemType][$index][] = array('skusign' => $item['text']);
						break;
					case 'optionalItems':
						$itemContainer[$itemType][] = array('skusign' => $item['text']);
						break;
					default:
						break;
				}
				$hasChild = false;
			}
		}
		//整理可选项
		if($hasParent){//如果有父节点则需要则不需要考虑组合的合理性,直接为可选项设置空值
			if(!$hasChild && isset($itemContainer['requiredItems'])){//如果没有子节点且同级下没有必选项的时候给optional增加空记录,允许不选的可能出现。
				$itemContainer['optionalItems'][] = array('skusign' => '');
			}else{//由于不可以不选,所以此级节点的数据将被视为必须处理节点对待。
				$returnRequired = false;
			}
		}else{
			$itemContainer['optionalItems'][] = array('skusign' => '');
		}
		//整理必选项
		if(isset($itemContainer['requiredItems'])){//如果设置了必选项,则通过itemComb方法进行组合处理
			$itemContainer['requiredItems'] = itemsComb($itemContainer['requiredItems']);
		}else{//否则设置一个空值
			$itemContainer['requiredItems'][] = array('skusign' => '');
		}

		//将必选项与可选项进行组合
		foreach($itemContainer['requiredItems'] as $rItem){
			if(isset($itemContainer['optionalItems']) && !$returnRequired){//如果同级有可选项,且返回必选参数为是否的时候遍历optional项目列表
				foreach($itemContainer['optionalItems'] as $oItem){
					$sperate = "";
					if(!empty($rItem['skusign']) && !empty($oItem['skusign']))
						$sperate = ",";
					$newItems = array('skusign' => $rItem['skusign'].$sperate.$oItem['skusign']);
					$skuItems[] = $newItems;
				}
			}else{
				$newItems = array('skusign' => $rItem['skusign']);
				$skuItems[] = $newItems;
			}
		}

		return $skuItems;
	}
	/*
	 * 功能描述:通过递归的方法将分项进行排列组合
	 * @param:$items array() 需要排列组合的分项数组,必须有键值
	 * @return:array
	 * by eric
	 */
	function itemsComb($items){


		if(count($items) == 1){//只有一个分项表的时候直接返回分项表
			foreach($items as $item){
				return $item;
			}
			return null;
		}

		$returnArr = array();
		if(count($items) == 2){//有两个分项表的时候进行依次的排列组合
			$newItems = array();

			foreach($items as $item){
				$newItems[] = $item;
			}

			foreach($newItems[0] as $item0){
				foreach($newItems[1] as $item1){
					$returnArr[] = array('skusign' => $item0['skusign'].",".$item1['skusign']);
				}
			}

			return $returnArr;
		}

		if(count($items) > 2){//大于两个分项表的时候抽取一个表其他的递归
			$tempItems = array();
			$isFirst = true;
			foreach($items as $item){
				if($isFirst){
					$newItems[] = $item;
					$isFirst = false;
				}else{
					$tempItems[] = $item;
				}
			}

			$newItems[] = itemsComb($tempItems);
			return itemsComb($newItems);
		}

		return null;
	}


?>

由于好久没有研究代码了,以上代码用了我整整一天时间,实在惭愧,还是分享出来。


另外,手头事情太多,没有时间继续琢磨了,看看能不能有高手帮我实现仅将必要项的第一个组合抽取出来,并不遍历所有组合。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值