react 使用antd的TreeSelect树选择组件实现多个树选择循环

需求说明,一个帐号角色可以设置管理多个项目的菜单权限

且菜单接口每次只能查询特定项目的菜单数据【无法查全部】

开发思路:

1,获取项目接口数组,得到项目数据

2,循环项目数据,以此为参数递归查询菜单数据【递归查询是为保证循环时数据异步请求顺序 不稳定】

3,将菜单数组组装成一个二维数组,以待循环树选择组件作展示 数据使用

4,循环树选择组件,实现树选择菜单功能

5,读取某条用户信息的菜单权限,将返回字符串菜单编码处理成与菜单数据相同的二维数组

6,奖该用户信息的菜单权限数组加载到循环树选择组件作默认选中

开发难点:

1,菜单编号要在指定数组中进行增删改,就需要对其分别 打上标签,这里是额外创建一个numListArry对象,打上对应项目标签顺序下标,以此匹配菜单数据二维数组的下标【因递归查询 ,所以次序固定】

2,理解树选择的点击事件返回的各种参数,将返回的数据 实时更新到该条用户信息的菜单权限数组中,实现树状选择功能

3,各种基础算法都用到不少,如字符串去重,数组求交集,递归,查找等

用户角色信息【表格数据】

项目数据:

菜单数据

numListArry对象,对应角标关系准确匹配数组数据

