【JavaScript 关于数据结构】假如你会很快速的处理一些奇怪的数据结构...

简介: 【JavaScript 关于数据结构】假如你会很快速的处理一些奇怪的数据结构...


特殊的穿梭框

先来看一个我自己封装的一个 Antd UI 组件库的一个穿梭框

这个穿梭框记得当时踩了一点坑

起初并没有做组件化,以为是很简单的一个数据穿梭框,没想到…

import { Transfer, Tree } from 'antd'
import React, { useState, useEffect } from 'react'
import { ImportTransferContainer } from './style'
/**
 *
 * @param treeData 左侧用到的数据
 * @param render 穿梭框返回的title
 * @param targetKeys 右侧选中的keys
 * @param dataSource 数据源
 * @param rowKey 哪条该被选中
 * @param generateTree 处理数据
 * @param searchName 搜索名字
 * @returns
 */
const TreeTransfer = ({ // 传参列表
    treeData, dataSource, targetKeys,
    render, rowKey, generateTree, searchName = 'name',
    ...restProps
}) => {
    const transferDataSource = []
    const [data, setData] = useState([])
    useEffect(() => {
        setData(generateTree(dataSource, targetKeys))
    }, [dataSource, targetKeys])
    const flatten = (list = []) => {
        list.forEach((item) => {
            transferDataSource.push(item)
            flatten(item.children)
        })
    }
    flatten(dataSource)
    const isChecked = (selectedKeys, eventKey) => selectedKeys.includes(eventKey)
    const onSearch = (direction, value) => {
        if (direction === 'left') {
            if (value) {
                setData(generateTree(dataSource, targetKeys).filter(item => value.includes(item[searchName])))
            } else {
                setData(generateTree(dataSource, targetKeys))
            }
        }
    }
    return (
        <ImportTransferContainer>
            <Transfer
                {...restProps}
                targetKeys={targetKeys}
                dataSource={transferDataSource}
                className="tree-transfer"
                titles={['源数据列表', '目的数据列表']}
                render={render}
                onSearch={onSearch}
                rowKey={rowKey}
                showSelectAll={false}
            >
                {({ direction, onItemSelect, selectedKeys }) => {
                    if (direction === 'left') {
                        const checkedKeys = [...selectedKeys, ...targetKeys]
                        return (
                            <div>
                                <Tree
                                    blockNode
                                    checkable
                                    checkStrictly
                                    defaultExpandAll
                                    checkedKeys={checkedKeys}
                                    treeData={data}
                                    onCheck={(_, { node: { key } }) => {
                                        onItemSelect(key, !isChecked(checkedKeys, key))
                                    }}
                                    onSelect={(_, { node: { key } }) => {
                                        onItemSelect(key, !isChecked(checkedKeys, key))
                                    }}
                                />
                            </div>
                        )
                    }
                    return null
                }}
            </Transfer>
        </ImportTransferContainer>
    )
}
export default TreeTransfer

不难发现,这是一个根据树形结构显示左侧数据源的一个穿梭框

可惜的是,我并没有用到 children ,因为我只是单层数据结构

看看我是如何使用它的

<VImportTransfer
    rowKey={(e) => e.id} // 每行的标识
    render={(item) => item.name} // 显示名称
    dataSource={systemData} // 包含左侧和右侧共同的数据[数据源]
    targetKeys={targetKeys} // 目标id数组,右侧数据
    showSearch // 搜索框控制显隐
    onChange={onChange} // change事件
    generateTree={generateTree} // 处理 树组件数据方法
