zoukankan      html  css  js  c++  java
  • 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',  '5%'},
    150             { title: '角色名称',dataIndex: 'role_name',key: 'role_name',  '10%'},
    151             { title: '权限', dataIndex: 'permission',key: 'permission',   '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',  '15%',},
    172             { title: '更新时间', dataIndex: 'update_time',key: 'update_time',  '15%', },
    173             { title: '状态', dataIndex: 'is_del',key: 'is_del',  '7%',
    174                 render: (text, record) => {
    175                     return (
    176                         statusObj[record["is_del"]]
    177                     )
    178                 }
    179             },
    180 
    181             { title: '操作', key: '#',  '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                      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={{  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;
  • 相关阅读:
    安卓巴士诚招版主,希望各位巴友踊跃加入我们!
    android用户界面之菜单(Menu)教程实例汇总
    360将推出多款360用户特供手机
    安卓巴士最新精选文章,请您查阅
    android用户界面之SeekBar教程实例汇总
    Android OpenGL ES 开发教程小结
    android用户界面之按钮(Button)教程实例汇
    Seleniumwebdriver系列教程(5)————如何定位frame中的元素
    Seleniumwebdriver系列教程(10)————如何智能的等待页面加载完成
    Seleniumwebdriver系列教程(6)————如何捕获弹出窗口
  • 原文地址:https://www.cnblogs.com/zhixi/p/10419878.html
Copyright © 2011-2022 走看看