列表页面代码

  1 import React from ‘react‘;
  2 import {Table,Form,Button,Popconfirm } from ‘antd‘;
  3 import LoadingMixin from ‘../../../../../libs/loading.common.mixin‘;
  4 import RequestMixin from ‘../../../../../libs/request.mixin‘;
  5 import NotificationMixin from ‘../../../../../libs/notification.mixin‘;
  6 import ModalWrapper from ‘../../../../../libs/modalwrapper‘;
  7 import Helper from ‘../../../../../libs/helper‘;
  8 import ‘./index.css‘;
  9 import AddOrUpdateModal from ‘./addorupdatemodal‘;
 10 import LocalService from "../../../../../services/local.services";
 11
 12 let prolist = LocalService.getUserInfo() && JSON.parse(LocalService.getUserInfo()) || [] ;
 13 const createForm = Form.create;
 14 let user =  React.createClass({
 15     mixins: [LoadingMixin,NotificationMixin,RequestMixin],
 16     getInitialState(){
 17         return {
 18             data: [],
 19             menuList: [], //所有菜单信息集合
 20             menuArry:[],
 21             fetchCategoryListArry:[],
 22             numListArry:{}  //菜单ID匹配下标对象
 23         }
 24
 25     },
 26     componentWillMount() {
 27         this.fetch();
 28         if(prolist.prolist){
 29             this.fetchCategoryList(prolist.id,prolist.prolist,0);
 30         }
 31
 32
 33     },
 34     fetch() {
 35
 36         this.get({  //查询用户角色信息 【数据用以在表格中展示】
 37             url: "Api/lists/module/role/key/dac509bd9***********a6f1af4bac",
 38             param: {},
 39             noLoading: true
 40         }).then(result=> {
 41             this.setState({data: result.result || []});
 42             this.fetchCategoryListArry()
 43         });
 44     },
 45     fetchCategoryListArry() {  //查询项目信息
 46         var getSessionId = LocalService.getSessionId()
 47         this.get({
 48             url: "Api/lists/module/menu/key/dac509bd90a**************1af4bac",
 49             param: {
 50                 sessionid:getSessionId || ‘‘
 51             },
 52             noLoading: true
 53         }).then(result=> {
 54             this.setState({fetchCategoryListArry: result.result || []});
 55         });
 56     },
 57     fetchCategoryList(userid,thisprolist,numIndex) {  //查询菜单
 58         var proid = thisprolist[numIndex];
 59         var getSessionId = LocalService.getSessionId()
 60         this.get({
 61             url: "Api/search/module/menu/key/dac509bd90***************6f1af4bac",
 62             param: {
 63                 userid: userid,
 64                 proid:proid,
 65                 sessionid:getSessionId || ‘‘
 66             },
 67             noLoading: true
 68         }).then(result=> {
 69             let menuList = result.result || [];
 70             let treeData = [];
 71             let menuArry = this.state.menuArry;
 72             var numListArry=this.state.numListArry;
 73
 74             menuList && menuList.map(item => {
 75                 let treeItem = {
 76                     id: item.id,
 77                     label: item.title,
 78                     value: item.id,
 79                     key: item.id,
 80                     hasChildren: item.hasChildren,
 81                     proid:proid, //取出项目ID存入每条信息中,方便在树选择中显示为哪个项目的提示【placeholder】
 82                     numIndex:numIndex
 83                 };
 84                 numListArry[item.id]=numIndex;
 85                 if (item.hasChildren) { //处理接口数据
 86                     let children = [];
 87                     item.children && item.children.map(menu => {
 88                         children.push({
 89                             id: menu.id,
 90                             fid: item.id,
 91                             label: menu.title,
 92                             value: menu.id,
 93                             key: menu.id,
 94                             proid:proid,
 95                             numIndex:numIndex
 96                         });
 97
 98                         numListArry[menu.id]=numIndex;
 99                     });
100                     treeItem.children = children;
101                 }
102                 treeData.push(treeItem);
103             });
104             menuArry.push(treeData)
105
106             var numArry = []
107             this.setState({menuList: menuArry,numListArry:numListArry}); //将全部菜单数据和处理好的菜单ID下标对象存入各自全局变量
108
109             numIndex++;
110             if(numIndex<thisprolist.length){
111                 this.fetchCategoryList(userid,thisprolist,numIndex); //递归查询
112             }
113         });
114     },
115     deleteRole(parms){ //删除角色信息接口
116         let that = this;
117         if (!parms) return;
118         that.post({
119             url: "Api/batchDelete/module/role/key/dac509********4bac",
120             param: {ids:parms.id},
121             noLoading: true
122         }).then(result=> {
123             if (result.result) {
124                 that.success("删除成功");
125                 that.fetch();
126             }
127         });
128     },
129     addOrUpdate(modal,e) {
130         e && e.preventDefault() ;
131         e && e.stopPropagation();
132         new ModalWrapper(AddOrUpdateModal, "addOrUpdateModal", () => {
133             this.fetch();
134         }, null, {
135             menuList:this.state.menuList, //打开弹窗时,将全部菜单数据传入子组件
136             numListArry:this.state.numListArry,  //菜单ID下标对象,同上
137             title:  modal && modal.id  ? ‘编辑角色‘ : ‘新增角色‘,
138             item: modal && modal.id ? Helper.copyObject(modal) : {}, //本条角色信息的数据,包含其已有的菜单数据【回显】
139             isEdit: modal && modal.id  ? true:false
140         }).show();
141     },
142     render(){
143         let statusObj = {
144             0: "有效",
145             1: "无效"
146         };
147
148         let columns = [
149             { title: ‘编号‘,dataIndex: ‘id‘,key: ‘id‘, width: ‘5%‘},
150             { title: ‘角色名称‘,dataIndex: ‘role_name‘,key: ‘role_name‘, width: ‘10%‘},
151             { title: ‘权限‘, dataIndex: ‘permission‘,key: ‘permission‘,  width: ‘35%‘,
152                 render: (text, record) => {
153                     let menuList = [];
154                     let permission = record.permission.split(‘,‘)
155                     permission && permission.map( item =>{
156                         this.state.fetchCategoryListArry && this.state.fetchCategoryListArry.map( first =>{
157                             if(item == first.id){
158                                 menuList.push(first.title);
159                             }
160                         })
161                     })
162
163                     if( !menuList==null || !menuList.length==0){
164                         return (
165                             menuList.join(‘,‘)+‘...‘
166                         )
167                     }
168
169                 }
170             },
171             { title: ‘创建时间‘, dataIndex: ‘create_time‘,key: ‘create_time‘, width: ‘15%‘,},
172             { title: ‘更新时间‘, dataIndex: ‘update_time‘,key: ‘update_time‘, width: ‘15%‘, },
173             { title: ‘状态‘, dataIndex: ‘is_del‘,key: ‘is_del‘, width: ‘7%‘,
174                 render: (text, record) => {
175                     return (
176                         statusObj[record["is_del"]]
177                     )
178                 }
179             },
180
181             { title: ‘操作‘, key: ‘#‘, width: ‘23%‘,
182                 render: (text, record) => {
183                     return (
184                         <div>
185                             <Button type="primary" style={{ marginRight: 8 }} onClick={this.addOrUpdate.bind(this,record)}>修改</Button>
186                             <Popconfirm title="确定删除角色?" onConfirm={this.deleteRole.bind(this,record)} okText="确定" cancelText="取消">
187                                 <Button type="primary">删除</Button>
188                             </Popconfirm>
189                         </div>
190
191                     )
192                 }
193             }
194         ];
195         return (
196             <div className="role">
197                 <div className="title">
198                     <h2>角色管理</h2>
199
200                     <Button type="primary" onClick={this.addOrUpdate.bind(this,‘‘)}>添加角色</Button>
201                 </div>
202                 <div className="list-role">
203                     <Table columns={columns}
204                            dataSource={this.state.data}
205                            pagination={false}
206                            scroll={{ y: 600 }}
207                            rowKey={(record) => record.id}
208                     >
209                     </Table>
210                 </div>
211             </div>
212         )
213     }
214 });
215 user = createForm()(user);
216 export default user;

