Next-Admin最佳实践!支持可视化拖拽模块

hello,大家好,我是徐小夕,之前和大家分享了很多可视化低代码 的最佳实践,以及前端工程化 的实战项目,今天继续和大家分享一下我开源的比较有价值的项目------Next-Admin ,目前已经支持拖拽搭建模块,并且支持:

  • 参考线吸附
  • 组件成组和取消成组
  • 组件对齐
  • 支持多选,键盘多选
  • 开箱即用的拖拽搭建方案

开源地址:https://github.com/MrXujiang/next-admin

在线demo:http://next-admin.com

往期精彩

模块演示

技术实现

拖拽模块我采用了 movable, 并研究了它的大量 API,最终实现了我想要的效果,当然我还设计了一套数据结构,如果大家对可视化搭建感兴趣,也可以扩展成自己的拖拽搭建结构。

元素多选 我采用了 selecto 模块,成组管理器 我采用了 @moveable/helper, 当然在使用这些库的时候也踩了不少坑,好在已经完美解决。

下面分享一个简单的数据结构,以支持我们的元素自由搭建:

js 复制代码
const schema = {
    "Button": {
        id: 'wep_001',
        name: 'Button',
        type: 'base', // 基础类型组件
        base: {
            width: 120,
            height: 36,
            transform: 'translate(100px,100px)'
        }
    },
    "Image": {
        id: 'wep_002',
        name: 'Image',
        type: 'base', // 基础类型组件
        base: {
            width: 120,
            height: 120,
            url: '',
            transform: 'translate(300px,160px)'
        }
    }
}

export default schema

工具条实现

对于工具条的实现,我做了统一的封装,以便后期可能更低成本的维护和管理:

  • config 工具条配置
  • actions 工具条选项对应的功能方法

接下来看看工具条的配置:

js 复制代码
const toolbar = {
    base: [
        {
            key: 'group',
            icon: <GroupOutlined />,
            text: '成组',
        },
        {
            key: 'ungroup',
            icon: <UngroupOutlined />,
            text: '取消成组'
        },
        {
            key: 'left',
            icon: <AlignLeftOutlined />,
            text: '左对齐'
        },
        // ... 其他工具条配置
        {
            key: 'v-space',
            icon: <PicCenterOutlined />,
            text: '垂直分布空间'
        },
        {
            key: 'h-space',
            icon: <PicCenterOutlined style={{transform: 'rotate(-90deg)'}} />,
            text: '水平分布空间'
        },
        
    ]
}

工具条方法封装:

js 复制代码
const handleOperate = (key: string) => {
        // ... some function
        // 顶对齐实现
        if(key === 'top') {
            const rect = moveableRef.current!.getRect();
            // console.log(rect)
            const moveables = moveableRef.current!.getMoveables();

            if (moveables.length <= 1) {
                return;
            }
            moveables.forEach(child => {
                child.request<DraggableRequestParam>("draggable", {
                    y: rect.top,
                }, true);
            });

            moveableRef.current?.updateRect();
            return
        }
        // 底对齐
        if(key === 'bottom') {
            const rect = moveableRef.current!.getRect();
            const moveables = moveableRef.current!.getMoveables();
            if (moveables.length <= 1) {
                return;
            }
            moveables.forEach(child => {
                child.request<DraggableRequestParam>("draggable", {
                    y: rect.top + rect.height - (child.props?.target ? (child.props.target as any).offsetHeight : 0),
                }, true);
            });
            moveableRef.current?.updateRect();
            return
        }

        // ... 其他工具条方法
        // 水平分布
        if(key === 'h-space') {
            const groupRect = moveableRef.current!.getRect();
            const moveables = moveableRef.current!.getMoveables();
            let left = groupRect.left;

            if (moveables.length <= 1) {
                return;
            }
            const gap = (groupRect.width - groupRect.children!.reduce((prev, cur) => {
                return prev + cur.width;
            }, 0)) / (moveables.length - 1);

            moveables.sort((a, b) => {
                return a.state.left - b.state.left;
            });
            moveables.forEach(child => {
                const rect = child.getRect();

                child.request<DraggableRequestParam>("draggable", {
                    x: left,
                }, true);

                left += rect.width + gap;
            });

            moveableRef.current?.updateRect();
            return
        }

    }

