40 changed files with 847 additions and 806 deletions
@ -0,0 +1,76 @@ |
|||
/** |
|||
* @name: createProps |
|||
* @author: 卜启缘 |
|||
* @date: 2021/5/30 10:50 |
|||
* @description:createProps |
|||
* @update: 2021/5/30 10:50 |
|||
*/ |
|||
|
|||
import { |
|||
createEditorInputProp, |
|||
createEditorSelectProp, |
|||
createEditorSwitchProp, |
|||
createEditorTableProp |
|||
} from '@/visual-editor/visual-editor.props' |
|||
|
|||
// 对齐方式
|
|||
const alignOptions = [ |
|||
{ |
|||
label: '左对齐', |
|||
value: 'left' |
|||
}, |
|||
{ |
|||
label: '右对齐', |
|||
value: 'right' |
|||
}, |
|||
{ |
|||
label: '居中对齐', |
|||
value: 'center' |
|||
} |
|||
] |
|||
|
|||
export const compProps = { |
|||
'slots.default.children': createEditorTableProp({ |
|||
label: '表单项', |
|||
option: { |
|||
options: [ |
|||
{ label: '显示值', field: 'label' }, |
|||
{ label: '绑定值', field: 'value' }, |
|||
{ label: '备注', field: 'comments' } |
|||
], |
|||
showKey: 'label' |
|||
}, |
|||
defaultValue: [] |
|||
}), |
|||
colon: createEditorSwitchProp({ label: '是否在 label 后面添加冒号' }), |
|||
disabled: createEditorSwitchProp({ label: '是否禁用表单中的所有输入框' }), |
|||
errorMessageAlign: createEditorSelectProp({ |
|||
label: '错误提示文案对齐方式', |
|||
defaultValue: 'left', |
|||
options: alignOptions |
|||
}), |
|||
inputAlign: createEditorSelectProp({ |
|||
label: '输入框对齐方式', |
|||
defaultValue: 'left', |
|||
options: alignOptions |
|||
}), |
|||
labelAlign: createEditorSelectProp({ |
|||
label: '表单项 label 对齐方式', |
|||
defaultValue: 'left', |
|||
options: alignOptions |
|||
}), |
|||
labelWidth: createEditorInputProp({ label: '表单项 label 宽度,默认单位为px' }), |
|||
readonly: createEditorSwitchProp({ label: '是否将表单中的所有输入框设置为只读状态' }), |
|||
scrollToError: createEditorSwitchProp({ |
|||
label: '在提交表单且校验不通过时滚动至错误的表单项' |
|||
}), |
|||
showError: createEditorSwitchProp({ label: '是否在校验不通过时标红输入框' }), |
|||
showErrorMessage: createEditorSwitchProp({ |
|||
label: '是否在校验不通过时在输入框下方展示错误提示' |
|||
}), |
|||
submitOnEnter: createEditorSwitchProp({ label: '是否在按下回车键时提交表单' }), |
|||
validateFirst: createEditorSwitchProp({ label: '是否在某一项校验不通过时停止校验' }), |
|||
validateTrigger: createEditorInputProp({ |
|||
label: '表单校验触发时机,可选值为 onChange、onSubmit,详见下表' |
|||
}) |
|||
} |
|||
@ -1,2 +1,9 @@ |
|||
import { App } from 'vue' |
|||
import '@vant/touch-emulator' |
|||
import 'vant/lib/index.css' |
|||
|
|||
import { Lazyload } from 'vant' |
|||
|
|||
export const setupVant = (app: App) => { |
|||
app.use(Lazyload) |
|||
} |
|||
|
|||
@ -0,0 +1,8 @@ |
|||
/** |
|||
* @name: index |
|||
* @author: 卜启缘 |
|||
* @date: 2021/5/30 10:57 |
|||
* @description:index |
|||
* @update: 2021/5/30 10:57 |
|||
*/ |
|||
export { TablePropEditor } from './table-prop-editor/table-prop-editor' |
|||
@ -0,0 +1,11 @@ |
|||
/** |
|||
* @name: index.d |
|||
* @author: 卜启缘 |
|||
* @date: 2021/5/30 10:40 |
|||
* @description:index.d |
|||
* @update: 2021/5/30 10:40 |
|||
*/ |
|||
declare type LabelValueOptions = { |
|||
label: string |
|||
value: any |
|||
}[] |
|||
@ -1,232 +1,232 @@ |
|||
import { useCommander } from './plugins/command.plugin' |
|||
import { VisualEditorBlockData, VisualEditorModelValue } from './visual-editor.utils' |
|||
import { cloneDeep } from 'lodash' |
|||
|
|||
export function useVisualCommand({ |
|||
focusData, |
|||
updateBlocks, |
|||
dataModel, |
|||
dragstart, |
|||
dragend |
|||
}: { |
|||
focusData: { value: { focus: VisualEditorBlockData[]; unFocus: VisualEditorBlockData[] } } |
|||
updateBlocks: (blocks?: VisualEditorBlockData[]) => void |
|||
dataModel: { value: VisualEditorModelValue } |
|||
dragstart: { on: (cb: () => void) => void; off: (cb: () => void) => void } |
|||
dragend: { on: (cb: () => void) => void; off: (cb: () => void) => void } |
|||
}) { |
|||
const commander = useCommander() |
|||
|
|||
/** |
|||
* 删除命令 |
|||
* @author 卜启缘 |
|||
* @date 2021/4/22 11:37 下午 |
|||
*/ |
|||
commander.registry({ |
|||
name: 'delete', |
|||
keyboard: ['backspace', 'delete', 'ctrl+d'], |
|||
execute: () => { |
|||
// console.log('执行删除命令')
|
|||
const data = { |
|||
before: dataModel.value.blocks, |
|||
after: focusData.value.unFocus |
|||
} |
|||
return { |
|||
redo: () => { |
|||
// console.log('重做删除命令')
|
|||
updateBlocks(cloneDeep(data.after)) |
|||
}, |
|||
undo: () => { |
|||
// console.log('撤回删除命令')
|
|||
updateBlocks(cloneDeep(data.before)) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
/** |
|||
* 拖拽命令,适用于三种情况: |
|||
* - 从菜单拖拽组件到容器画布; |
|||
* - 在容器中拖拽组件调整位置 |
|||
* - 拖拽调整组件的宽度和高度; |
|||
* @author 卜启缘 |
|||
* @date 2021/4/22 11:38 下午 |
|||
*/ |
|||
commander.registry({ |
|||
name: 'drag', |
|||
init() { |
|||
this.data = { before: null as null | VisualEditorBlockData[] } |
|||
const handler = { |
|||
dragstart: () => (this.data.before = cloneDeep(dataModel.value.blocks)), |
|||
dragend: () => commander.state.commands.drag() |
|||
} |
|||
dragstart.on(handler.dragstart) |
|||
dragend.on(handler.dragend) |
|||
return () => { |
|||
dragstart.off(handler.dragstart) |
|||
dragend.off(handler.dragend) |
|||
} |
|||
}, |
|||
execute() { |
|||
const before = cloneDeep(this.data.before) |
|||
const after = cloneDeep(dataModel.value.blocks) |
|||
return { |
|||
redo: () => { |
|||
updateBlocks(cloneDeep(after)) |
|||
}, |
|||
undo: () => { |
|||
updateBlocks(cloneDeep(before)) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
commander.registry({ |
|||
name: 'clear', |
|||
execute: () => { |
|||
const data = { |
|||
before: cloneDeep(dataModel.value.blocks), |
|||
after: cloneDeep([]) |
|||
} |
|||
return { |
|||
redo: () => { |
|||
updateBlocks(cloneDeep(data.after)) |
|||
}, |
|||
undo: () => { |
|||
updateBlocks(cloneDeep(data.before)) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
commander.registry({ |
|||
name: 'placeTop', |
|||
keyboard: 'ctrl+up', |
|||
execute: () => { |
|||
const data = { |
|||
before: cloneDeep(dataModel.value.blocks), |
|||
after: cloneDeep( |
|||
(() => { |
|||
const { focus, unFocus } = focusData.value |
|||
const maxZIndex = |
|||
unFocus.reduce((prev, block) => Math.max(prev, block.zIndex), -Infinity) + 1 |
|||
focus.forEach((block) => (block.zIndex = maxZIndex)) |
|||
return cloneDeep(dataModel.value.blocks) |
|||
})() |
|||
) |
|||
} |
|||
return { |
|||
redo: () => { |
|||
updateBlocks(cloneDeep(data.after)) |
|||
}, |
|||
undo: () => { |
|||
updateBlocks(cloneDeep(data.before)) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
commander.registry({ |
|||
name: 'placeBottom', |
|||
keyboard: 'ctrl+down', |
|||
execute: () => { |
|||
const data = { |
|||
before: cloneDeep(dataModel.value.blocks), |
|||
after: cloneDeep( |
|||
(() => { |
|||
const { focus, unFocus } = focusData.value |
|||
let minZIndex = |
|||
unFocus.reduce((prev, block) => Math.min(prev, block.zIndex), Infinity) - 1 |
|||
if (minZIndex < 0) { |
|||
const dur = Math.abs(minZIndex) |
|||
unFocus.forEach((block) => (block.zIndex += dur)) |
|||
minZIndex = 0 |
|||
} |
|||
focus.forEach((block) => (block.zIndex = minZIndex)) |
|||
return cloneDeep(dataModel.value.blocks) |
|||
})() |
|||
) |
|||
} |
|||
return { |
|||
redo: () => { |
|||
updateBlocks(cloneDeep(data.after)) |
|||
}, |
|||
undo: () => { |
|||
updateBlocks(cloneDeep(data.before)) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
commander.registry({ |
|||
name: 'updateBlock', |
|||
execute: (newBlock: VisualEditorBlockData, oldBlock: VisualEditorBlockData) => { |
|||
let blocks = cloneDeep(dataModel.value.blocks || []) |
|||
const data = { |
|||
before: blocks, |
|||
after: (() => { |
|||
blocks = [...blocks] |
|||
const index = dataModel.value.blocks!.indexOf(oldBlock) |
|||
if (index > -1) { |
|||
blocks.splice(index, 1, newBlock) |
|||
} |
|||
return cloneDeep(blocks) |
|||
})() |
|||
} |
|||
return { |
|||
redo: () => { |
|||
updateBlocks(cloneDeep(data.after)) |
|||
}, |
|||
undo: () => { |
|||
updateBlocks(cloneDeep(data.before)) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
commander.registry({ |
|||
name: 'updateModelValue', |
|||
execute: (val: VisualEditorModelValue) => { |
|||
const data = { |
|||
before: cloneDeep(dataModel.value), |
|||
after: cloneDeep(val) |
|||
} |
|||
return { |
|||
redo: () => { |
|||
dataModel.value = data.after |
|||
}, |
|||
undo: () => { |
|||
dataModel.value = data.before |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
commander.registry({ |
|||
name: 'selectAll', |
|||
followQueue: false, |
|||
keyboard: 'ctrl+a', |
|||
execute: () => { |
|||
return { |
|||
redo: () => { |
|||
;(dataModel.value.blocks || []).forEach((block) => (block.focus = true)) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
commander.init() |
|||
|
|||
return { |
|||
undo: () => commander.state.commands.undo(), |
|||
redo: () => commander.state.commands.redo(), |
|||
delete: () => commander.state.commands.delete(), |
|||
clear: () => commander.state.commands.clear(), |
|||
placeTop: () => commander.state.commands.placeTop(), |
|||
placeBottom: () => commander.state.commands.placeBottom(), |
|||
updateBlock: (newBlock: VisualEditorBlockData, oldBlock: VisualEditorBlockData) => |
|||
commander.state.commands.updateBlock(newBlock, oldBlock), |
|||
updateModelValue: (val: VisualEditorModelValue) => |
|||
commander.state.commands.updateModelValue(val) |
|||
} |
|||
} |
|||
// import { useCommander } from './plugins/command.plugin'
|
|||
// import { VisualEditorBlockData, VisualEditorModelValue } from './visual-editor.utils'
|
|||
// import { cloneDeep } from 'lodash'
|
|||
//
|
|||
// export function useVisualCommand({
|
|||
// focusData,
|
|||
// updateBlocks,
|
|||
// dataModel,
|
|||
// dragstart,
|
|||
// dragend
|
|||
// }: {
|
|||
// focusData: { value: { focus: VisualEditorBlockData[]; unFocus: VisualEditorBlockData[] } }
|
|||
// updateBlocks: (blocks?: VisualEditorBlockData[]) => void
|
|||
// dataModel: { value: VisualEditorModelValue }
|
|||
// dragstart: { on: (cb: () => void) => void; off: (cb: () => void) => void }
|
|||
// dragend: { on: (cb: () => void) => void; off: (cb: () => void) => void }
|
|||
// }) {
|
|||
// const commander = useCommander()
|
|||
//
|
|||
// /**
|
|||
// * 删除命令
|
|||
// * @author 卜启缘
|
|||
// * @date 2021/4/22 11:37 下午
|
|||
// */
|
|||
// commander.registry({
|
|||
// name: 'delete',
|
|||
// keyboard: ['backspace', 'delete', 'ctrl+d'],
|
|||
// execute: () => {
|
|||
// // console.log('执行删除命令')
|
|||
// const data = {
|
|||
// before: dataModel.value.blocks,
|
|||
// after: focusData.value.unFocus
|
|||
// }
|
|||
// return {
|
|||
// redo: () => {
|
|||
// // console.log('重做删除命令')
|
|||
// updateBlocks(cloneDeep(data.after))
|
|||
// },
|
|||
// undo: () => {
|
|||
// // console.log('撤回删除命令')
|
|||
// updateBlocks(cloneDeep(data.before))
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
//
|
|||
// /**
|
|||
// * 拖拽命令,适用于三种情况:
|
|||
// * - 从菜单拖拽组件到容器画布;
|
|||
// * - 在容器中拖拽组件调整位置
|
|||
// * - 拖拽调整组件的宽度和高度;
|
|||
// * @author 卜启缘
|
|||
// * @date 2021/4/22 11:38 下午
|
|||
// */
|
|||
// commander.registry({
|
|||
// name: 'drag',
|
|||
// init() {
|
|||
// this.data = { before: null as null | VisualEditorBlockData[] }
|
|||
// const handler = {
|
|||
// dragstart: () => (this.data.before = cloneDeep(dataModel.value.blocks)),
|
|||
// dragend: () => commander.state.commands.drag()
|
|||
// }
|
|||
// dragstart.on(handler.dragstart)
|
|||
// dragend.on(handler.dragend)
|
|||
// return () => {
|
|||
// dragstart.off(handler.dragstart)
|
|||
// dragend.off(handler.dragend)
|
|||
// }
|
|||
// },
|
|||
// execute() {
|
|||
// const before = cloneDeep(this.data.before)
|
|||
// const after = cloneDeep(dataModel.value.blocks)
|
|||
// return {
|
|||
// redo: () => {
|
|||
// updateBlocks(cloneDeep(after))
|
|||
// },
|
|||
// undo: () => {
|
|||
// updateBlocks(cloneDeep(before))
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
//
|
|||
// commander.registry({
|
|||
// name: 'clear',
|
|||
// execute: () => {
|
|||
// const data = {
|
|||
// before: cloneDeep(dataModel.value.blocks),
|
|||
// after: cloneDeep([])
|
|||
// }
|
|||
// return {
|
|||
// redo: () => {
|
|||
// updateBlocks(cloneDeep(data.after))
|
|||
// },
|
|||
// undo: () => {
|
|||
// updateBlocks(cloneDeep(data.before))
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
//
|
|||
// commander.registry({
|
|||
// name: 'placeTop',
|
|||
// keyboard: 'ctrl+up',
|
|||
// execute: () => {
|
|||
// const data = {
|
|||
// before: cloneDeep(dataModel.value.blocks),
|
|||
// after: cloneDeep(
|
|||
// (() => {
|
|||
// const { focus, unFocus } = focusData.value
|
|||
// const maxZIndex =
|
|||
// unFocus.reduce((prev, block) => Math.max(prev, block.zIndex), -Infinity) + 1
|
|||
// focus.forEach((block) => (block.zIndex = maxZIndex))
|
|||
// return cloneDeep(dataModel.value.blocks)
|
|||
// })()
|
|||
// )
|
|||
// }
|
|||
// return {
|
|||
// redo: () => {
|
|||
// updateBlocks(cloneDeep(data.after))
|
|||
// },
|
|||
// undo: () => {
|
|||
// updateBlocks(cloneDeep(data.before))
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
//
|
|||
// commander.registry({
|
|||
// name: 'placeBottom',
|
|||
// keyboard: 'ctrl+down',
|
|||
// execute: () => {
|
|||
// const data = {
|
|||
// before: cloneDeep(dataModel.value.blocks),
|
|||
// after: cloneDeep(
|
|||
// (() => {
|
|||
// const { focus, unFocus } = focusData.value
|
|||
// let minZIndex =
|
|||
// unFocus.reduce((prev, block) => Math.min(prev, block.zIndex), Infinity) - 1
|
|||
// if (minZIndex < 0) {
|
|||
// const dur = Math.abs(minZIndex)
|
|||
// unFocus.forEach((block) => (block.zIndex += dur))
|
|||
// minZIndex = 0
|
|||
// }
|
|||
// focus.forEach((block) => (block.zIndex = minZIndex))
|
|||
// return cloneDeep(dataModel.value.blocks)
|
|||
// })()
|
|||
// )
|
|||
// }
|
|||
// return {
|
|||
// redo: () => {
|
|||
// updateBlocks(cloneDeep(data.after))
|
|||
// },
|
|||
// undo: () => {
|
|||
// updateBlocks(cloneDeep(data.before))
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
//
|
|||
// commander.registry({
|
|||
// name: 'updateBlock',
|
|||
// execute: (newBlock: VisualEditorBlockData, oldBlock: VisualEditorBlockData) => {
|
|||
// let blocks = cloneDeep(dataModel.value.blocks || [])
|
|||
// const data = {
|
|||
// before: blocks,
|
|||
// after: (() => {
|
|||
// blocks = [...blocks]
|
|||
// const index = dataModel.value.blocks!.indexOf(oldBlock)
|
|||
// if (index > -1) {
|
|||
// blocks.splice(index, 1, newBlock)
|
|||
// }
|
|||
// return cloneDeep(blocks)
|
|||
// })()
|
|||
// }
|
|||
// return {
|
|||
// redo: () => {
|
|||
// updateBlocks(cloneDeep(data.after))
|
|||
// },
|
|||
// undo: () => {
|
|||
// updateBlocks(cloneDeep(data.before))
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
//
|
|||
// commander.registry({
|
|||
// name: 'updateModelValue',
|
|||
// execute: (val: VisualEditorModelValue) => {
|
|||
// const data = {
|
|||
// before: cloneDeep(dataModel.value),
|
|||
// after: cloneDeep(val)
|
|||
// }
|
|||
// return {
|
|||
// redo: () => {
|
|||
// dataModel.value = data.after
|
|||
// },
|
|||
// undo: () => {
|
|||
// dataModel.value = data.before
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
//
|
|||
// commander.registry({
|
|||
// name: 'selectAll',
|
|||
// followQueue: false,
|
|||
// keyboard: 'ctrl+a',
|
|||
// execute: () => {
|
|||
// return {
|
|||
// redo: () => {
|
|||
// ;(dataModel.value.blocks || []).forEach((block) => (block.focus = true))
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
//
|
|||
// commander.init()
|
|||
//
|
|||
// return {
|
|||
// undo: () => commander.state.commands.undo(),
|
|||
// redo: () => commander.state.commands.redo(),
|
|||
// delete: () => commander.state.commands.delete(),
|
|||
// clear: () => commander.state.commands.clear(),
|
|||
// placeTop: () => commander.state.commands.placeTop(),
|
|||
// placeBottom: () => commander.state.commands.placeBottom(),
|
|||
// updateBlock: (newBlock: VisualEditorBlockData, oldBlock: VisualEditorBlockData) =>
|
|||
// commander.state.commands.updateBlock(newBlock, oldBlock),
|
|||
// updateModelValue: (val: VisualEditorModelValue) =>
|
|||
// commander.state.commands.updateModelValue(val)
|
|||
// }
|
|||
// }
|
|||
|
|||
File diff suppressed because it is too large
Loading…
Reference in new issue