弹窗组件:

  1 import React from "react";
  2 import {Modal, Form, Input, Select, TreeSelect} from ‘antd‘;
  3 import LoadingMixin from ‘../../../../../libs/loading.common.mixin‘;
  4 import RequestMixin from ‘../../../../../libs/request.mixin‘;
  5 import NotificationMixin from ‘../../../../../libs/notification.mixin‘;
  6 import LocalService from "../../../../../services/local.services";
  7 const FormItem = Form.Item;
  8 const createForm = Form.create;
  9 const Option = Select.Option;
 10 const SHOW_PARENT = TreeSelect.SHOW_ALL;
 11 let prolist = LocalService.getUserInfo() && JSON.parse(LocalService.getUserInfo()) || [] ;
 12
 13 let addOrUpdateModal = React.createClass({
 14     mixins: [LoadingMixin, NotificationMixin, RequestMixin],
 15     propTypes: {
 16         onManualClose: React.PropTypes.func,
 17         onOk: React.PropTypes.func,
 18         onCancel: React.PropTypes.func,
 19         title: React.PropTypes.string,
 20         item: React.PropTypes.object,
 21         menuList: React.PropTypes.array,
 22         isEdit: React.PropTypes.bool,
 23         numListArry:React.PropTypes.object,
 24     },
 25     getInitialState() {
 26         return {
 27             item: this.props.item || {}, //用户信息
 28             menuList: this.props.menuList || [],  //权限集合
 29             numListArry: this.props.numListArry || [],
 30             permissions: [],
 31             oldPermissions: [],
 32             headNav:[],
 33             permissionsArry:[],
 34             newPermissionsArry:[],
 35             headNavsearchPlaceholder:[]
 36         };
 37     },
 38     componentWillMount(){
 39
 40         if (this.props.isEdit) {
 41             let item = this.state.item || {}; //角色已有权限数据作去重去空转换成数组
 42             let permissions = item.permission.split(",");
 43             let menuList = this.state.menuList;
 44
 45             var arry = permissions.filter(function(element,index,self){  //去重去空
 46                      return self.indexOf(element) === index;
 47               });
 48             for(var i = 0;i<arry.length;i++){
 49                 if(arry[i]==‘‘||arry[i]==null||typeof(arry[i])==undefined){
 50                     arry.splice(i,1);
 51                     i=i-1;
 52                 }
 53             }
 54             permissions = arry;
 55
 56             this.setState({permissionsArry: permissions});
 57         }
 58         this.getHeaderMenu()
 59
 60     },
 61     getHeaderMenu(){ //顶部菜单
 62         this.get({
 63             url: "Api/lists/module/project/key/dac509bd90a82719a3569291e12c24a6f1af4bac",
 64             param: {
 65             }
 66         }).then(result => {
 67             this.setState({headNav: result.result || []});
 68             var permissions = []
 69             var itemList =[]
 70             var itemStr = this.state.permissionsArry
 71             var menuList = this.state.menuList;
 72             var menuListArry =[]
 73             menuList && menuList.map((item)=>{  //全部菜单数组转换成只含ID的数组【待与传来的角色已有权限数据作交集处理】
 74                 var newPermissions =[]
 75                 item && item.map((pros)=>{
 76                     if (pros.children){
 77                         newPermissions.push(pros.id);
 78                         pros.children.map( (list) =>{
 79                             newPermissions.push(list.id);
 80                         })
 81                     }else{
 82                         newPermissions.push(pros.id);
 83                     }
 84                 })
 85
 86                 Array.intersect = function(arr1, arr2) {
 87                     if(Object.prototype.toString.call(arr1) === "[object Array]" && Object.prototype.toString.call(arr2) === "[object Array]") {
 88                         return arr1.filter(function(v){
 89                             return arr2.indexOf(v)!==-1
 90                         })
 91                     }
 92                 }
 93                 var mergeArry = Array.intersect(itemStr, newPermissions); // 此处求交集原因:因角色所含菜单数据是一个包含菜单ID的无绪字符串,转成数据也无法直接在树选择组件中循环回显
 94                 menuListArry.push(mergeArry)                                //所以要先将所有菜单数据的二维数组处理成一个只含ID的二维数组,将其每条子数组与角色所含菜单数据进行交集匹配,
 95             })                                                                //处理完的二维数组就可以用在树选择组件中循环回选了
 96
 97             this.setState({permissions: menuListArry});
 98
 99             var headNavsearchPlaceholder = [] //权限列表提示
100             this.state.headNav && this.state.headNav.map((item)=>{
101                 headNavsearchPlaceholder[item.id] = item.title
102             })
103             this.setState({headNavsearchPlaceholder:headNavsearchPlaceholder});
104         });
105     },
106
107     onChange(value,label,extra){
108         let newPermissions = [];
109         let proid =‘‘
110         let permissions = this.state.permissions;
111         var numListArry = this.state.numListArry;
112         var numIndex = numListArry[extra.triggerValue];
113
114         if(numIndex != null){ //判断 点击数据是否有菜单ID,有的话按照之前numListArry对象中匹配该菜单ID所属项目下标,【对应递归查询的二维数组下标】
115             permissions[numIndex] = value;
116         }
117        this.setState({permissions:permissions ,oldPermissions:permissions});
118     },
119     postSubmit(url, param) {
120         this.post({
121             url: url,
122             param: param,
123             noLoading: true
124         }).then(result => {
125             if (result && result.result) {
126                 this.success(!this.props.isEdit ? ‘新增成功‘ : ‘修改成功‘);
127                 this.props.onManualClose && this.props.onManualClose();
128             }
129         });
130     },
131     handleSubmit() {
132         this.props.form.validateFieldsAndScroll((errors, values) => {
133             if (!errors) {
134                 var param = values;
135                 param.permission = this.state.permissions.join(‘,‘); //循环后的树选择返回的数据也是个只含菜单ID二维数组,接口只能入传字符串,所以进行数据处理
136                 if (!this.props.isEdit) {  //判断是新增还是修改
137                     this.postSubmit("Api/add/module/role/key/dac509bd90******1af4bac", param);
138                 }
139                 else {
140                     param.id = this.state.item && this.state.item.id;
141                     this.postSubmit("Api/edit/module/role/key/dac509bd90*****af4bac", param);
142                 }
143             }
144         });
145     },
146     hideModal() {
147         this.props.onCancel && this.props.onCancel();
148     },
149     render() {
150         const {getFieldDecorator} = this.props.form;
151         const formItemLayout = {
152             labelCol: {span: 6},
153             wrapperCol: {span: 10},
154         };
155
156         const treeSelectArry = []
157         this.state.menuList && this.state.menuList.map((item,index)=>{  //循环树选择数据
158             const tProps = {
159                 value: this.state.permissions[index],
160                 treeData: item,
161                 onChange: this.onChange,
162                 multiple: true,
163                 treeCheckable: true,
164                 dropdownStyle: {maxHeight:"350px"},
165                 showCheckedStrategy: SHOW_PARENT,
166                 searchPlaceholder: ‘请选择‘+this.state.headNavsearchPlaceholder[item[0].proid]+‘权限列表‘,
167                 style: {
168                     width: 300,
169                 },
170             };
171             treeSelectArry.push(tProps)
172         })
173
174         return (
175             <Modal title={this.props.title || ‘新增‘} visible={true} width="550px" onOk={this.handleSubmit}
176                    onCancel={this.hideModal} maskClosable={false}>
177                 <Form layout="horizontal" autoComplete="off">
178                     <FormItem {...formItemLayout} label="角色名称" >
179                         {getFieldDecorator(‘role_name‘, {
180                             initialValue: this.state.item && this.state.item.role_name || ‘‘,
181                             rules: [{ required: true, message: ‘请输入角色名称!‘ }],
182                         })(
183                             <Input  placeholder="请输入角色名称"/>
184                         )}
185                     </FormItem>
186
187                     <FormItem {...formItemLayout} label="权限列表">
188                         {
189                             this.state.menuList && this.state.menuList.map((item,index)=>{ //循环树选择DOM
190
191                                 return (
192                                     <div style={{ marginBottom : "10px" }} key={index}>
193                                         <TreeSelect  {...treeSelectArry[index]} />
194                                     </div>
195                                 )
196                             })
197                         }
198                         {/*<TreeSelect {...tProps} />*/}
199                     </FormItem>
200
201
202                     <FormItem{...formItemLayout} label="状态">
203                         {getFieldDecorator(‘is_del‘,{
204                             initialValue: this.state.is_del && this.state.is_del || ‘0‘
205                         })(
206                             <Select style={{ width: 100 }}>
207                                 <Option value="1">无效</Option>
208                                 <Option value="0">有效</Option>
209                             </Select>
210                         )}
211                     </FormItem>
212
213                 </Form>
214             </Modal>
215         )
216     }
217 });
218 addOrUpdateModal = createForm()(addOrUpdateModal);
219 export default addOrUpdateModal;

