tree.axml
<view class="flex" style="flex-direction:{{direction}}">
<block a:for="{{list}}" key="{{index}}">
<view class="tree-wrap">
<view class="main flex color-99" onTap="select"
data-name="{{item.TypeName}}"
data-cid="{{item.TypeId}}"
data-pid="{{item.Superior}}"
data-len="{{item.children?item.children.length:0}}"
a:if="{{item.Superior===pid}}"
>
<image mode="scaleToFill" src="/assets/image/icon_{{item.icon}}.png" />
<view class="{{item.TypeId === cid?'fc-018FFB':''}}">{{item.TypeName}}view>
view>
<view class="flex" a:if="{{item.children.length>0}}">
<tree list="{{item.children}}" direction="column" onSelect="childSelect" onChildSelect="childSelect" pid="{{pid}}" cid="{{cid}}">tree>
view>
view>
block>
view>
tree.js
Component({
mixins: [],
data: {},
props: {
list: Object, // 列表数据
icon: '', // 节点图标
direction: 'row', // 排列方式,row: 横向排列 column:纵向排列
pid: 0, // 父节点id
cid: 0, // 子节点id
onSelect: () => { }, // 根节点选择
onChildSelect: () => { } // 其他节点选择
},
didMount() {
},
didUpdate() {
},
didUnmount() { },
methods: {
select(e) {
const { cid, name, pid, len } = e.target.dataset
this.props.onSelect({ cid, name, pid, len })
},
childSelect(data) {
this.props.onChildSelect(data)
}
},
});
tree.acss
.main {
padding: 0 40rpx 30rpx 0;
font-size: 24rpx;
align-items: center;
line-height: 30rpx;
}
.main image {
margin-right: 20rpx;
width: 30rpx;
height: 30rpx;
}
.show-select-wrap {
overflow-x: auto;
width: 690rpx;
margin-bottom: 60rpx;
padding: 20rpx 30rpx;
background: #E0F0FC;
white-space: nowrap;
font-size: 22rpx;
box-sizing: border-box;
}
.select-item {
padding: 0 20rpx;
border-right: 1px solid #999;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
box-sizing: border-box;
}
tree.json
{
"component": true,
"usingComponents": {
"tree": "/components/cascade/tree/tree"
}
}
弹窗使用基于 Alipay Design 设计规范的小程序扩展组件库
mini-ali-ui
cascade.axml
<popup
show="{{show}}"
position="bottom"
disableScroll="{{false}}"
zIndex="999"
>
<view class="popup popup-product">
<view class="popup-header flex">
<view>级联view>
<view class="close-popup" onTap="onClose">view>
view>
<view class="parents">
<view class="show-select-wrap" a:if="{{pid!==0&&showSelect}}">
<view class="flex">
<view
a:for="{{selectList}}"
key="{{index}}"
class="select-item flex-0"
style="max-width:{{690/selectList.length}}rpx"
data-pid="{{item.pid}}"
data-index="{{index}}"
onTap="selectBack"
>
{{item.name}}
view>
<view class="fc-018FFB flex-0" style="padding: 0 30rpx 0 20rpx">请选择view>
view>
view>
<scroll-view scroll-y="{{true}}" class="scroll-view">
<tree list="{{treeList}}" onSelect="select" onChildSelect="childSelect" pid="{{pid}}" cid="{{initTreeData.typeId}}">tree>
scroll-view>
view>
view>
popup>
cascade.js
Component({
mixins: [],
data: {
list: [], // 列表
// 选中树节点数据
treeData: {
typeId: 0, // 选择节点id
typeName: '', // 选择节点名称
},
// 初始选中树节点数据
initTreeData: {
typeId: 0, // 选择节点id
typePid: 0, // 选择节点父id
typeName: '', // 选择节点名称
},
pid: 0, // 父节点id
initSelectList: [], // 初始选项切换列表
selectList: [], // 选项切换列表
showSelect: false, // 展示选项切换列表
lock: false, // 级联显示时锁定子组件handleShow方法
},
props: {
show: false, // 显示弹窗
className: '', // 类名
treeList: Array, // 树列表
clear: false, // 清空标记
onClose: () => { }, // 关闭
onClear: () => { }, // 清空
onGetName: () => { }, // 获取选中的名字
},
didMount() {
},
didUpdate() {
if (!this.data.lock && this.props.show) this.handleshow()
if (this.props.clear) this.handleClear()
},
didUnmount() { },
methods: {
/**
* @description 根节点选择
* @param {object} data 组件数据 name:节点名称, pid:父节点id, cid:子节点id, len:子节点长度
*/
select(data) {
this.handleSelectTree(data)
},
/**
* @description 父/子节点选择
* @param {object} data 组件数据 name:节点名称, pid:父节点id, cid:子节点id, len:子节点长度
*/
childSelect(data) {
this.handleSelectTree(data)
},
/**
* @description 节点选择
* @param {object} data 组件数据 name:节点名称, pid:父节点id, cid:子节点id, len:子节点长度
*/
handleSelectTree(data) {
// len=0表示点击项没有有子节点
const { name, pid, cid, len } = data
let selectList = this.data.selectList
if (len !== 0) {
selectList.push({
name,
cid,
pid
})
}
this.setData({
'treeData.typeName': len ? '' : name,
'treeData.typeId': len ? 0 : cid,
showSelect: !!len,
pid: cid,
selectList: [...selectList]
}, () => {
if (len === 0) {
this.onClose()
this.setData({
'initTreeData.typeName': name,
'initTreeData.typeId': cid,
'initTreeData.typePid': pid,
pid,
initSelectList: [...selectList],
})
this.props.onGetName({ typeName: this.data.initTreeData.typeName })
}
})
},
// 选项返回
selectBack(e) {
const { pid, index } = e.target.dataset
let selectList = this.data.selectList
selectList.splice(index)
this.setData({
pid,
showSelect: !!pid,
selectList: [...selectList]
})
},
// 显示
handleshow() {
const { show } = this.props
if (show) {
const { initTreeData: { typeId, typePid }, initSelectList } = this.data
this.setData({
lock: true,
pid: typePid,
showSelect: !(typeId === 0),
selectList: typeId === 0 ? [] : [...initSelectList], // 选项切换列表
})
}
},
// 关闭
onClose() {
this.setData({ lock: false })
this.props.onClose({ show: false })
},
// 清空
handleClear() {
this.setData({
'treeData.typeId': 0, // 选择节点id
'treeData.typeName': '', // 选择节点名称
'initTreeData.typeId': 0, // 选择节点id
'initTreeData.typePid': 0, // 选择节点父id
'initTreeData.typeName': '', // 选择节点名称
pid: 0, // 产品类型-父节点id
initSelectList: [], // 产品类型-初始选项切换列表
selectList: [], // 产品类型-选项切换列表
})
this.props.onClear({ clear: false })
}
},
});
cascade.acss
.popup {
height: 85vh;
background: #fff;
border-radius: 34rpx 34rpx 0px 0px;
}
.popup-header {
padding: 0 30rpx;
height: 70rpx;
justify-content: space-between;
align-items: center;
font-size: 32rpx;
line-height: 70rpx;
font-weight: bold;
}
.popup .popup-header {
height: 142rpx;
line-height: 142rpx;
}
.close-popup {
width: 70rpx;
height: 70rpx;
background: url(/assets/image/icon_close.png) no-repeat right center;
background-size: 22rpx 22rpx;
}
.selected-field {
background: #E9F5FE!important;
border: 1px solid #429AFD!important;
color: #018FFB!important;
}
.popup .scroll-view {
padding: 30rpx 0;
height: calc(85vh - 300rpx);
}
.parents {
padding: 0 30rpx;
}
{
"component": true,
"usingComponents": {
"popup": "mini-ali-ui-rpx/es/popup/index",
"tree": "/components/cascade/tree/tree"
}
}
app.acss
page {
overflow: hidden;
}
.flex {
display: flex;
display: -webkit-flex;
}
.flex-0 {
-webkit-box-flex: 0;
flex-grow: 0;
flex-shrink: 0;
}
.flex-1 {
-webkit-box-flex: 1;
flex-grow: 1;
flex-shrink: 1;
}
.list-item {
margin: 0 auto 30rpx;
width: 690rpx;
background: #FFFFFF;
box-shadow: 0px 4rpx 30rpx 0px rgba(19, 106, 203, 0.1);
border-radius: 20rpx;
box-sizing: border-box;
}
.item-body {
padding: 30rpx 30rpx 24rpx;
}
.item-body>view {
margin-bottom: 18rpx;
line-height: 40rpx;
}
.position-relative {
position: relative;
}
.color-99 {
color: #999
}
.fc-018FFB {
color: #018FFB;
}
/* 分割线 */
.divide {
width: 100%;
height: 20rpx;
background-color: #f5f5f5;
}
cascade.axml
<view class="scroll-item flex">
<view class="label flex-0">请选择view>
<view class="flex-0 input-wrap">
<view class="input-select position-relative type-wrap">
<view class="type-name" onTap="handleShow">{{typeName}}view>
<view class="select-icon icon-arrow" a:if="{{typeName===''}}">view>
<view
class="select-icon icon-clear"
a:if="{{typeName!==''}}"
onTap="handleClear"
data-type="typeName"
>view>
view>
view>
view>
<cascade
show="{{show}}"
clear="{{clear}}"
lock="{{lock}}"
treeList="{{treeList}}"
onClose="onClose"
onClear="onClear"
onGetName="getName"
/>
cascade.js
Page({
data: {
clear: false, // 清空标记
typeName: '', // 名称
show: false, // 显示级联弹窗
treeList: [], // 树形结构列表
list: [
{ TypeId: 1, TypeName: "一级 1", Superior: 0 },
{ TypeId: 2, TypeName: "一级 2", Superior: 0 },
{ TypeId: 3, TypeName: "一级 3", Superior: 0 },
{ TypeId: 4, TypeName: "一级 4", Superior: 0 },
{ TypeId: 5, TypeName: "二级 2-1", Superior: 2 },
{ TypeId: 6, TypeName: "二级 2-2", Superior: 2 },
{ TypeId: 7, TypeName: "二级 1-1", Superior: 1 },
{ TypeId: 8, TypeName: "二级 1-2", Superior: 1 },
{ TypeId: 9, TypeName: "二级 1-3", Superior: 1 },
{ TypeId: 10, TypeName: "三级 2-1-1", Superior: 5 },
{ TypeId: 11, TypeName: "四级 2-1-1-1", Superior: 10 },
{ TypeId: 12, TypeName: "四级 2-1-1-2", Superior: 10 },
{ TypeId: 13, TypeName: "四级 2-1-1-3", Superior: 10 },
{ TypeId: 14, TypeName: "四级 2-1-1-4", Superior: 10 },
{ TypeId: 15, TypeName: "五级 2-1-1-4-1", Superior: 14 },
{ TypeId: 16, TypeName: "五级 2-1-1-4-2", Superior: 14 },
{ TypeId: 17, TypeName: "五级 2-1-1-4-3", Superior: 14 },
{ TypeId: 18, TypeName: "四级 2-1-1-5", Superior: 10 },
{ TypeId: 19, TypeName: "四级 2-1-1-6", Superior: 10 },
{ TypeId: 20, TypeName: "三级 2-1-2", Superior: 5 },
{ TypeId: 21, TypeName: "三级 1-1-1", Superior: 7 },
{ TypeId: 22, TypeName: "三级 1-1-2", Superior: 7 },
{ TypeId: 23, TypeName: "三级 1-2-1", Superior: 8 },
{ TypeId: 24, TypeName: "三级 1-2-2", Superior: 8 }],
},
onLoad() {
this.getParentList()
},
// 显示级联
handleShow() {
this.setData({ show: true })
},
// 清空操作
handleClear() {
this.setData({ clear: true, typeName: '' })
},
// 关闭级联
onClose(data) {
const { show } = data
this.setData({ show })
},
// 清空 - 设置清空标记
onClear(data) {
const { clear } = data
this.setData({ clear })
},
// 获取选择的内容
getName(data) {
const { typeName } = data
this.setData({ typeName })
},
/**
* @description 转换为树结构
* @param {array} list 扁平数据结构列表
*/
getParentList() {
const { list } = this.data
const parentList = list.filter(item => item.Superior == 0);
const treeList = this.getOrderTreeList(list, parentList);
this.setData({ treeList })
},
/**
* @description 转换为树结构
* @param {array} data 子列表
* @param {array} dataArr 父列表
*/
getOrderTreeList(data, dataArr) {
for (let i = 0; i < dataArr.length; i++) {
let childrenArr = data.filter(item => item.Superior == dataArr[i].TypeId);
if (dataArr[i].Superior === 0)
dataArr[i].icon = childrenArr.length > 0 ? 'files' : 'file'
else
dataArr[i].icon = childrenArr.length > 0 ? 'files2' : 'file1'
if (childrenArr.length > 0) {
dataArr[i].children = childrenArr;
this.getOrderTreeList(data, childrenArr);
}
}
return dataArr
}
});
cascade.acss
.scroll-item {
margin-bottom: 30rpx;
justify-content: center;
align-items: center;
}
.label {
margin-right: 30rpx;
font-size: 28rpx;
}
.input-select {
padding: 15rpx 20rpx;
min-width: 150rpx;
min-height: 70rpx;
background: #F6F6F6;
border-radius: 35px;
line-height: 40rpx;
box-sizing: border-box;
font-size: 24rpx;
color: #999;
/* text-align: center; */
border: 1px solid #F6F6F6;
}
.input-select-wrap {
display: flex;
justify-content: flex-end;
}
.input-wrap .input-select {
width: 514rpx;
}
.type-wrap .select-icon {
position: absolute;
top: 50%;
right: 10%;
transform: translateY(-50%);
}
.select-icon {
/* margin-left: 20rpx; */
width: 40rpx;
height: 40rpx;
background-repeat: no-repeat;
background-position: right center;
}
.icon-arrow {
background-image: url(/assets/image/icon_arrow1.png);
background-size: 20rpx 12rpx;
}
.icon-clear {
background-image: url(/assets/image/icon_clear1.png);
background-size: 24rpx 24rpx;
}
.type-wrap .select-icon {
position: absolute;
top: 50%;
right: 10%;
transform: translateY(-50%);
}
.type-name {
width: 88%;
min-height: 40rpx;
}
cascade.json
{
"defaultTitle": "级联",
"usingComponents": {
"cascade": "/components/cascade/cascade"
}
}