本文转自:http://blog.csdn.net/eadio/article/details/52913363
前瞻
目前学习小程序更多的是看看能否二次封装其它组件,利于以后能快速开发各种小程序应用。目前发现picker的selector模式只有一级下拉,那么我们是否可以通过3个picker来实现三级联动模板的形式来引入其它页面中呢?答案是肯定可以的。那么我的思路是这样的:
1、使用template模板语法进行封装,数据从页面传入
2、根据picker组件的语法,range只能是一组中文地区数组,但是我们需要每个地区的唯一码来触发下一级联动数据。这样,我的做法是通过一个对象里面的两组数据分表存储中文名和唯一码的两个对象数组。格式【province:{code:['110000', '220000'...], name: ['北京市', '天津市'...]}】,这个格式是固定的,需要服务端配合返回
3、通过picker的bindchange事件来获取下一级的数据,每个方法都写入函数中在暴露出来供页面调用
然后讲下我demo的目录结构:
common
-net.js//wx.request请求接口二次整合
-cityTemplate.js//三级联动方法
page
-demo
-demo.js
-demo.wxml
template
-cityTemplate.wxml
app.js
app.json
app.wxss
然后,使用phpstudy搭建了简单的服务端供测试。不要问我服务端的为啥是这样的,我也不懂,刚入门我只要数据...
当然你可以省掉这一步,将数据直接固定在demo.js里面进行测试...
代码如下:【服务端的返回数据格式是遵循了下面的retArr的规范的】
<?php
header("Content-type: text/html; charset=utf-8");
$type=$_REQUEST["type"];//获取省市区的标志
$fcode=$_GET["fcode"];
$retArr=[
"status"=>true,
"data"=>[],
"msg"=>""
];
if($type!="province" && $type!="city" && $type!="county"){
$retArr["status"]=false;
$retArr["msg"]="获取地区类型错误,请检查";
echo json_encode($retArr);
exit;
}
function getProvince(){
$province=[];
$code=["110000", "350000", "710000"];
$province["code"]=$code;
$name=["北京市", "福建省", "台湾省"];
$province["name"]=$name;
$fcode=["0", "0", "0"];
$province["fcode"]=$fcode;
return $province;
}
function getCity($P_fcode){
$city=[];
$code=[];
$name=[];
$fcode=[];
if($P_fcode=="110000"){
$code=["110100"];
$name=["北京市"];
$fcode=$P_fcode;
}
if($P_fcode=="350000"){
$code=["350100", "350200", "350300", "350400", "350500", "350600", "350700", "350800", "350900"];
$name=["福州市", "厦门市", "莆田市", "三明市", "泉州市", "漳州市", "南平市", "龙岩市", "宁德市"];
$fcode=$P_fcode;
}
if($P_fcode=="710000"){
}
$city=["code"=>$code, "name"=>$name, "fcode"=>$fcode];
return $city;
}
function getCounty($P_fcode){
$county=[];
$code=[];
$name=[];
$fcode=[];
if($P_fcode=="110100"){
$code=["110101", "110102", "110103", "110104", "110105", "110106", "110107"];
$name=["东城区", "西城区", "崇文区", "宣武区", "朝阳区", "丰台区", "石景山区"];
$fcode=$P_fcode;
}
if($P_fcode=="350100"){
$code=["350102", "350103", "350104"];
$name=["鼓楼区", "台江区", "苍山区"];
$fcode=$P_fcode;
}
if($P_fcode=="350200"){
$code=["350203", "350205", "350206"];
$name=["思明区", "海沧区", "湖里区"];
$fcode=$P_fcode;
}
$county=["code"=>$code, "name"=>$name, "fcode"=>$fcode];
return $county;
}
//var_dump($province);
if($type=="province"){
$province=getProvince();
$retArr["data"]=$province;
}else if($type=="city"){
$city=getCity($fcode);
$retArr["data"]=$city;
}else if($type="county"){
$county=getCounty($fcode);
$retArr["data"]=$county;
}
echo json_encode($retArr);
?>
cityTemplate.js::
/**
* 获取三级联动的三个函数
* that: 注册页面的this实例 必填
* p_url: 一级省份url 必填
* p_data:一级省份参数 选填
*/
var net = require( "net" );//引入request方法
var g_url, g_datd, g_cbSuccess, g_cbSuccessErr, g_cbFail, g_cbComplete, g_header, g_method;
function initCityFun( that, p_url, p_data ) {
//获取一级省份数据
console.log(p_url+JSON.stringify(p_data));
g_cbSuccess = function( res ) {
that.setData( {
'city.province': res
});
};
net.r( p_url, p_data, g_cbSuccess, g_cbSuccessErr, g_cbFail, g_cbComplete, g_header, g_method );
//点击一级picker触发事件并获取市区方法
var changeProvince = function( e ) {
that.setData( {
'city.city': {},
'city.county': {},
'city.provinceIndex': e.detail.value,
'city.cityIndex': 0,
'city.countyIndex': 0
});
var _fcode = that.data.city.province.code[ e.detail.value ];
if( !_fcode ) {
_fcode = 0;
}
var _cityUrl = e.target.dataset.cityUrl;
g_url = _cityUrl + _fcode;
console.log("province:"+g_url);
g_cbSuccess = function( res ) {
console.log(res);
that.setData( {
'city.city': res
});
}
net.r( g_url, g_datd, g_cbSuccess, g_cbSuccessErr, g_cbFail, g_cbComplete, g_header, g_method );
};
that[ "provincePickerChange" ] = changeProvince;
//点击二级picker触发事件并获取地区方法
var changeCity = function( e ) {
that.setData( {
'city.county': {},
'city.cityIndex': e.detail.value,
'city.countyIndex': 0
});
var _fcode = that.data.city.city.code[ e.detail.value ];
if( !_fcode ) {
_fcode = 0;
}
var _countyUrl = e.target.dataset.countyUrl;
g_url = _countyUrl + _fcode;
g_cbSuccess = function( res ) {
that.setData( {
'city.county': res
});
};
net.r( g_url, g_datd, g_cbSuccess, g_cbSuccessErr, g_cbFail, g_cbComplete, g_header, g_method );
};
that[ "cityPickerChange" ] = changeCity;
//点击三级picker触发事件
var changeCounty = function( e ) {
that.setData( {
'city.countyIndex': e.detail.value
});
};
that["countyPickerChange"]=changeCounty;
}
function getProvinceFun(that, p_url, p_data){
g_cbSuccess = function( res ) {
that.setData( {
'city.province': res
});
};
net.r( p_url, p_data, g_cbSuccess, g_cbSuccessErr, g_cbFail, g_cbComplete, g_header, g_method );
}
module.exports={
initCityFun: initCityFun,
getProvinceFun: getProvinceFun
}
顺道net.js方法::
/**
* 网络发送http请求,默认为返回类型为json
*
* url: 必须,其他参数非必须 接口地址
* data:请求的参数 Object或String
* successFun(dts):成功返回的回调函数,已自动过滤微信端添加数据,按接口约定,返回成功后的data数据,过滤掉msg和status
* successErrorFun(msg):成功执行请求,但是服务端认为业务错误,执行其他行为,默认弹出系统提示信息.
* failFun:接口调用失败的回调函数
* completeFun:接口调用结束的回调函数(调用成功、失败都会执行)
* header:object,设置请求的 header , header 中不能设置 Referer
* method:默认为 GET,有效值:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
*
*/
function r( url, data, successFun, successErrorFun, failFun, completeFun, header, method ) {
var reqObj = {};
reqObj.url = url;
reqObj.data = data;
//默认头为json
reqObj.header = { 'Content-Type': 'application/json' };
if( header ) {
//覆盖header
reqObj.header = header;
}
if( method ) {
reqObj.method = method;
}
reqObj.success = function( res ) {
var returnData = res.data; //将微信端结果过滤,获取服务端返回的原样数据
var status = returnData.status; //按接口约定,返回status时,才调用成功函数
//console.log(res);
//正常执行的业务函数
if( status == true ) {
if( successFun ) {
var dts = returnData.data;
successFun( dts );//回调,相当于获取到data后直接在回调里面处理赋值数据
}
} else if( status == false ) {
var msg = returnData.msg;
if( !successErrorFun ) {
console.log( msg );
} else {
successErrorFun( msg );
}
} else {
console.log( "服务端没有按照接口约定格式返回数据" );
}
}
reqObj.fail = function( res ) {
if( failFun ) {
failFun( res );
}
}
reqObj.complete = function( res ) {
if( completeFun ) {
completeFun( res );
}
}
wx.request( reqObj );
}
module.exports = {
r: r
}
核心代码就是上面这三个文件,接下来是demo文件做测试::demo.wxml::
<import src="../../template/cityTemplate.wxml"/>
<template is="city" data="{{...city}}" />
demo.js::
var city = require( '../../common/cityTemplate' );
Page( {
data: {
},
onLoad: function( options ) {
var _that = this;
//创建三级联动数据对象 ---- 这个city对象是固定的,只有请求的url是根据各自的服务端地址来更改的
_that.setData( {
city: {
province: {},//格式province:{code: ["11000", "12000"], name: ["北京市", "上海市"]},只能固定是name和code,因为模板需要根据这俩参数显示
city: {},
county: {},
provinceIndex: 0,
cityIndex: 0,
countyIndex: 0,
cityUrl: "http://localhost:8282/phpserver/areas.php?type=city&fcode=",//type表示获取地区 fcode是一级code码,到时具体根据后端请求参数修改
countyUrl: "http://localhost:8282/phpserver/areas.php?type=county&fcode="
}
})
var _url = "http://localhost:8282/phpserver/areas.php";
var _data = { 'type': 'province', 'fcode': '0' };
city.initCityFun( _that, _url, _data );
}
})
以上完整代码文件,最终测试如下:
这里存在一个bug,开启下拉刷新和picker组件的下拉会重叠了,不知道是开发工具原因,还是还为修改的bug。。。只能等微信方面更新消息给反馈了
今天更新的0.10.102700这个版本后,发现使用text组件触发不了picker组件了,经过一番测试后需把picker组件内的text标签换成view标签才能正常执行业务。
以上实例的cityTemplate.wxml文件里面text标签请自行更换成view标签。在进行测试~
昨天发现一个bug,请重新复制cityTemplate.js文件,这里纠正的是:触发一级下拉事件的时候先清空二级三级的内容和充值默认索引值为0,触发二级下拉事件的时候清空三级的内容和默认索引值。保证每次点击选择都会重新触发事件获取值。