原文地址:https://www.cnblogs.com/zhixi/p/10419878.html

时间: 2024-11-02 03:40:04

react 使用antd的TreeSelect树选择组件实现多个树选择循环的相关文章

react-native DatePicker日期选择组件的实现

本教程的实现效果如下: 为了实现其淡入/淡出的覆盖效果, 还有取消按钮, 在此用了一个三方的组件, 大家可以先安装一下: 三方组件的地址:https://github.com/eyaleizenberg/react-native-custom-action-sheet (可以看看,也可以直接按我的步骤走) 1. 在terminal的该工程目录下运行: npm install react-native-custom-action-sheet --save 2. 然后运行: npm start 3.

React 生命周期(顺序)和 组件 通信

很多情况下,我们是通过props来定制组件实例的外观及行为,这样的组件我们称之为无状态/stateless的组件,因为在任何时刻,组件 实例的表现都仅仅取决于外部传入的props属性,与 它自身之前的表现毫无关系,即,它本身没有任何记忆 让一个组件拥有记忆能力,根据自身的状态对同样的刺激做出 不同的反应,React的组件的确引入了状态机的概念state 1. setState 尖端使用的方法 callback //默认初始化close为false this.setState({close : t

js组件开发-移动端地区选择控件mobile-select-area

移动端地区选择控件mobile-select-area 由于之前的[js开源组件开发]js手机联动选择地区仿ios 开源git 很受欢迎,于是我又对其进行了一些优化,包括可选的范围变大了,添加了默认空首地址的功能,也添加了更多api参数,首先我们先来看下这次的效果图. 它的github地址请点击https://github.com/tianxiangbing/mobile-select-area 它的demo演示请点击 http://www.lovewebgames.com/jsmodule/m

