perf: dragable components

This commit is contained in:
bqy_fe 2021-06-14 18:50:33 +08:00
parent 43b6361515
commit 240a0bd779
35 changed files with 513 additions and 109 deletions

View File

@ -52,8 +52,13 @@ let propObj = {
$$('#props + table tr').reduce((prev, curr) => {
const children = curr.children
const key = children[0].textContent.replace(/-([a-z])/g, (all, i) => i.toUpperCase())
const child3Text = children[3].textContent
const defaultValue = ['true', 'false'].includes(child3Text)
? child3Text
: `'${child3Text == '-' ? '' : child3Text}'`
const value = (propObj[children[2].textContent] ?? propObj['string'])({
label: `'${children[1].textContent}'`
label: `'${children[1].textContent}'`,
defaultValue
}).replaceAll('"', '')
prev[key] = value
return prev

View File

@ -57,8 +57,13 @@ let propObj = {
$$('#props + table tr').reduce((prev, curr) => {
const children = curr.children
const key = children[0].textContent.replace(/-([a-z])/g, (all, i) => i.toUpperCase())
const child3Text = children[3].textContent
const defaultValue = ['true', 'false'].includes(child3Text)
? child3Text
: `'${child3Text == '-' ? '' : child3Text}'`
const value = (propObj[children[2].textContent] ?? propObj['string'])({
label: `'${children[1].textContent}'`
label: `'${children[1].textContent}'`,
defaultValue
}).replaceAll('"', '')
prev[key] = value
return prev

View File

@ -21,8 +21,8 @@
},
"dependencies": {
"@vant/touch-emulator": "^1.3.0",
"@vueuse/core": "^5.0.2",
"@vueuse/integrations": "^5.0.2",
"@vueuse/core": "^5.0.3",
"@vueuse/integrations": "^5.0.3",
"animate.css": "^4.1.1",
"axios": "^0.21.1",
"dayjs": "^1.10.5",
@ -56,7 +56,7 @@
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.11.0",
"eslint-plugin-vue": "^7.11.1",
"gh-pages": "^3.2.0",
"husky": "^6.0.0",
"lint-staged": "^11.0.0",

View File

@ -1,14 +1,23 @@
import { defineComponent } from 'vue'
/*
* @Author:
* @Date: 2021-05-04 05:36:58
* @LastEditTime: 2021-06-14 10:03:06
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\preview\views\comp-render.tsx
*/
import { defineComponent, PropType } from 'vue'
import { VisualEditorBlockData, VisualEditorConfig } from '@/visual-editor/visual-editor.utils'
export default defineComponent({
name: 'CompRender',
props: {
element: {
type: Object,
type: Object as PropType<VisualEditorBlockData>,
default: () => ({})
},
config: {
type: Object,
type: Object as PropType<VisualEditorConfig>,
default: () => ({})
}
},

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-12 21:29:32
* @LastEditTime: 2021-06-12 22:03:43
* @LastEditTime: 2021-06-13 19:27:04
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\hooks\useAnimate.ts
@ -9,30 +9,47 @@
import { Animation } from '@/visual-editor/visual-editor.utils'
export const useAnimate = async (animateEl: HTMLElement, animations: Animation | Animation[]) => {
export const useAnimate = async (
animateEl: HTMLElement,
animations: Animation | Animation[],
prefixCls = 'animate__'
) => {
animations = Array.isArray(animations) ? animations : [animations]
const play = (animate: Animation) =>
new Promise((resolve) => {
if (animateEl) {
const animationName = `animate__${animate.value}`
animateEl.style.setProperty('--animate-duration', `${animate.duration}s`)
animateEl.style.setProperty('--animate-delay', `${animate.delay}s`)
animateEl.style.setProperty(
'animation-iteration-count',
`${animate.infinite ? 'infinite' : animate.count}`
)
const animationName = `${prefixCls}${animate.value}`
// 过滤可能残留的animate.css动画类名
animateEl.classList.value = animateEl.classList.value
.split(' ')
.filter((item) => !item.includes(prefixCls))
.join(' ')
// 设置动画属性
const setAnimate = () => {
animateEl.style.setProperty('--animate-duration', `${animate.duration}s`)
animateEl.style.setProperty('animation-delay', `${animate.delay}s`)
animateEl.style.setProperty(
'animation-iteration-count',
`${animate.infinite ? 'infinite' : animate.count}`
)
animateEl?.classList.add(`${prefixCls}animated`, animationName)
}
// 动画结束时,删除类名
const handleAnimationEnd = (event?: AnimationEvent) => {
event?.stopPropagation()
animateEl.classList.remove(`animate__animated`, animationName)
animateEl.classList.remove(`${prefixCls}animated`, animationName)
animateEl.removeEventListener('animationend', handleAnimationEnd)
resolve('animation end')
}
animateEl?.classList.add(`animate__animated`, animationName)
setAnimate()
animateEl?.addEventListener('animationend', handleAnimationEnd, { once: true })
// animateEl?.addEventListener('animationcancel', handleAnimationEnd, { once: true })
} else {
resolve('动画执行失败!执行动画元素不存在!')
}

View File

@ -6,16 +6,23 @@ import {
createEditorSelectProp
} from '@/visual-editor/visual-editor.props'
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
export default {
key: 'divider',
moduleName: 'baseWidgets',
label: '分割线',
preview: () => <Divider style="width:190px"></Divider>,
render: ({ props }) => {
const style = `color:${props['text-color']};borderColor:${props['divider-color']}`
render: ({ props, block }) => {
const { registerRef } = useGlobalProperties()
const style = {
color: props['text-color'],
borderColor: props['divider-color']
}
return (
<Divider {...props} style={style}>
<Divider ref={(el) => registerRef(el, block._vid)} {...props} style={style}>
{{
default: () => props.text
}}

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-01 09:45:21
* @LastEditTime: 2021-06-12 09:55:10
* @LastEditTime: 2021-06-14 10:31:27
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\image\index.tsx
@ -32,8 +32,8 @@ export default {
),
render: ({ props, block }) => {
const { registerRef } = useGlobalProperties()
const ImageComp = () => <Image ref={(el) => registerRef(el, block._vid)} {...props} />
return <ImageComp></ImageComp>
return <Image ref={(el) => registerRef(el, block._vid)} {...props} />
},
props: {
src: createEditorInputProp({

View File

@ -13,6 +13,11 @@ export default {
render: ({ model, size, block, props, custom }) => {
const { registerRef } = useGlobalProperties()
let rules = []
try {
rules = JSON.parse(props.rules)
} catch (e) {}
return (
<Field
ref={(el) => registerRef(el, block._vid)}
@ -20,6 +25,7 @@ export default {
{...props}
{...model.default}
v-model={props.modelValue}
rules={rules}
style={{
width: size.width ? `${size.width}px` : null
}}

View File

@ -0,0 +1,44 @@
/*
* @Author:
* @Date: 2021-06-14 12:24:12
* @LastEditTime: 2021-06-14 12:38:02
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\notice-bar\createFieldProps.ts
*/
import {
createEditorInputProp,
createEditorSelectProp,
createEditorSwitchProp
} from '@/visual-editor/visual-editor.props'
export const createFieldProps = () => ({
background: createEditorInputProp({ label: '滚动条背景' }),
color: createEditorInputProp({ label: '通知文本颜色' }),
delay: createEditorInputProp({ label: '动画延迟时间 (s)' }),
leftIcon: createEditorInputProp({ label: '左侧图标名称或图片链接', defaultValue: 'volume-o' }),
mode: createEditorSelectProp({
label: '通知栏模式',
options: [
{
label: '默认',
value: ''
},
{
label: '可关闭',
value: 'closeable'
},
{
label: '链接',
value: 'link'
}
]
}),
scrollable: createEditorSwitchProp({ label: '是否开启滚动播放,内容长度溢出时默认开启' }),
speed: createEditorInputProp({ label: '滚动速率 (px/s)' }),
text: createEditorInputProp({
label: '通知文本内容',
defaultValue: '在代码阅读过程中人们说脏话的频率是衡量代码质量的唯一标准。'
}),
wrapable: createEditorSwitchProp({ label: '是否开启文本换行,只在禁用滚动时生效' })
})

View File

@ -0,0 +1,37 @@
/*
* @Author:
* @Date: 2021-06-14 12:24:12
* @LastEditTime: 2021-06-14 12:56:23
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\notice-bar\index.tsx
*/
import { NoticeBar } from 'vant'
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
import { createFieldProps } from './createFieldProps'
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
export default {
key: 'NoticeBar',
moduleName: 'baseWidgets',
label: '通知栏',
preview: () => (
<NoticeBar
style={{ width: '180px' }}
leftIcon={'volume-o'}
text={'在代码阅读过程中人们说脏话的频率是衡量代码质量的唯一标准。'}
/>
),
render: ({ block, props }) => {
const { registerRef } = useGlobalProperties()
return <NoticeBar ref={(el) => registerRef(el, block._vid)} {...props} />
},
props: createFieldProps(),
resize: {
width: true
},
model: {
default: '绑定字段'
}
} as VisualEditorComponent

View File

@ -49,7 +49,7 @@ export default {
)
}}
</Field>
<Popup v-model={[state.showPicker, 'show', ['modifier']]} position={'bottom'}>
<Popup v-model={[state.showPicker, 'show']} position={'bottom'}>
<Picker
ref={(el) => registerRef(el, block._vid)}
{...props}

View File

@ -11,9 +11,11 @@ export default {
key: 'process',
moduleName: 'baseWidgets',
label: '进度条',
preview: () => <Progress style="width:190px" percentage={0} />,
preview: () => <Progress style="width:190px" percentage={50} />,
render: ({ props }) => {
return <Progress {...props} pivotText={props.pivotText || undefined} />
const RenderProgress = () => <Progress {...props} pivotText={props.pivotText || undefined} />
return <RenderProgress />
},
props: {
percentage: createEditorInputNumberProp({ label: '进度百分比', defaultValue: 50 }),

View File

@ -11,7 +11,7 @@ import {
export default {
key: 'rate',
moduleName: 'baseWidgets',
label: '表单项类型 - 单选框',
label: '表单项类型 - 评分',
preview: () => (
<Field
name="rate"

View File

@ -10,7 +10,7 @@ import {
export default {
key: 'stepper',
moduleName: 'baseWidgets',
label: '表单项类型 - 单选框',
label: '表单项类型 - 步进器',
preview: () => (
<Field
name="stepper"

View File

@ -0,0 +1,33 @@
/*
* @Author:
* @Date: 2021-06-14 12:24:12
* @LastEditTime: 2021-06-14 18:43:21
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\swipe\createFieldProps.ts
*/
import {
createEditorInputProp,
createEditorSwitchProp,
createEditorCrossSortableProp
} from '@/visual-editor/visual-editor.props'
export const createFieldProps = () => ({
images: createEditorCrossSortableProp({
label: '图片列表',
labelPosition: 'top',
defaultValue: ['https://img.yzcdn.cn/vant/apple-1.jpg', 'https://img.yzcdn.cn/vant/apple-2.jpg']
}),
width: createEditorInputProp({ label: '滑块宽度,单位为 px', defaultValue: 'auto' }),
height: createEditorInputProp({ label: '滑块高度,单位为 px', defaultValue: '200' }),
autoplay: createEditorInputProp({ label: '自动轮播间隔,单位为 ms', defaultValue: '' }),
duration: createEditorInputProp({ label: '动画时长,单位为 ms', defaultValue: '500' }),
indicatorColor: createEditorInputProp({ label: '指示器颜色', defaultValue: '#1989fa' }),
initialSwipe: createEditorInputProp({ label: '初始位置索引值', defaultValue: '0' }),
lazyRender: createEditorSwitchProp({ label: '是否延迟渲染未展示的轮播', defaultValue: false }),
loop: createEditorSwitchProp({ label: '是否开启循环播放', defaultValue: true }),
showIndicators: createEditorSwitchProp({ label: '是否显示指示器', defaultValue: true }),
stopPropagation: createEditorSwitchProp({ label: '是否阻止滑动事件冒泡', defaultValue: true }),
touchable: createEditorSwitchProp({ label: '是否可以通过手势滑动', defaultValue: true }),
vertical: createEditorSwitchProp({ label: '是否为纵向滚动', defaultValue: false })
})

View File

@ -0,0 +1,54 @@
/*
* @Author:
* @Date: 2021-06-14 12:24:12
* @LastEditTime: 2021-06-14 18:48:44
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\swipe\index.tsx
*/
import { Swipe, SwipeItem } from 'vant'
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
import { createFieldProps } from './createFieldProps'
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
const swipeItemStyle = `color: #fff;
font-size: 20px;
line-height: 150px;
text-align: center;
background-color: #39a9ed;`
export default {
key: 'swipe',
moduleName: 'baseWidgets',
label: '轮播图',
preview: () => (
<Swipe style={{ width: '180px', height: '80%' }} indicatorColor={'white'}>
<SwipeItem style={swipeItemStyle}>1</SwipeItem>
<SwipeItem style={swipeItemStyle}>2</SwipeItem>
<SwipeItem style={swipeItemStyle}>3</SwipeItem>
<SwipeItem style={swipeItemStyle}>4</SwipeItem>
</Swipe>
),
render: ({ block, props }) => {
const { registerRef } = useGlobalProperties()
return (
<Swipe ref={(el) => registerRef(el, block._vid)} {...props}>
{props.images?.map((item) => (
<>
<SwipeItem key={item}>
<img src={item} />
</SwipeItem>
</>
))}
</Swipe>
)
},
props: createFieldProps(),
resize: {
width: true
},
model: {
default: '绑定字段'
}
} as VisualEditorComponent

View File

@ -0,0 +1,54 @@
/*
* @Author:
* @Date: 2021-06-14 00:53:21
* @LastEditTime: 2021-06-14 00:55:55
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\text\fontArr.ts
*/
export const fontArr = [
{ label: '宋体', value: 'SimSun' },
{ label: '黑体', value: 'SimHei' },
{ label: '微软雅黑', value: 'Microsoft Yahei' },
{ label: '微软正黑体', value: 'Microsoft JhengHei' },
{ label: '楷体', value: 'KaiTi' },
{ label: '新宋体', value: 'NSimSun' },
{ label: '仿宋', value: 'FangSong' },
{ label: '苹方', value: 'PingFang SC' },
{ label: '华文黑体', value: 'STHeiti' },
{ label: '华文楷体', value: 'STKaiti' },
{ label: '华文宋体', value: 'STSong' },
{ label: '华文仿宋', value: 'STFangsong' },
{ label: '华文中宋', value: 'STZhongsong' },
{ label: '华文琥珀', value: 'STHupo' },
{ label: '华文新魏', value: 'STXinwei' },
{ label: '华文隶书', value: 'STLiti' },
{ label: '华文行楷', value: 'STXingkai' },
{ label: '冬青黑体简', value: 'Hiragino Sans GB' },
{ label: '兰亭黑-简', value: 'Lantinghei SC' },
{ label: '翩翩体-简', value: 'Hanzipen SC' },
{ label: '手札体-简', value: 'Hannotate SC' },
{ label: '宋体-简', value: 'Songti SC' },
{ label: '娃娃体-简', value: 'Wawati SC' },
{ label: '魏碑-简', value: 'Weibei SC' },
{ label: '行楷-简', value: 'Xingkai SC' },
{ label: '雅痞-简', value: 'Yapi SC' },
{ label: '圆体-简', value: 'Yuanti SC' },
{ label: '幼圆', value: 'YouYuan' },
{ label: '隶书', value: 'LiSu' },
{ label: '华文细黑', value: 'STXihei' },
{ label: '华文楷体', value: 'STKaiti' },
{ label: '华文宋体', value: 'STSong' },
{ label: '华文仿宋', value: 'STFangsong' },
{ label: '华文中宋', value: 'STZhongsong' },
{ label: '华文彩云', value: 'STCaiyun' },
{ label: '华文琥珀', value: 'STHupo' },
{ label: '华文新魏', value: 'STXinwei' },
{ label: '华文隶书', value: 'STLiti' },
{ label: '华文行楷', value: 'STXingkai' },
{ label: '方正舒体', value: 'FZShuTi' },
{ label: '方正姚体', value: 'FZYaoti' },
{ label: '思源黑体', value: 'Source Han Sans CN' },
{ label: '思源宋体', value: 'Source Han Serif SC' },
{ label: '文泉驿微米黑', value: 'WenQuanYi Micro Hei' }
]

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-01 09:45:21
* @LastEditTime: 2021-06-12 10:06:33
* @LastEditTime: 2021-06-14 10:17:54
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\text\index.tsx
@ -13,6 +13,7 @@ import {
createEditorSelectProp
} from '@/visual-editor/visual-editor.props'
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
import { fontArr } from './fontArr'
export default {
key: 'text',
@ -21,18 +22,19 @@ export default {
preview: () => <span></span>,
render: ({ props, block }) => {
const { registerRef } = useGlobalProperties()
const Text = () => (
return (
<div
ref={(el) => registerRef(el, block._vid)}
style={{ color: props.color, fontSize: props.size }}
style={{ color: props.color, fontSize: props.size, fontFamily: props.font }}
>
{props.text || '默认文本'}
</div>
)
return <Text></Text>
},
props: {
text: createEditorInputProp({ label: '显示文本' }),
font: createEditorSelectProp({ label: '字体设置', options: fontArr }),
color: createEditorColorProp('字体颜色'),
size: createEditorSelectProp({
label: '字体大小',

View File

@ -1,7 +1,7 @@
<!--
* @Author: 卜启缘
* @Date: 2021-06-01 13:30:22
* @LastEditTime: 2021-06-12 18:29:28
* @LastEditTime: 2021-06-14 00:21:31
* @LastEditors: 卜启缘
* @Description: 手机模拟器
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\common\simulator.vue

View File

@ -151,7 +151,7 @@ export const useTools = () => {
icon: 'el-icon-position',
onClick: () => {
localStorage.setItem(localKey, JSON.stringify(jsonData))
window.open(location.href.replace('/#/', '/preview/'))
window.open(location.href.replace('/#/', '/preview/#/'))
}
},
{

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-11 18:08:01
* @LastEditTime: 2021-06-12 22:07:05
* @LastEditTime: 2021-06-13 18:32:53
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\animate\Animate.tsx
@ -92,10 +92,10 @@ export const Animate = defineComponent({
<>
{currentBlock.value.animations?.map((item, index) => (
<ElAlert
onClose={() => delAnimate(index)}
key={item.value}
type={'info'}
style={{ marginTop: '12px' }}
key={item.value}
onClose={() => delAnimate(index)}
>
{{
title: () => (
@ -164,9 +164,10 @@ export const Animate = defineComponent({
<div v-show={!state.isAddAnimates}>
<ElButton
type={'primary'}
onClick={() => (state.isAddAnimates = true)}
disabled={!currentBlock.value.animations}
plain
icon={'el-icon-plus'}
onClick={() => (state.isAddAnimates = true)}
>
</ElButton>

View File

@ -1,10 +1,10 @@
/*
* @Author:
* @Date: 2021-06-10 16:23:06
* @LastEditTime: 2021-06-10 16:46:36
* @LastEditTime: 2021-06-14 17:22:11
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\AttrEditor.tsx
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\attr-editor\AttrEditor.tsx
*/
import { defineComponent } from 'vue'
import {
@ -19,7 +19,7 @@ import {
ElPopover
} from 'element-plus'
import { VisualEditorProps, VisualEditorPropsType } from '@/visual-editor/visual-editor.props'
import { TablePropEditor } from '../'
import { TablePropEditor, CrossSortableOptionsEditor } from '../'
import { useDotProp } from '@/visual-editor/hooks/useDotProp'
import { useVisualData } from '@/visual-editor/hooks/useVisualData'
@ -37,6 +37,9 @@ export const AttrEditor = defineComponent({
[VisualEditorPropsType.inputNumber]: () => <ElInputNumber v-model={propObj[prop]} />,
[VisualEditorPropsType.switch]: () => <ElSwitch v-model={propObj[prop]} />,
[VisualEditorPropsType.color]: () => <ElColorPicker v-model={propObj[prop]} />,
[VisualEditorPropsType.crossSortable]: () => (
<CrossSortableOptionsEditor v-model={propObj[prop]} />
),
[VisualEditorPropsType.select]: () => (
<ElSelect v-model={propObj[prop]} valueKey={'value'} multiple={propConfig.multiple}>
{propConfig.options?.map((opt) => (
@ -91,21 +94,31 @@ export const AttrEditor = defineComponent({
content.push(
...Object.entries(component.props || {}).map(([propName, propConfig]) => (
<>
<ElFormItem key={currentBlock.value._vid + propName}>
<ElFormItem
key={currentBlock.value._vid + propName}
style={
propConfig.labelPosition == 'top'
? {
display: 'flex',
'flex-direction': 'column',
'align-items': 'flex-start'
}
: {}
}
>
{{
label: () =>
propConfig.tips ? (
<>
label: () => (
<>
{propConfig.tips && (
<ElPopover width={200} trigger={'hover'} content={propConfig.tips}>
{{
reference: () => <i class={'el-icon-warning-outline'}></i>
}}
</ElPopover>
{propConfig.label}
</>
) : (
propConfig.label
),
)}
{propConfig.label}
</>
),
default: () => renderEditor(propName, propConfig)
}}
</ElFormItem>
@ -117,7 +130,7 @@ export const AttrEditor = defineComponent({
}
return (
<>
<ElForm size="mini" label-position="left">
<ElForm size="mini" labelPosition={'left'}>
{content}
</ElForm>
</>

View File

@ -0,0 +1,72 @@
/*
* @Author:
* @Date: 2021-06-14 15:00:45
* @LastEditTime: 2021-06-14 17:41:14
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\cross-sortable-options\cross-sortable-options.tsx
*/
import { defineComponent, reactive, computed, PropType } from 'vue'
import Draggable from 'vuedraggable'
import { ElInput } from 'element-plus'
import { useVModel } from '@vueuse/core'
export const CrossSortableOptionsEditor = defineComponent({
props: {
modelValue: {
type: Array as PropType<string[]>,
default: () => []
}
},
setup(props, { emit }) {
const state = reactive({
list: useVModel(props, 'modelValue', emit),
drag: false
})
const dragOptions = computed(() => {
return {
animation: 200,
group: 'description',
disabled: false,
ghostClass: 'ghost'
}
})
return () => (
<Draggable
tag="ul"
list={state.list}
class="list-group"
component-data={{
tag: 'ul',
type: 'transition-group',
name: !state.drag ? 'flip-list' : null
}}
handle=".handle"
{...dragOptions.value}
itemKey={''}
onStart={() => (state.drag = true)}
onEnd={() => (state.drag = false)}
>
{{
item: ({ element, index }) => (
<div class={'flex items-center justify-between'}>
<i class={'el-icon-s-grid handle'}></i>
<ElInput
v-model={state.list[index]}
class={'m-12px'}
style={{ width: '270px' }}
></ElInput>
<div class={'flex flex-col'}>
<i class={'el-icon-circle-plus-outline'} onClick={() => state.list.push('')}></i>
<i class={'el-icon-remove-outline'} onClick={() => state.list.splice(index, 1)}></i>
</div>
</div>
)
}}
</Draggable>
)
}
})

View File

@ -1,10 +1,13 @@
/**
* @name: index
* @author:
* @date: 2021/5/30 10:57
* @descriptionindex
* @update: 2021/5/30 10:57
/*
* @Author:
* @Date: 2021-06-12 22:18:48
* @LastEditTime: 2021-06-14 16:58:34
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\index.ts
*/
export { TablePropEditor } from './table-prop-editor/table-prop-editor'
export { AttrEditor } from './attr-editor/AttrEditor'
export { Animate } from './animate/Animate'
export { CrossSortableOptionsEditor } from './cross-sortable-options/cross-sortable-options'

View File

@ -0,0 +1,8 @@
/*
* @Author:
* @Date: 2021-06-13 22:07:29
* @LastEditTime: 2021-06-14 18:18:51
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\page-setting\pageSetting.tsx
*/

View File

@ -1,17 +0,0 @@
/**
* @name: index.common
* @author: 卜启缘
* @date: 2021/5/3 16:05
* @descriptionindex.common
* @update: 2021/5/3 16:05
*/
body {
.el-form-item__label {
font-size: 12px;
}
.el-form-item--mini .el-form-item__content {
display: flex;
justify-content: flex-end;
align-items: center;
}
}

View File

@ -67,6 +67,16 @@ $boxShadow: -2px 0 4px 0 rgb(0 0 0 / 10%);
padding-bottom: 50px;
overflow-y: auto;
}
.el-form-item__label {
font-size: 12px;
}
.el-form-item--mini .el-form-item__content {
display: flex;
justify-content: flex-end;
align-items: center;
}
}
}
}

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-01 13:22:14
* @LastEditTime: 2021-06-12 19:25:26
* @LastEditTime: 2021-06-13 21:26:49
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\index.tsx
@ -10,11 +10,7 @@
import { defineComponent, reactive } from 'vue'
import styles from './index.module.scss'
import './index.common.scss'
import { ElTabPane, ElTabs } from 'element-plus'
console.log(styles, 'styles')
import MonacoEditor from '../common/monaco-editor/MonacoEditor'
import { useVisualData } from '@/visual-editor/hooks/useVisualData'
import { AttrEditor, Animate } from './components'

View File

@ -1,5 +1,13 @@
/*
* @Author:
* @Date: 2021-05-04 05:36:58
* @LastEditTime: 2021-06-14 10:02:47
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\simulator-editor\comp-render.tsx
*/
import { defineComponent, PropType } from 'vue'
import { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils'
import { VisualEditorBlockData, VisualEditorConfig } from '@/visual-editor/visual-editor.utils'
export default defineComponent({
name: 'CompRender',
@ -9,7 +17,7 @@ export default defineComponent({
default: () => ({})
},
config: {
type: Object,
type: Object as PropType<VisualEditorConfig>,
default: () => ({})
}
},

View File

@ -94,9 +94,9 @@ export default defineComponent({
height: 100%;
min-height: 40px;
&.isDrag div[data-draggable='true'] {
padding: 8px 0;
}
// &.isDrag div[data-draggable='true'] {
// padding: 2px 0;
// }
&.isDrag:not(.no-child) :deep(.list-group-item.has-slot) {
@include showContainerBorder;

View File

@ -1,5 +1,9 @@
<template>
<DraggableTransitionGroup v-model:drag="drag" v-model="currentPage.blocks">
<DraggableTransitionGroup
v-model:drag="drag"
v-model="currentPage.blocks"
style="min-height: 500px"
>
<template #item="{ element: outElement }">
<div
class="list-group-item"
@ -14,6 +18,7 @@
@mousedown="selectComp(outElement)"
>
<comp-render
:key="outElement._vid"
:config="visualConfig"
:element="outElement"
:style="{
@ -93,6 +98,7 @@ export default defineComponent({
}
}
//
const handleSlotsFocus = (block, _vid) => {
const slots = block.props?.slots || {}
if (Object.keys(slots).length > 0) {
@ -112,6 +118,7 @@ export default defineComponent({
}
}
//
const selectComp = (element) => {
setCurrentBlock(element)
currentPage.value.blocks.forEach((block) => {
@ -165,7 +172,10 @@ export default defineComponent({
const index = parentBlocks.findIndex((item) => item._vid == block._vid)
if (index != -1) {
delete globalProperties.$$refs[parentBlocks[index]._vid]
parentBlocks.splice(index, 1)
const delTarget = parentBlocks.splice(index, 1)[0]
if (delTarget.focus) {
setCurrentBlock({} as VisualEditorBlockData)
}
}
}
}}

View File

@ -5,7 +5,7 @@
v-model:drag="isDrag"
class="inner-draggable"
:class="{ slot: !slotChildren?.length }"
:data-slot="`插槽(${slotKey}\n 拖拽组件到此处${drag}`"
:data-slot="`插槽(${slotKey}\n 拖拽组件到此处`"
>
<template #item="{ element: innerElement }">
<div

View File

@ -122,6 +122,8 @@ export const initVisualData = (): VisualData => {
state.currentPage = jsonData.pages['/']
router.replace('/')
}
const currentFocusBlock = state.currentPage.blocks.find((item) => item.focus)
setCurrentBlock(currentFocusBlock ?? ({} as VisualEditorBlockData))
}
// 设置当前被操作的组件

View File

@ -4,13 +4,15 @@ export enum VisualEditorPropsType {
color = 'color',
select = 'select',
table = 'table',
switch = 'switch'
switch = 'switch',
crossSortable = 'crossSortable'
}
export type VisualEditorProps = {
type: VisualEditorPropsType
label: string
tips?: string
tips?: string // 表单项提示
labelPosition?: string // 表单域标签的位置
multiple?: boolean
defaultValue?: any
} & {
@ -150,3 +152,24 @@ export function createEditorTableProp({
defaultValue
}
}
/*---------------------------------------CrossSortableOptions-------------------------------------------*/
interface EditorCrossSortableProp {
label: string
labelPosition: string
defaultValue?: string[]
}
export function createEditorCrossSortableProp({
label,
labelPosition,
defaultValue
}: EditorCrossSortableProp): VisualEditorProps {
return {
type: VisualEditorPropsType.crossSortable,
label,
labelPosition,
defaultValue
}
}

View File

@ -964,20 +964,20 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.1.1.tgz#2287cfc3dc20e5b20aeb65c2c3a56533bdca801c"
integrity sha512-g+4pzAw7PYSjARtLBoDq6DmcblX8i9KJHSCnyM5VDDFFifUaUT9iHbFpOF/KOizQ9f7QAqU2JH3Y6aXjzUMhVA==
"@vueuse/core@^5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-5.0.2.tgz#302389f620c0d4b51fdf157012d9b5b522b605e7"
integrity sha512-Sp9+7AL4Cg3Tx6I55WoH7zICGRlp6ZUF9NW3EU8SZTkryHm0afAjFfASMwlfV030JFeh45BdqafDOrenVmM9Cw==
"@vueuse/core@^5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-5.0.3.tgz#8f3170e2a51ae62fb1725c84d4cc02a7552aad0b"
integrity sha512-TMCL11EVMaj2Y5qdYosvuwA+i1aKrerFXs7fhNZiQiLCWxF8XsrNdxzoiaI2n12UcmSOXvd1xdyWs7Nss+p/Hg==
dependencies:
"@vueuse/shared" "5.0.2"
"@vueuse/shared" "5.0.3"
vue-demi "*"
"@vueuse/integrations@^5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-5.0.2.tgz#95b9f5bea831430f747d7311d9497ec9f52db3bd"
integrity sha512-jh9Ywz8zu2YmsSd1xeyXkuoA8fhNRkoUkzX4y/hSm2IQC2ydkD8Rk/aOvPAew5RiwndtWhXQYu5XrSMQRsYDYQ==
"@vueuse/integrations@^5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-5.0.3.tgz#64820965d068b356b4df50ed47a87adf0141f68e"
integrity sha512-c7dy7u4XTJeSfq/NnFotoUPrbkPJv/DbWwEYVW9YXXMdDvz1IxDdXUFrOu8ElM0qzGn4CuYg6Yr37uYR13MgXg==
dependencies:
"@vueuse/shared" "5.0.2"
"@vueuse/shared" "5.0.3"
vue-demi "*"
optionalDependencies:
axios "^0.21.1"
@ -987,10 +987,10 @@
qrcode "^1.4.4"
universal-cookie "^4.0.4"
"@vueuse/shared@5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-5.0.2.tgz#274c2bf163d25eb7fd2fc51f23088a2b7f060594"
integrity sha512-S1hRRmEdipjTD4DbXgPdw4ZZYebU/nDi75vNP3Ibpa1irW3NUNUKOT/TWnwRHLQvXquUtdvalhI8D9Db+czZJg==
"@vueuse/shared@5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-5.0.3.tgz#31613951d5036459650ad8d47a9185e8950ea3c9"
integrity sha512-aY93WPygr8H/4RB8YuOmAD83Y+faq7zwW10Kd9i0kD9zf5ysVP+32j09rF/mZVtGCa0CSM8ambPZMsEhCkRbwQ==
dependencies:
vue-demi "*"
@ -2324,10 +2324,10 @@ eslint-plugin-prettier@^3.4.0:
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-plugin-vue@^7.11.0:
version "7.11.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.11.0.tgz#c19b098899b7e3cd692beffbbe73611064ef1ea6"
integrity sha512-Qwo8wilqnOXnG9B5auEiTstyaHefyhHd5lEhhxemwXoWsAxIW2yppzuVudowC5n+qn1nMLNV9TANkTthBK7Waw==
eslint-plugin-vue@^7.11.1:
version "7.11.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.11.1.tgz#77eb4b44032d5cca79f9af21d06991d8694a314a"
integrity sha512-lbw3vkEAGqYjqd1HpPFWHXtYaS8mILTJ5KOpJfRxO3Fo7o0wCf1zD7vSOasbm6nTA9xIgvZQ4VcyGIzQXxznHw==
dependencies:
eslint-utils "^2.1.0"
natural-compare "^1.4.0"