/>
// 可能是技术水平有限,只能在这里写这个方法
const generateTree = () => {
  // 在数据源中,使用filter方法过滤掉了systemData中那些id包含在unitKeys数组中的对象
    const treeData = systemData.filter(obj => !unitKeys.includes(obj.id))
    // 使用map方法经过数据处理返回一个新的对象,我们给它加了几个参数
    // 例如:disabled 有相同则置灰
    return treeData.map((item) => ({
        ...item,
        disabled: targetKeys.includes(item.id),
        title: item.name,
        key: item.id
    }))
}
// 从系统库中选择change事件
// 这里是我 onChange 作的各种判断~
// keys:当前选中的id数组
// direction:穿梭框方向移动(left/right)
// movekeys:被移动的id数组
const onChange = (keys, direction, moveKeys) => {
    if (direction === 'right') {
        setDataResoure([...dataResource, ...moveKeys])
    }
    if (direction === 'left') {
        if (moveKeys.length === 1) {
            dataResource.splice(moveKeys[0], 1)
        } else {
            moveKeys.forEach(element => {
                dataResource.splice(element, 1)
            })
        }
        const unitFlag = unitList.map(i => moveKeys.includes(i.id))
        if (unitFlag.includes(true)) {
            return messageApi.open({
                type: 'warning',
                content: t('不能从模板库导入系统库')
            })
        }
        const filteredMoveKeys = moveKeys.filter(key => !keys.includes(key))
        moveKeys.splice(0, moveKeys.length, ...filteredMoveKeys)
    }
    setTargetKeys(keys)
}

注意: 这只是加强数据结构的记忆,这里的代码并不完善,甚至有一些数据的定义我在这里面并没有表明

奇怪的树形结构(真的很奇怪)

废话不多说,先整个图,如图所示,你很快就能看出来这是一个 用户权限 数据…

组建的封装代码

首先看这个组件的封装代码

import React, { useEffect, useRef, useState } from 'react'
import { Tree } from 'antd'
import { useTranslation } from 'react-i18next'
import { TREE_DATA, CONVERSION_TYPE } from './contants'
const UserPromiseTree = ({
    value,
    powerKeys = [],
    setGetTree,
    setGetCheckKey,
    onChange
}) => {
    const { t } = useTranslation()
    const treeData = useRef(TREE_DATA({ t }))
    const [checkedKeys, setCheckedKeys] = useState(powerKeys)
    const [treeParams, setTreeParams] = useState(value)
    useEffect(() => {
        if (value) {
            const tree = formatTreeData(treeData.current)
            setTreeParams(tree)
        }
    }, [])
    useEffect(() => {
        // getTree是根据 checkKeys选中勾选的,如果不手动勾选并拿不到真实的值, 只做为数据处理
        setGetTree(treeParams)
        // getTree 根据 checkKeys 进行勾选渲染
        setGetCheckKey(checkedKeys)
    }, [treeParams])
    // 格式化树状结构数据
    const formatTreeData = (props) => {
        let tree = {}
        props.forEach(item => {
            if (item.children?.length > 0) {
                tree = { ...tree, [item.key]: formatTreeData(item.children) }
            } else {
                tree = { ...tree, [item.key]: !!item.checked }
            }
        })
        return tree
    }
    // 将选中数据转换为给接口的数据
    const dataConversion = (data, checked, key, type = CONVERSION_TYPE.单个) => {
        return data.map(item => {
            if (item.key === key || type === CONVERSION_TYPE.全部) {
                if (item.children?.length > 0) {
                    return { ...item, children: dataConversion(item.children, checked, key, CONVERSION_TYPE.全部) }
                }
                return { ...item, checked }
            } if (item.children?.length > 0) {
                return { ...item, children: dataConversion(item.children, checked, key) }
            }
            return item
        })
    }
    // 点击复选框触发
    const onCheck = (checkedKeysValue, { checked, node }) => {
        const { key } = node
        setCheckedKeys(checkedKeysValue)
        treeData.current = dataConversion(treeData.current, checked, key)
        const tree = formatTreeData(treeData.current)
        setTreeParams(tree)
        onChange(treeParams)
    }
    return (
        <Tree
            checkable
            onCheck={onCheck}
            checkedKeys={checkedKeys}
            treeData={TREE_DATA({ t })}
        />
    )
}
export default UserPromiseTree

中间层结构,勾选判断作用