通过以上的封装方式我们就能轻松扩展自己的工具条啦~

接下来我们看看工具条实现的效果:

当然代码我已经提交到 github 上了, 大家感兴趣可以参考研究一下。

开源地址:https://github.com/MrXujiang/next-admin

多选 & 成组实现

下面直接上代码:

js 复制代码
<Selecto
    ref={selectoRef}
    // dragContainer={container.current}
    selectableTargets={[".wep-area .cube"]}
    hitRate={0}
    selectByClick={true}
    selectFromInside={false}
    toggleContinueSelect={["shift"]}
    ratio={0}
    onDragStart={e => {
        const moveable = moveableRef.current!;
        const target = e.inputEvent.target;
        const flatted = deepFlat(targets);

        if (
            target.tagName === "BUTTON"
            || moveable.isMoveableElement(target)
            || flatted.some(t => t === target || t.contains(target))
        ) {
            e.stop();
        }
        e.data.startTargets = targets;
    }}
    onSelect={e => {
        const {
            startAdded,
            startRemoved,
            isDragStartEnd,
        } = e;

        if (isDragStartEnd) {
            return;
        }
        const nextChilds = groupManager.selectSameDepthChilds(
            e.data.startTargets,
            startAdded,
            startRemoved,
        );

        setSelectedTargets(nextChilds.targets());
    }}
    onSelectEnd={e => {
        const {
            isDragStartEnd,
            isClick,
            added,
            removed,
            inputEvent,
        } = e;
        const moveable = moveableRef.current!;

        if (isDragStartEnd) {
            inputEvent.preventDefault();

            moveable.waitToChangeTarget().then(() => {
                moveable.dragStart(inputEvent);
            });
        }
        let nextChilds: TargetList;

        if (isDragStartEnd || isClick) {
            if (isCommand) {
                nextChilds = groupManager.selectSingleChilds(targets, added, removed);
            } else {
                nextChilds = groupManager.selectCompletedChilds(targets, added, removed, isShift);
            }

        } else {
            nextChilds = groupManager.selectSameDepthChilds(e.data.startTargets, added, removed);
        }
        e.currentTarget.setSelectedTargets(nextChilds.flatten());
        setSelectedTargets(nextChilds.targets());
    }}
></Selecto>

完整代码都同步到 Next-Admin 了, 如果大家感兴趣也可以研究一下。

后期规划

后续会在 Next-Admin 中集成更多最佳实践,也欢迎感兴趣的朋友一起交流讨论。

如果你对 next 开发或者需要开发一套管理系统, 我相信 Next-Admin 会给你开发和学习的灵感。

同时也欢迎和我一起贡献, 让它变得更优秀~

github地址: https://github.com/MrXujiang/next-admin

演示地址:http://next-admin.com

由于服务器在国外, 所以建议大家git到本地体验~

欢迎star + 反馈~

更多推荐

可视化表单&试卷搭建平台技术详解

爆肝1000小时, Dooring零代码搭建平台3.5正式上线

相关推荐
无限进步_2 分钟前
【C++】寻找数组中出现次数超过一半的数字:三种解法深度剖析
开发语言·c++·git·算法·leetcode·github·visual studio
Go 言 Go 语3 分钟前
Claude Code 核心加载机制详解
服务器·前端·数据库
朝阳394 分钟前
CSS【详解】给子元素添加间距的最佳实践(含space 和 gap 的区别图解和面试的标准答案)
前端·css
s6516654966 分钟前
Makefile语法学习
java·linux·前端
悟空爬虫-彪哥14 分钟前
Stich接入Codex教程
java·前端·数据库
深海鱼在掘金14 分钟前
Next.js从入门到实战保姆级教程(第十五章):部署运维与 CI/CD
前端·ci/cd·next.js
Mr.mjw15 分钟前
vue中封装一个进度条组件,无需引入,纯css
javascript·css·vue.js
veminhe17 分钟前
Java后端、PC前端学习备忘
前端
深海鱼在掘金18 分钟前
Next.js从入门到实战保姆级教程(第十七章):综合实战项目(下)——前端页面、性能优化与部署
前端·ci/cd·next.js
深海鱼在掘金18 分钟前
Next.js从入门到实战保姆级教程(第十六章):实战项目(上)——全栈博客系统架构与核心功能
前端·数据库·next.js