低代码4之实现json导入导出
低代码4之实现json导入导出
·
目录
低代码4之实现json导入导出
- 功能
- 导入与导出
- 1:editor.jsx 文件 在 buttons添加导出导入按钮
- 2:useCommand.jsx 文件之中 添加更新updateContainer的命令( 这个命令需要加入消息队列之中,并且从外面commands.updateContainer传递的参数,更新最新的值 ) …args
- 3: component / Dailog.jsx 实现弹窗组件
src / package / editor.jsx
import { computed, defineComponent, inject, ref } from "vue";
import "./editor.scss";
import EditorBlock from "./editor-block";
import deepcopy from "deepcopy";
import { useMenuDragger } from "./useMenuDragger";
import { useFocus } from "./useFocus";
import { useBlockDragger } from "./useBlockDragger";
import { useCommand } from "./useCommand";
import { $dialog } from "@/components/Dailog";
export default defineComponent({
props: {
modelValue: {
type: Object,
},
},
emits: ["update:modelValue"], // 1:菜单拖拽功能-03:触发事件 更新app的数据 set之中更新
setup(props, ctx) {
// console.log('props',props.modelValue);
const data = computed({
get() {
return props.modelValue;
},
set(newValue) {
ctx.emit("update:modelValue", deepcopy(newValue));
},
});
const contentStyle = computed(() => ({
width: data.value.container.width + "px",
height: data.value.container.height + "px",
}));
const config = inject("config");
// 1:菜单拖拽功能-02:实现h5的拖拽放入组件容器形成被拖拽的组件 useMenuDragger实现左侧菜单拖拽功能
const containerRef = ref(null);
const { dragstart, dragend } = useMenuDragger(containerRef, data);
// 2:容器内获取焦点功能-01:点击容器时候聚焦与按住shift时候支持多个聚焦;选中后拖拽
const { blockMousedown, containerMousedown, focusData,lastSelectBlock } = useFocus(
data,
(e) => {
// 3:获取焦点后 进行拖拽-02
mousedown(e, focusData);
}
);
// 3:实现组件拖拽-01:
const { mousedown,markLine } = useBlockDragger(focusData,lastSelectBlock,data);
// 4:每一次操作的记录 撤销与重做功能
const {commands} = useCommand(data)
const buttons = [
{lable:'撤销',icon:'',handler:()=>commands.undo()},
{lable:'重做',icon:'',handler:()=>commands.redo()},
{lable:'导出',icon:'',handler:()=>{
$dialog({
title:'导出json使用',
content: JSON.stringify(data.value)
})
}},
{lable:'导入',icon:'',handler:()=>{
$dialog({
title:'导入json使用',
content:'',
footer:true,
onComfirm:(text)=>{
// 需要在useCommand.js文件之中 调用消息队列 实现撤销重做等操作
commands.updateContainer( JSON.parse(text) )
}
})
}}
]
return () => (
<div class="editor">
<div class="editor-left">
{/** 根据config的注册列表 渲染出左侧的物料区域、:1:菜单拖拽功能-01:实现h5的拖拽-draggable */}
{config.componetsList.map((component) => (
<div
class="editor-left-item"
draggable
onDragstart={(e) => dragstart(e, component)}
onDragend={dragend}
>
<span class="editor-left-item-label">{component.label}</span>
<div>{component.preview()}</div>
</div>
))}
</div>
<div className="editor-center">
<div class="editor-top">
{
buttons.map((btn,idx)=>{
return <div class={'editor-top-btn'} onClick={btn.handler}>
<i class={btn.icon}></i>
<span>{btn.lable}</span>
</div>
})
}
</div>
<div class="editor-content">
{/* 负责尝试滚动条 */}
<div class="editor-content-canvas">
{/* 产生内容区域 */}
<div
class="editor-content-canvas_content"
style={contentStyle.value}
ref={containerRef}
onMousedown={containerMousedown}
>
{data.value.blocks.map((block,index) => (
<EditorBlock
class={block.focus ? "editoe-blick-focus" : ""}
block={block}
onMousedown={(e) => blockMousedown(e, block,index)}
></EditorBlock>
))}
{/**辅助线 */}
{ markLine.x !== null && <div class='line-x' style={{left:markLine.x + 'px'}}></div>}
{ markLine.y !== null && <div class='line-y' style={{top:markLine.y + 'px'}}></div>}
</div>
</div>
</div>
</div>
<div class="editor-right">right</div>
</div>
);
},
});
src / package / useCommand.jsx
import deepcopy from "deepcopy";
import { onUnmounted } from "vue";
import { events } from "./events";
export function useCommand(data) {
const state = {
// 前进和后退都需要指针
current: -1, // 前进后退的索引
queue: [], // 存放所有的操作指令
commands: {}, // 制作命令和执行功能一个映射表 undo: ()=>{} ; redo: ()=>{}
commandArray: [], // 存放所有的命令
destoryArray: [], // 销毁的命令
};
const regeister = (command) => {
state.commandArray.push(command);
state.commands[command.name] = (...args) => { // 更新的时候 传递的参数 args
// 命令名字 映射 执行函数
const { redo, undo } = command.execute(...args);
redo();
if (!command.pushQueue) {
// 判断是否需要放入队列之中 不需要直接return
return;
}
let { queue, current } = state;
// 如果 放入了组件1 组件2 点击撤回 放入组件3 => 最终结果为 组件1 组件3
if (queue.length > 0) {
queue = queue.slice(0, current + 1); // 以当前的位置 截取最新的数据(可能撤销了几个),以当前最新的current来计算值
state.queue = queue;
}
queue.push({
// 保存指令的前进和后退
redo,
undo,
});
state.current = current + 1;
};
};
// 注册我们需要的命令
// 重做命令
regeister({
name: "redo",
keyboard: "ctrl+y",
execute() {
return {
redo() {
let item = state.queue[state.current + 1]; // 找到当前的下一步操作
if (item) {
// 若是有值 则撤销
item.redo && item.redo();
state.current++;
}
},
};
},
});
// 撤销命令
regeister({
name: "undo",
keyboard: "ctrl+z",
execute() {
return {
redo() {
if (state.current == -1) return; // 若是没有 则退出
let item = state.queue[state.current]; // 找到当前的上一步操作
if (item) {
// 若是有值 则撤销
item.undo && item.undo();
state.current--;
}
},
};
},
});
// 拖拽命令 如果希望将操作放到队列中 可以增加一个属性 标识等会操作的要放到队列中
regeister({
name: "drag",
pushQueue: true,
init() {
// 初始化操作 默认就执行
this.before = null; // 操作前的数据
// 监控拖拽开始事件,保存当前状态
const start = () => (this.before = deepcopy(data.value.blocks));
// 拖拽后 需要触发的对应指令
const end = () => state.commands.drag();
events.on("start", start);
events.on("end", end);
return () => {
// 卸载函数
events.off("start", start);
events.off("end", end);
};
},
execute() {
let before = this.before; // 之前的
let after = data.value.blocks; // 最新的
return {
redo() {
// 更新最新的值 默认一松手的时候 把当前的事情处理了
data.value = { ...data.value, blocks: after };
},
undo() {
// 撤销 往前面退一个操作
data.value = { ...data.value, blocks: before };
},
};
},
});
// 更新 带有历史记录的模式
regeister({
name:'updateContainer', // 更新整个容器
pushQueue: true, // 丢到消息队列
execute(newValue){ // newValue 拿到调用 commands.updateContainer传递的参数
let stete = {
before: data.value, // 当前的值
after: newValue // 导入后最新的值
}
return {
redo:()=>{// 重做
data.value = stete.after
},
undo:()=>{// 撤销
data.value = stete.before
}
}
}
})
const keyboardEvent = (() => {
const init = () => {
// 初始化事件
const keyCodes = {
90: "z",
89: "y",
};
const onKeydown = (e) => {
const { ctrlKey, keyCode } = e;
let keyStrings = []; // ctrl+z 或者 ctrl+y
if (ctrlKey) keyStrings.push("ctrl");
keyStrings.push(keyCodes[keyCode]);
keyStrings = keyStrings.join("+");
state.commandArray.forEach(({keyboard, name}) => {
if (!keyboard) return;
if (keyboard === keyStrings) {
state.commands[name]();
e.preventDefault(); // 阻止默认行为
}
});
};
window.addEventListener("keydown", onKeydown);
return () => {
// 销毁事件
window.removeEventListener("keydown", onKeydown);
};
};
return init;
})();
// 查看是否有拖拽后的效果 有立即执行更新操作 并且存放当前数据(用于销毁撤销)
(() => {
// 监听键盘事件 ctrl + z 实现撤销
state.destoryArray.push(keyboardEvent());
// 查看是否有拖拽后的效果 有立即执行更新操作 并且存放当前数据(用于销毁撤销)
state.commandArray.forEach(
(command) => command.init && state.destoryArray.push(command.init())
);
})();
onUnmounted(() => {
// 清理绑定的事件
state.destoryArray.forEach((fn) => fn && fn());
});
return state;
}
src / component / Dailog.jsx
import { ElButton, ElDialog, ElInput } from "element-plus";
import { createVNode, defineComponent, reactive, render } from "vue";
const DialogComponent = defineComponent({
props: {
option: {
type: Object,
},
},
setup(props, ctx) {
const state = reactive({
option:props.option, // 用户给组件的属性
isShow: false,
});
// 当前组件 想要暴露那些方法
ctx.expose({
showDialog(option) {
state.option = option
state.isShow = true;
},
});
const onCancel = ()=>{
state.isShow = false
}
const onComfirm = ()=>{
state.isShow = false;
state.option.onComfirm && state.option.onComfirm(state.option.content)
}
return () => {
return <ElDialog v-model={state.isShow} title={state.option.title}>
{{
// 默认插槽
default:()=> <ElInput type="textarea" v-model={state.option.content} rows={10}></ElInput>,
// 底部插槽 若是有footer则展示 div
footer:()=> state.option.footer && <div>
<ElButton onClick={onCancel}>取消</ElButton>
<ElButton type='primary' onClick={onComfirm}>确定</ElButton>
</div>
}}
</ElDialog>;
};
},
});
let vm;
export function $dialog(option) {
console.log("option", option);
// ele-plus之中的 el-dialog组件
// 手动挂载组件
if (!vm) {
let el = document.createElement("div");
// 讲DialogComponent组件 渲染为 虚拟节点
vm = createVNode(DialogComponent, { option });
document.body.appendChild((render(vm, el), el)); // 把虚拟节点渲染为真实的节点 并且添加为body子节点
}
// 将组件渲染到 el 元素之中
let { showDialog } = vm.component.exposed;
showDialog(option);
}
更多推荐



所有评论(0)