checkedKeys 是一个具有很多值的数组,若是你在树组件中勾选了它们,它们则会被添加到 checkedKeys

这个数据结构在组件的 onCheck 事件中被添加

setCheckedKeys(checkedKeysValue) // onCheck
[
    "主页",
    "站管理器",
    "模板管理器",
    "项目管理器",
    "数据分析",
    "站管理器页",
    "新增",
    "编辑",
    "删除",
    "模板管理器页",
    "最近打开模板",
    "模板项目",
    "模板对比",
    "新增模板",
    "导入模板",
    "语言"
]

初始数据结构

这是 TREE_DATA, CONVERSION_TYPE 两个引入的数据结构

export const TREE_DATA = ({ t }) => [
    {
        title: t('主页'),
        key: '主页',
        children: [
            {
                title: t('站管理器'),
                key: '站管理器',
                children: [{
                    title: t('站管理器'),
                    key: '站管理器页',
                    children: []
                }, {
                    title: t('新增'),
                    key: '新增',
                    children: []
                }, {
                    title: t('编辑'),
                    key: '编辑',
                    children: []
                }, {
                    title: t('删除'),
                    key: '删除',
                    children: []
                }]
            },
            {
                title: t('模板管理器'),
                key: '模板管理器',
                children: [{
                    title: t('模板管理器'),
                    key: '模板管理器页',
                    children: []
                }, {
                    title: t('最近打开模板'),
                    key: '最近打开模板',
                    children: []
                }, {
                    title: t('模板项目'),
                    key: '模板项目',
                    children: [{
                        title: t('模板对比'),
                        key: '模板对比',
                        children: []
                    }, {
                        title: t('新增模板'),
                        key: '新增模板',
                        children: []
                    }, {
                        title: t('导入模板'),
                        key: '导入模板',
                        children: []
                    }]
                }]
            },
            {
                title: t('项目管理器'),
                key: '项目管理器',
                children: []
            },
            {
                title: t('数据分析'),
                key: '数据分析',
                children: []
            }
        ]
    },
    {
        title: t('侧边栏'),
        key: '侧边栏',
        children: [
            {
                title: t('用户'),
                key: '用户',
                children: [{
                    title: t('切换用户'),
                    key: '切换用户',
                    children: []
                },
                {
                    title: t('修改密码'),
                    key: '修改密码',
                    children: []
                },
                {
                    title: t('用户列表'),
                    key: '用户列表',
                    children: [{
                        title: t('用户列表'),
                        key: '用户列表页',
                        children: []
                    }, {
                        title: t('新增'),
                        key: '新增',
                        children: []
                    }, {
                        title: t('编辑'),
                        key: '编辑',
                        children: []
                    }, {
                        title: t('删除'),
                        key: '删除',
                        children: []
                    }]
                },
                {
                    title: t('角色'),
                    key: '角色',
                    children: []
                }]
            },
            {
                title: t('语言'),
                key: '语言',
                children: []
            },
            {
                title: t('系统管理'),
                key: '系统管理',
                children: [{
                    title: t('单位管理'),
                    key: '单位管理',
                    children: []
                },
                {
                    title: t('结果变量管理器'),
                    key: '结果变量管理器',
                    children: []
                },
                {
                    title: t('语言管理'),
                    key: '语言管理',
                    children: []
                },
                {
                    title: t('输入变量管理器'),
                    key: '输入变量管理器',
                    children: []
                },
                {
                    title: t('信号变量管理器'),
                    key: '信号变量管理器',
                    children: []
                },
                {
                    title: t('函数管理器'),
                    key: '函数管理器',
                    children: []
                },
                {
                    title: t('试样设置'),
                    key: '试样设置',
                    children: []
                },
                {
                    title: t('点检'),
                    key: '点检',
                    children: []
                },
                {
                    title: t('首选项'),
                    key: '首选项',
                    children: []
                },
                {
                    title: t('许可证管理'),
                    key: '许可证管理',
                    children: []
                }]
            },
            {
                title: t('软件配置'),
                key: '软件配置',
                children: [{
                    title: t('硬件管理器'),
                    key: '硬件管理器',
                    children: []
                }, {
                    title: t('软件管理器'),
                    key: '软件管理器',
                    children: []
                }, {
                    title: t('侧边栏站管理器'),
                    key: '侧边栏站管理器',
                    children: []
                }, {
                    title: t('映像管理器'),
                    key: '映像管理器',
                    children: []
                }, {
                    title: t('逻辑资源总览'),
                    key: '逻辑资源总览',
                    children: []
                }, {
                    title: t('总站管理器'),
                    key: '总站管理器',
                    children: []
                }, {
                    title: t('动作管理器'),
                    key: '动作管理器',
                    children: []
                }, {
                    title: t('图片管理器'),
                    key: '图片管理器',
                    children: []
                }, {
                    title: t('语音管理器'),
                    key: '语音管理器',
                    children: []
                }, {
                    title: t('视频管理器'),
                    key: '视频管理器',
                    children: []
                }, {
                    title: t('项目管理'),
                    key: '项目管理',
                    children: []
                }, {
                    title: t('模板管理'),
                    key: '模板管理',
                    children: []
                }, {
                    title: t('指令下发'),
                    key: '指令下发',
                    children: []
                }]
            },
            {
                title: t('信息'),
                key: '信息',
                children: [{
                    title: t('日志'),
                    key: '日志',
                    children: []
                }]
            },
            {
                title: t('帮助'),
                key: '帮助',
                children: []
            }
        ]
    },
    {
        title: t('工具栏'),
        key: '工具栏',
        children: [
            {
                title: t('首页'),
                key: '首页',
                children: []
            },
            {
                title: t('关闭系统'),
                key: '关闭系统',
                children: []
            }]
    },
    {
        title: t('模板设置'),
        key: '模板设置',
        children: [
            {
                title: t('试验流程'),
                key: '试验流程',
                children: [
                    {
                        title: t('试验流程'),
                        key: '试验流程页',
                        children: []
                    },
                    {
                        title: t('控件库'),
                        key: '控件库',
                        children: []
                    }
                ]
            },
            {
                title: t('试验设置'),
                key: '试验设置',
                children: [{
                    title: t('试验设置'),
                    key: '试验设置页',
                    children: []
                }, {
                    title: t('向导'),
                    key: '向导',
                    children: []
                }, {
                    title: t('对话框'),
                    key: '对话框',
                    children: []
                }, {
                    title: t('参数'),
                    key: '参数',
                    children: []
                }, {
                    title: t('单位管理'),
                    key: '单位管理',
                    children: []
                }, {
                    title: t('前进'),
                    key: '前进',
                    children: []
                }, {
                    title: t('返回'),
                    key: '返回',
                    children: []
                }]
            },
            {
                title: t('运行测试'),
                key: '运行测试',
                children: []
            },
            {
                title: t('输出测试数据'),
                key: '输出测试数据',
                children: []
            }]
    }
]
export const CONVERSION_TYPE = {
    单个: 'ONE',
    全部: 'ALL'
}

