Next-Admin最佳實(shí)踐!支持可視化拖拽模塊
模塊演示
圖片
技術(shù)實(shí)現(xiàn)
拖拽模塊我采用了 movable, 并研究了它的大量 API,最終實(shí)現(xiàn)了我想要的效果,當(dāng)然我還設(shè)計(jì)了一套數(shù)據(jù)結(jié)構(gòu),如果大家對(duì)可視化搭建感興趣,也可以擴(kuò)展成自己的拖拽搭建結(jié)構(gòu)。
圖片
元素多選我采用了 selecto 模塊,成組管理器我采用了 @moveable/helper, 當(dāng)然在使用這些庫(kù)的時(shí)候也踩了不少坑,好在已經(jīng)完美解決。
下面分享一個(gè)簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),以支持我們的元素自由搭建:
const schema = {
"Button": {
id: 'wep_001',
name: 'Button',
type: 'base', // 基礎(chǔ)類型組件
base: {
width: 120,
height: 36,
transform: 'translate(100px,100px)'
}
},
"Image": {
id: 'wep_002',
name: 'Image',
type: 'base', // 基礎(chǔ)類型組件
base: {
width: 120,
height: 120,
url: '',
transform: 'translate(300px,160px)'
}
}
}
export default schema
工具條實(shí)現(xiàn)
圖片
對(duì)于工具條的實(shí)現(xiàn),我做了統(tǒng)一的封裝,以便后期可能更低成本的維護(hù)和管理:
- config 工具條配置
- actions 工具條選項(xiàng)對(duì)應(yīng)的功能方法
接下來(lái)看看工具條的配置:
const toolbar = {
base: [
{
key: 'group',
icon: <GroupOutlined />,
text: '成組',
},
{
key: 'ungroup',
icon: <UngroupOutlined />,
text: '取消成組'
},
{
key: 'left',
icon: <AlignLeftOutlined />,
text: '左對(duì)齊'
},
// ... 其他工具條配置
{
key: 'v-space',
icon: <PicCenterOutlined />,
text: '垂直分布空間'
},
{
key: 'h-space',
icon: <PicCenterOutlined style={{transform: 'rotate(-90deg)'}} />,
text: '水平分布空間'
},
]
}
工具條方法封裝:
const handleOperate = (key: string) => {
// ... some function
// 頂對(duì)齊實(shí)現(xiàn)
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
}
// 底對(duì)齊
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
}
}
通過(guò)以上的封裝方式我們就能輕松擴(kuò)展自己的工具條啦~
接下來(lái)我們看看工具條實(shí)現(xiàn)的效果:
圖片
當(dāng)然代碼我已經(jīng)提交到 github 上了, 大家感興趣可以參考研究一下。
開(kāi)源地址:https://github.com/MrXujiang/next-admin
多選 & 成組實(shí)現(xiàn)
圖片
下面直接上代碼:
<Selecto
ref={selectoRef}
// dragCnotallow={container.current}
selectableTargets={[".wep-area .cube"]}
hitRate={0}
selectByClick={true}
selectFromInside={false}
toggleCnotallow={["shift"]}
ratio={0}
notallow={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;
}}
notallow={e => {
const {
startAdded,
startRemoved,
isDragStartEnd,
} = e;
if (isDragStartEnd) {
return;
}
const nextChilds = groupManager.selectSameDepthChilds(
e.data.startTargets,
startAdded,
startRemoved,
);
setSelectedTargets(nextChilds.targets());
}}
notallow={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 了, 如果大家感興趣也可以研究一下。