基于React Native的Material Design风格的组件库 MRN

基于React Native的Material Design风格的组件库.(为了平台统一体验,目前只打算支持安卓) 官方网站 http://mrn.js.org/ Github https://github.com/binggg/mrn 示例应用在线演示 https://appetize.io/app/j48zj9r83cetpd1mhg4g8buc4w 示例应用下载 https://github.com/binggg/MaterialReactNative/blob/master/androi

省份、城市选择组件

组件要求: 1.能够选择中国的省份.城市 2.组件具有可扩展性,较好的复用性 效果: 具体实施: 1.类似于照片选择组件,第一个界面显示省份,第二个组件显示城市. // // ViewController.m // CityPicker // // Created by vousaimer on 15-1-23. // Copyright (c) 2015年 va. All rights reserved. // #import "ViewController.h" #import &

Ext4.X--年月选择组件

1.类的定义: 在Ext项目开发中,有时会用到只选择年月的日期组件,下面的代码会帮你完成,只需向下面的类拷贝到你的项目中即可使用. Ext.define('Ext.form.field.Month', { extend:'Ext.form.field.Date', alias: 'widget.monthfield', requires: ['Ext.picker.Month'], alternateClassName: ['Ext.form.MonthField', 'Ext.form.Mon

android 拍照和从相册选择组件

android 拍照及从相册选择组件 单独封装到一个 activity 中便于更好的复用 拍照或从相册选择成功后使用 EventBus 发出广播回传图片路径,和调用者充分解耦合 根据传入参数支持裁剪和不裁剪两种模式 /** * <pre> * 拍照及从相册选择弹出 activity * 成功后会发送 TakePhotoOutputEvent 事件,返回图片路径 * </pre> */ public class TakePhotoPopupActivity extends Activ

日期选择组件

在开发过程中,偶尔会碰到相同内容多处使用的情况.同一种处理办法,却要相互间互不影响. 以下是个人在项目开发中碰到的一个日期选择组件.HTML5提供了input type = "date",但是由于其兼容性,连IE10都无法启用.所以必须考虑使用其它办法.在此之前考虑了JQ UI的日期时间选择.但介于文件超过400KB,这对于一般小型网站,流量资源十分珍贵的情况下还是自己开发一个直接.简单.轻巧. JS代码如下: 使用方式,十分简单, var b = new A($('#two'));

vue仿京东省市区三级联动选择组件

工作中需要一个盒京东购物车地址选择相似的一个省市区三级联动选择组件,google查了下都是下拉框形式的,于是自己写了一个,希望对使用vue开发项目的朋友有帮助,显示效果如下:使用vue2.0开发 html代码如下 <!--居住地址三级联动选项--> <section class="showChose" v-show="showChose"> <section class="address"> <secti