后端需要的数据结构

这里的数据是将 TREE_DATA 内的 title 变为 key 来进行数据的勾选,其实后端要的就是这种中文结构,它其实是转换成了这样

{
    "模板设置": {
        "试验流程": {
            "控件库": true,
            "试验流程页": true
        },
        "试验设置": {
            "参数": true,
            "向导": true,
            "单位管理": true,
            "试验设置页": true,
            "前进": true,
            "对话框": true,
            "返回": true
        },
        "运行测试": true,
        "输出测试数据": true
    },
    "工具栏": {
        "首页": true,
        "关闭系统": true
    },
    "主页": {
        "站管理器": {
            "新增": true,
            "删除": true,
            "站管理器页": true,
            "编辑": true
        },
        "项目管理器": true,
        "模板管理器": {
            "最近打开模板": true,
            "模板项目": {
                "模板对比": true,
                "新增模板": true,
                "导入模板": true
            },
            "模板管理器页": true
        },
        "数据分析": true
    },
    "侧边栏": {
        "语言": true,
        "用户": {
            "修改密码": true,
            "用户列表": {
                "新增": true,
                "删除": true,
                "用户列表页": true,
                "编辑": true
            },
            "切换用户": true,
            "角色": true
        },
        "帮助": true,
        "信息": {
            "日志": true
        },
        "软件配置": {
            "项目管理": true,
            "视频管理器": true,
            "软件管理器": true,
            "指令下发": true,
            "侧边栏站管理器": true,
            "图片管理器": true,
            "硬件管理器": true,
            "逻辑资源总览": true,
            "映像管理器": true,
            "总站管理器": true,
            "模板管理": true,
            "动作管理器": true,
            "语音管理器": true
        },
        "系统管理": {
            "点检": true,
            "语言管理": true,
            "试样设置": true,
            "结果变量管理器": true,
            "首选项": true,
            "输入变量管理器": true,
            "许可证管理": true,
            "单位管理": true,
            "信号变量管理器": true,
            "函数管理器": true
        }
    }
}

进行数据勾选,最后修改 true/false

/**
* getTree 根据 checkKeys 进行判断.
* 根据checkKeys数组的values与getTree的value进行判断.
* 如果存在于getTree中则getTree中的false为true, 并且支持多层查值
*/
const updateObj = (obj, array) => {
    const newObj = { ...obj }
    Object.keys(newObj).forEach(key => {
        if (typeof newObj[key] === 'object') {
            newObj[key] = updateObj(newObj[key], array)
        } else if (array.includes(key)) {
            newObj[key] = true
        }
    })
    return newObj
}
updateObj(getTree, getCheckKey)  // 初始树组件数据,key数组数据

实时监听 权限树, 很奇怪吧 O.o

相关文章
|
1月前
|
JSON JavaScript 前端开发
解决js中Long类型数据在请求与响应过程精度丢失问题(springboot项目中)
解决js中Long类型数据在请求与响应过程精度丢失问题(springboot项目中)
42 0
|
1月前
|
JavaScript 前端开发
JavaScript随手笔记 --- 对数据进行判断最大位数是否超过八位
JavaScript随手笔记 --- 对数据进行判断最大位数是否超过八位
|
2月前
|
存储 前端开发 JavaScript
JavaScript 中的 BLOB 数据结构的使用介绍
JavaScript 中的 BLOB 数据结构的使用介绍
60 1
|
3月前
|
JSON JavaScript 前端开发
JavaScript 如何对 JSON 数据进行冒泡排序?
JavaScript 如何对 JSON 数据进行冒泡排序?
51 0
|
3月前
|
JavaScript 前端开发
NUS CS1101S:SICP JavaScript 描述:二、使用数据构建抽象
NUS CS1101S:SICP JavaScript 描述:二、使用数据构建抽象
28 0
|
3月前
|
JavaScript 前端开发 算法
什么是Vue.js的响应式系统(reactivity system)?如何实现数据的双向绑定?
什么是Vue.js的响应式系统(reactivity system)?如何实现数据的双向绑定?
23 2
|
3月前
|
JavaScript 前端开发
JavaScript一种新的数据结构类型Map
JavaScript一种新的数据结构类型Map
|
19天前
|
JavaScript 前端开发
EasyUi js 加载数据表格DataGrid
EasyUi js 加载数据表格DataGrid
|
1月前
|
JSON JavaScript 前端开发
JavaScript随手笔记---数组中相同的元素进行分组(数据聚合) groupBy函数
JavaScript随手笔记---数组中相同的元素进行分组(数据聚合) groupBy函数
|
2月前
|
JavaScript
什么是Vue.js的响应式系统(reactivity system)?如何实现数据的双向绑定?
什么是Vue.js的响应式系统(reactivity system)?如何实现数据的双向绑定?
19 0