feat(component): pageSetting

This commit is contained in:
bqy_fe 2021-06-24 01:38:30 +08:00
parent 5f99924474
commit f387fa8993
31 changed files with 774 additions and 547 deletions

View File

@ -1,5 +1,10 @@
name: deploy
env:
# 7 GiB by default on GitHub, setting to 6 GiB
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
NODE_OPTIONS: --max-old-space-size=6144
on:
push:
branches: [main]
@ -16,10 +21,10 @@ jobs:
node-version: '14.x'
- name: Install
run: npm install
run: yarn install --frozen-lockfile
- name: Build
run: npm run build
run: yarn build
- name: Deploy
uses: peaceiris/actions-gh-pages@v3

29
components.d.ts vendored Normal file
View File

@ -0,0 +1,29 @@
// generated by vite-plugin-components
// read more https://github.com/vuejs/vue-next/pull/3399
declare module 'vue' {
export interface GlobalComponents {
ElHeader: typeof import('element-plus/es/el-header')['default']
ElAside: typeof import('element-plus/es/el-aside')['default']
ElMain: typeof import('element-plus/es/el-main')['default']
ElContainer: typeof import('element-plus/es/el-container')['default']
ElCol: typeof import('element-plus/es/el-col')['default']
ElButton: typeof import('element-plus/es/el-button')['default']
ElTooltip: typeof import('element-plus/es/el-tooltip')['default']
ElRow: typeof import('element-plus/es/el-row')['default']
ElPopover: typeof import('element-plus/es/el-popover')['default']
ElTabPane: typeof import('element-plus/es/el-tab-pane')['default']
ElTabs: typeof import('element-plus/es/el-tabs')['default']
ElDialog: typeof import('element-plus/es/el-dialog')['default']
ElTag: typeof import('element-plus/es/el-tag')['default']
ElDropdownItem: typeof import('element-plus/es/el-dropdown-item')['default']
ElDropdownMenu: typeof import('element-plus/es/el-dropdown-menu')['default']
ElDropdown: typeof import('element-plus/es/el-dropdown')['default']
ElTree: typeof import('element-plus/es/el-tree')['default']
ElInput: typeof import('element-plus/es/el-input')['default']
ElFormItem: typeof import('element-plus/es/el-form-item')['default']
ElForm: typeof import('element-plus/es/el-form')['default']
}
}
export {}

View File

@ -4,8 +4,8 @@
"private": false,
"description": "A Vite2.x + Vue3 + TypeScript LowCode",
"scripts": {
"dev": "vite",
"build": "vite build",
"dev": "cross-env --max_old_space_size=4096 vite",
"build": "cross-env vite build",
"build-tsc": "vue-tsc --noEmit && vite build",
"serve": "vite preview",
"deploy": "gh-pages -d dist",
@ -27,53 +27,54 @@
"axios": "^0.21.1",
"dayjs": "^1.10.5",
"dexie": "^3.0.3",
"element-plus": "^1.0.2-beta.48",
"element-plus": "1.0.2-beta.51",
"lodash": "^4.17.21",
"monaco-editor": "^0.25.0",
"monaco-editor": "^0.25.2",
"normalize.css": "^8.0.1",
"nprogress": "^1.0.0-1",
"qrcode": "^1.4.4",
"vant": "^3.0.18",
"vue": "3.1.1",
"vue-router": "^4.0.8",
"vant": "^3.1.0",
"vue": "3.1.2",
"vue-router": "^4.0.10",
"vuedraggable": "^4.0.3",
"vuex": "^4.0.1"
"vuex": "^4.0.2"
},
"devDependencies": {
"@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4",
"@types/node": "^15.12.2",
"@typescript-eslint/eslint-plugin": "^4.27.0",
"@typescript-eslint/parser": "^4.27.0",
"@vitejs/plugin-legacy": "^1.4.1",
"@types/node": "^15.12.4",
"@typescript-eslint/eslint-plugin": "^4.28.0",
"@typescript-eslint/parser": "^4.28.0",
"@vitejs/plugin-legacy": "^1.4.2",
"@vitejs/plugin-vue": "^1.2.3",
"@vitejs/plugin-vue-jsx": "^1.1.5",
"@vue/compiler-sfc": "3.1.1",
"@vue/compiler-sfc": "3.1.2",
"commitizen": "^4.2.4",
"cross-env": "^7.0.3",
"cz-conventional-changelog": "^3.3.0",
"cz-customizable": "^6.3.0",
"eslint": "^7.28.0",
"eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.11.1",
"gh-pages": "^3.2.1",
"gh-pages": "^3.2.3",
"husky": "^6.0.0",
"lint-staged": "^11.0.0",
"prettier": "^2.3.1",
"pretty-quick": "^3.1.0",
"sass": "1.35.0",
"pretty-quick": "^3.1.1",
"sass": "1.35.1",
"stylelint": "^13.13.1",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^22.0.0",
"stylelint-order": "^4.1.0",
"typescript": "^4.3.2",
"vite": "2.3.7",
"vite-plugin-components": "^0.11.1",
"vite-plugin-style-import": "^0.10.1",
"vite-plugin-windicss": "^1.0.3",
"typescript": "^4.3.4",
"vite": "2.3.8",
"vite-plugin-components": "^0.11.2",
"vite-plugin-style-import": "^1.0.0",
"vite-plugin-windicss": "^1.1.1",
"vue-eslint-parser": "^7.6.0",
"vue-tsc": "^0.1.7",
"vue-tsc": "^0.2.0",
"windicss": "^3.1.3"
},
"repository": {

View File

@ -1,10 +1,10 @@
/*
* @Author:
* @Date: 2021-05-04 05:36:58
* @LastEditTime: 2021-06-14 10:03:06
* @LastEditTime: 2021-06-24 00:36:24
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\preview\views\comp-render.tsx
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\simulator-editor\comp-render.tsx
*/
import { defineComponent, PropType } from 'vue'
import { VisualEditorBlockData, VisualEditorConfig } from '@/visual-editor/visual-editor.utils'
@ -22,15 +22,13 @@ export default defineComponent({
}
},
setup(props) {
return () => {
const component = props.config.componentMap[props.element.componentKey]
return component.render({
return () =>
props.config.componentMap[props.element.componentKey].render({
size: {},
props: props.element.props || {},
model: {},
block: props.element,
custom: {}
})
}
}
})

View File

@ -1,11 +1,19 @@
<!--
* @Author: 卜启缘
* @Date: 2021-06-01 09:45:21
* @LastEditTime: 2021-06-24 00:19:14
* @LastEditors: 卜启缘
* @Description:
* @FilePath: \vite-vue3-lowcode\preview\views\preview.vue
-->
<template>
<template v-for="outItem in currentPage" :key="outItem._vid">
<template v-for="outItem in blocks" :key="outItem._vid">
<slot-item :element="outItem" :config="visualConfig" />
</template>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue'
import { defineComponent, reactive, toRefs, onMounted } from 'vue'
import { Toast } from 'vant'
import { visualConfig } from '@/visual.config'
import { CacheEnum } from '@/enums'
@ -36,14 +44,24 @@ export default defineComponent({
const route = router.currentRoute
const currentPage = jsonData.pages[route.value.path]
console.log('currentPage:', currentPage)
const state = reactive({
currentPage: jsonData.pages[route.value.path]?.blocks
blocks: currentPage?.blocks
})
//
if (!state.currentPage) {
if (!state.blocks) {
router.replace('/')
}
onMounted(() => {
const { bgImage, bgColor } = currentPage.config
document.body.style.setProperty('--image', `url(${bgImage})`)
document.body.style.setProperty('--bg-color', bgColor)
})
return {
...toRefs(state),
visualConfig
@ -53,19 +71,8 @@ export default defineComponent({
</script>
<style lang="scss">
.h5-preview {
overflow: hidden;
.el-dialog__header {
display: none;
}
.simulator {
padding-right: 0;
&::-webkit-scrollbar {
width: 0;
}
}
body {
background-color: var(--bg-color);
background-image: var(--image);
}
</style>

View File

@ -1,3 +1,11 @@
/*
* @Author:
* @Date: 2021-06-01 09:45:21
* @LastEditTime: 2021-06-22 23:13:09
* @LastEditors:
* @Description: 线
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\divider\index.tsx
*/
import { Divider } from 'vant'
import {
createEditorColorProp,
@ -41,7 +49,7 @@ export default {
defaultValue: 'center'
}),
dashed: createEditorSwitchProp({ label: '是否为虚线' }),
'text-color': createEditorColorProp('文本颜色'),
'divider-color': createEditorColorProp('分割线颜色')
'text-color': createEditorColorProp({ label: '文本颜色' }),
'divider-color': createEditorColorProp({ label: '分割线颜色' })
}
} as VisualEditorComponent

View File

@ -1,3 +1,11 @@
/*
* @Author:
* @Date: 2021-05-04 05:36:58
* @LastEditTime: 2021-06-22 22:51:42
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\nav-bar\index.tsx
*/
import { NavBar } from 'vant'
import 'vant/lib/nav-bar/index.css'
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'

View File

@ -1,3 +1,11 @@
/*
* @Author:
* @Date: 2021-06-12 22:18:48
* @LastEditTime: 2021-06-22 23:14:22
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\process\index.tsx
*/
import { Progress } from 'vant'
import {
createEditorColorProp,
@ -21,11 +29,11 @@ export default {
percentage: createEditorInputNumberProp({ label: '进度百分比', defaultValue: 50 }),
strokeWidth: createEditorInputNumberProp({ label: '线条粗细', defaultValue: 5 }),
inactive: createEditorSwitchProp({ label: '是否置灰', defaultValue: false }),
color: createEditorColorProp('进度条颜色', '#1989fa'),
trackColor: createEditorColorProp('轨道颜色', '#e5e5e5'),
color: createEditorColorProp({ label: '进度条颜色', defaultValue: '#1989fa' }),
trackColor: createEditorColorProp({ label: '轨道颜色', defaultValue: '#e5e5e5' }),
pivotText: createEditorInputProp({ label: '进度文字内容' }),
pivotColor: createEditorColorProp('进度文字背景色', '#1989fa'),
textColor: createEditorColorProp('进度文字颜色', '#ffffff'),
pivotColor: createEditorColorProp({ label: '进度文字背景色', defaultValue: '#1989fa' }),
textColor: createEditorColorProp({ label: '进度文字颜色', defaultValue: '#ffffff' }),
showPivot: createEditorSwitchProp({ label: '是否显示进度文字', defaultValue: true })
}
} as VisualEditorComponent

View File

@ -1,10 +1,20 @@
/*
* @Author:
* @Date: 2021-06-01 09:45:21
* @LastEditTime: 2021-06-22 23:08:50
* @LastEditors:
* @Description: ' -
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\stepper\index.tsx
*/
import { Field, Stepper } from 'vant'
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
import { createFieldProps } from './createFieldProps'
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
import {
createEditorInputNumberProp,
createEditorInputProp
createEditorInputProp,
createEditorSwitchProp,
createEditorSelectProp
} from '@/visual-editor/visual-editor.props'
export default {
@ -45,9 +55,42 @@ export default {
modelValue: createEditorInputNumberProp({ label: '默认值', defaultValue: 0 }),
name: createEditorInputProp({ label: '字段名', defaultValue: 'stepper' }),
label: createEditorInputProp({ label: '输入框左侧文本', defaultValue: '步进器' }),
min: createEditorInputNumberProp({ label: '最小值' }),
min: createEditorInputNumberProp({ label: '最小值', defaultValue: 0 }),
max: createEditorInputNumberProp({ label: '最大值' }),
...createFieldProps()
...createFieldProps(),
allowEmpty: createEditorSwitchProp({ label: '是否允许输入的值为空', defaultValue: false }),
buttonSize: createEditorInputProp({
label: '按钮大小以及输入框高度,默认单位为 px',
defaultValue: '28px'
}),
decimalLength: createEditorInputProp({ label: '固定显示的小数位数', defaultValue: '' }),
defaultValue: createEditorInputProp({
label: '初始值,当 v-model 为空时生效',
defaultValue: '1'
}),
disableInput: createEditorSwitchProp({ label: '是否禁用输入框', defaultValue: false }),
disableMinus: createEditorSwitchProp({ label: '是否禁用减少按钮', defaultValue: false }),
disablePlus: createEditorSwitchProp({ label: '是否禁用增加按钮', defaultValue: false }),
disabled: createEditorSwitchProp({ label: '是否禁用步进器', defaultValue: false }),
inputWidth: createEditorInputProp({ label: '输入框宽度,默认单位为 px', defaultValue: '32px' }),
integer: createEditorSwitchProp({ label: '是否只允许输入整数', defaultValue: false }),
longPress: createEditorSwitchProp({ label: '是否开启长按手势', defaultValue: true }),
placeholder: createEditorInputProp({ label: '输入框占位提示文字', defaultValue: '' }),
showInput: createEditorSwitchProp({ label: '是否显示输入框', defaultValue: true }),
showMinus: createEditorSwitchProp({ label: '是否显示减少按钮', defaultValue: true }),
showPlus: createEditorSwitchProp({ label: '是否显示增加按钮', defaultValue: true }),
step: createEditorInputProp({ label: '步长,每次点击时改变的值', defaultValue: '1' }),
theme: createEditorSelectProp({
label: '样式风格',
options: [
{
label: '默认',
value: ''
},
{ label: '圆角风格', value: 'round' }
],
defaultValue: ''
})
},
resize: {
width: true

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-14 12:24:12
* @LastEditTime: 2021-06-14 18:43:21
* @LastEditTime: 2021-06-21 23:04:42
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\swipe\createFieldProps.ts
@ -18,9 +18,9 @@ export const createFieldProps = () => ({
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' }),
// width: createEditorInputProp({ label: '滑块宽度,单位为 px', defaultValue: 'auto' }),
height: createEditorInputProp({ label: '滑块高度,单位为 px', defaultValue: '200' }),
autoplay: createEditorInputProp({ label: '自动轮播间隔,单位为 ms', defaultValue: '' }),
autoplay: createEditorInputProp({ label: '自动轮播间隔,单位为 ms', defaultValue: '3000' }),
duration: createEditorInputProp({ label: '动画时长,单位为 ms', defaultValue: '500' }),
indicatorColor: createEditorInputProp({ label: '指示器颜色', defaultValue: '#1989fa' }),
initialSwipe: createEditorInputProp({ label: '初始位置索引值', defaultValue: '0' }),

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-14 12:24:12
* @LastEditTime: 2021-06-14 21:19:28
* @LastEditTime: 2021-06-21 23:06:36
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\swipe\index.tsx
@ -41,7 +41,7 @@ export default {
{props.images?.map((item) => (
<>
<SwipeItem key={item}>
<img src={item} />
<img style={{ width: '100%' }} src={item} />
</SwipeItem>
</>
))}

View File

@ -1,8 +1,20 @@
/*
* @Author:
* @Date: 2021-06-01 09:45:21
* @LastEditTime: 2021-06-23 10:16:32
* @LastEditors:
* @Description: -
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\switch\index.tsx
*/
import { Field, Switch } from 'vant'
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
import { createFieldProps } from './createFieldProps'
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
import { createEditorInputProp, createEditorSwitchProp } from '@/visual-editor/visual-editor.props'
import {
createEditorInputProp,
createEditorSwitchProp,
createEditorColorProp
} from '@/visual-editor/visual-editor.props'
export default {
key: 'switch',
@ -27,7 +39,6 @@ export default {
ref={(el) => registerRef(el, block._vid)}
{...props}
v-model={props.modelValue}
size={20}
/>
)
}}
@ -38,13 +49,13 @@ export default {
modelValue: createEditorInputProp({ label: '默认值', defaultValue: 'false' }),
name: createEditorInputProp({ label: '字段名', defaultValue: 'switch' }),
label: createEditorInputProp({ label: '输入框左侧文本', defaultValue: '开关' }),
'active-color': createEditorInputProp({ label: '打开时的背景色' }),
'active-value': createEditorInputProp({ label: '打开时对应的值' }),
activeColor: createEditorColorProp({ label: '打开时的背景色' }),
activeValue: createEditorInputProp({ label: '打开时对应的值', defaultValue: 'true' }),
inactiveColor: createEditorColorProp({ label: '关闭时的背景色' }),
inactiveValue: createEditorInputProp({ label: '关闭时对应的值', defaultValue: 'false' }),
disabled: createEditorSwitchProp({ label: '是否为禁用状态' }),
'inactive-color': createEditorInputProp({ label: '关闭时的背景色' }),
'inactive-value': createEditorInputProp({ label: '关闭时对应的值' }),
loading: createEditorSwitchProp({ label: '是否为加载状态' }),
size: createEditorInputProp({ label: '开关尺寸' }),
size: createEditorInputProp({ label: '开关尺寸', defaultValue: '20px' }),
...createFieldProps()
},
resize: {

View File

@ -1,16 +1,17 @@
/*
* @Author:
* @Date: 2021-06-01 09:45:21
* @LastEditTime: 2021-06-14 10:17:54
* @LastEditTime: 2021-06-22 23:14:46
* @LastEditors:
* @Description:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\text\index.tsx
*/
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
import {
createEditorColorProp,
createEditorInputProp,
createEditorSelectProp
createEditorSelectProp,
createEditorInputNumberProp
} from '@/visual-editor/visual-editor.props'
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
import { fontArr } from './fontArr'
@ -26,7 +27,11 @@ export default {
return (
<div
ref={(el) => registerRef(el, block._vid)}
style={{ color: props.color, fontSize: props.size, fontFamily: props.font }}
style={{
color: props.color,
fontSize: `${parseFloat(props.size)}px`,
fontFamily: props.font
}}
>
{props.text || '默认文本'}
</div>
@ -35,14 +40,10 @@ export default {
props: {
text: createEditorInputProp({ label: '显示文本' }),
font: createEditorSelectProp({ label: '字体设置', options: fontArr }),
color: createEditorColorProp('字体颜色'),
size: createEditorSelectProp({
color: createEditorColorProp({ label: '字体颜色' }),
size: createEditorInputNumberProp({
label: '字体大小',
options: [
{ label: '14px', value: '14px' },
{ label: '18px', value: '18px' },
{ label: '24px', value: '24px' }
]
defaultValue: 16
})
}
} as VisualEditorComponent

View File

@ -1,62 +0,0 @@
<!--
* @Author: 卜启缘
* @Date: 2021-06-01 13:30:22
* @LastEditTime: 2021-06-14 00:21:31
* @LastEditors: 卜启缘
* @Description: 手机模拟器
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\common\simulator.vue
-->
<template>
<div class="simulator-container">
<div class="simulator-editor">
<div class="simulator-editor-content">
<slot></slot>
</div>
</div>
</div>
</template>
<script lang="tsx">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Simulator'
})
</script>
<style lang="scss" scoped>
.simulator-container {
display: flex;
width: 100%;
height: 100%;
padding-right: 240px;
align-items: center;
justify-content: center;
@media (max-width: 1314px) {
padding-right: 0;
}
}
.simulator-editor {
width: 560px;
height: 640px;
min-width: 560px;
padding: 10px 100px;
overflow: hidden auto;
background: #fafafa;
border-radius: 5px;
transform: translate(0);
box-sizing: border-box;
background-clip: content-box;
contain: layout;
&::-webkit-scrollbar {
width: 0;
}
&-content {
min-height: 100%;
box-shadow: 0 8px 12px #ebedf0;
}
}
</style>

View File

@ -1,23 +1,25 @@
/*
* @Author:
* @Date: 2021-06-01 13:22:14
* @LastEditTime: 2021-06-12 14:39:38
* @LastEditTime: 2021-06-23 11:40:10
* @LastEditors:
* @Description:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\left-aside\components\base-widgets\index.tsx
*/
import { defineComponent } from 'vue'
import { defineComponent, ref } from 'vue'
import { cloneDeep } from 'lodash'
import { visualConfig } from '@/visual.config'
import Draggable from 'vuedraggable'
import styles from './index.module.scss'
import { createNewBlock } from '@/visual-editor/visual-editor.utils'
import DraggableTransitionGroup from '@/visual-editor/components/simulator-editor/draggable-transition-group.vue'
export default defineComponent({
name: 'BaseWidgets',
setup() {
const baseWidgets = ref(visualConfig.componentModules.baseWidgets)
const log = (evt) => {
window.console.log(evt)
window.console.log('onChange:', evt)
}
// 克隆组件
const cloneDog = (comp) => {
@ -29,15 +31,13 @@ export default defineComponent({
return () => (
<>
<Draggable
<DraggableTransitionGroup
class={styles.listGroup}
sort={false}
forceFallback={false}
list={visualConfig.componentModules.baseWidgets}
v-model={baseWidgets.value}
group={{ name: 'components', pull: 'clone', put: false }}
clone={cloneDog}
item-key="_vid"
onChange={log}
itemKey={'key'}
>
{{
item: ({ element }) => (
@ -46,7 +46,7 @@ export default defineComponent({
</div>
)
}}
</Draggable>
</DraggableTransitionGroup>
</>
)
}

View File

@ -66,7 +66,7 @@
<script lang="ts">
import { defineComponent, reactive, computed, toRefs } from 'vue'
import { useVisualData } from '@/visual-editor/hooks/useVisualData'
import { useVisualData, createNewPage } from '@/visual-editor/hooks/useVisualData'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
@ -146,7 +146,7 @@ export default defineComponent({
await router.replace(path)
state.currentNodeKey = path
} else {
incrementPage(path, { title, blocks: [] })
incrementPage(path, createNewPage({ title }))
}
state.dialogFormVisible = false
}

View File

@ -51,10 +51,12 @@ export default defineComponent({
::v-deep(.el-tabs__item) {
height: 80px;
padding: 20px 16px;
[class^='el-icon-'] {
font-size: 20px;
}
}
::v-deep(.el-tabs__content) {
height: 100%;
overflow-y: auto;

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-10 16:23:06
* @LastEditTime: 2021-06-14 17:22:11
* @LastEditTime: 2021-06-21 10:00:54
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\attr-editor\AttrEditor.tsx
@ -30,6 +30,8 @@ export const AttrEditor = defineComponent({
const renderEditor = (propName: string, propConfig: VisualEditorProps) => {
const { propObj, prop } = useDotProp(currentBlock.value.props, propName)
propObj[prop] ??= propConfig.defaultValue
return {
[VisualEditorPropsType.input]: () => (
<ElInput v-model={propObj[prop]} placeholder={propConfig.tips || propConfig.label} />
@ -43,7 +45,7 @@ export const AttrEditor = defineComponent({
[VisualEditorPropsType.select]: () => (
<ElSelect v-model={propObj[prop]} valueKey={'value'} multiple={propConfig.multiple}>
{propConfig.options?.map((opt) => (
<ElOption label={opt.label} value={opt.value} />
<ElOption label={opt.label} style={{ fontFamily: opt.value }} value={opt.value} />
))}
</ElSelect>
),

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-12 22:18:48
* @LastEditTime: 2021-06-14 18:53:02
* @LastEditTime: 2021-06-23 22:17:38
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\index.ts
@ -11,3 +11,4 @@ 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-editor/cross-sortable-options-editor'
export { PageSetting } from './page-setting/pageSetting'

View File

@ -1,8 +1,49 @@
/*
* @Author:
* @Date: 2021-06-13 22:07:29
* @LastEditTime: 2021-06-14 18:18:51
* @LastEditTime: 2021-06-24 00:23:39
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\page-setting\pageSetting.tsx
*/
import { defineComponent } from 'vue'
import { ElForm, ElFormItem, ElInput, ElUpload, ElColorPicker } from 'element-plus'
import styles from './styles.module.scss'
import { useVisualData } from '@/visual-editor/hooks/useVisualData'
export const PageSetting = defineComponent({
setup() {
const { currentPage } = useVisualData()
const pageConfig = currentPage.value.config
const beforeUpload = (file: File) => {
console.log(file, '要上传的文件')
const fileReader = new FileReader()
fileReader.onload = (event) => {
pageConfig.bgImage = event.target?.result as string
}
fileReader.readAsDataURL(file)
}
return () => (
<>
<ElForm>
<ElFormItem label="背景颜色">
<ElColorPicker v-model={pageConfig.bgColor} />
</ElFormItem>
<ElFormItem label="背景图片">
<ElInput v-model={pageConfig.bgImage} placeholder={'图片地址'} />
</ElFormItem>
<ElUpload action={''} beforeUpload={beforeUpload} class={styles.upload}>
{pageConfig.bgImage ? (
<img src={pageConfig.bgImage} />
) : (
<i class="el-icon-plus uploader-icon"></i>
)}
</ElUpload>
</ElForm>
</>
)
}
})

View File

@ -0,0 +1,20 @@
.upload {
:global {
.el-upload {
position: relative;
overflow: hidden;
cursor: pointer;
border: 1px dashed #d9d9d9;
border-radius: 6px;
}
.uploader-icon {
width: 178px;
height: 178px;
font-size: 28px;
line-height: 178px;
color: #8c939d;
text-align: center;
}
}
}

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-06-01 13:22:14
* @LastEditTime: 2021-06-13 21:26:49
* @LastEditTime: 2021-06-23 22:18:34
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\index.tsx
@ -13,7 +13,7 @@ import styles from './index.module.scss'
import { ElTabPane, ElTabs } from 'element-plus'
import MonacoEditor from '../common/monaco-editor/MonacoEditor'
import { useVisualData } from '@/visual-editor/hooks/useVisualData'
import { AttrEditor, Animate } from './components'
import { AttrEditor, Animate, PageSetting } from './components'
export default defineComponent({
name: 'RightAttributePanel',
@ -57,6 +57,9 @@ export default defineComponent({
title=""
/>
</ElTabPane>
<ElTabPane label="页面设置" name="page-setting">
<PageSetting />
</ElTabPane>
</ElTabs>
</div>
</div>

View File

@ -1,7 +1,7 @@
/*
* @Author:
* @Date: 2021-05-04 05:36:58
* @LastEditTime: 2021-06-14 10:02:47
* @LastEditTime: 2021-06-24 00:36:24
* @LastEditors:
* @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\simulator-editor\comp-render.tsx
@ -22,15 +22,13 @@ export default defineComponent({
}
},
setup(props) {
return () => {
const component = props.config.componentMap[props.element.componentKey]
return component.render({
return () =>
props.config.componentMap[props.element.componentKey].render({
size: {},
props: props.element.props || {},
model: {},
block: props.element,
custom: {}
})
}
}
})

View File

@ -9,11 +9,11 @@
type: 'transition-group',
name: !isDrag ? 'flip-list' : null
}"
item-key="_vid"
v-bind="dragOptions"
:group="group"
v-bind="{ ...dragOptions, $attrs }"
:item-key="itemKey"
@start="isDrag = true"
@end="isDrag = false"
@change="log"
>
<template #item="item">
<div>
@ -46,7 +46,16 @@ export default defineComponent({
drag: {
type: Boolean,
default: false
}
},
itemKey: {
type: String,
default: '_vid'
},
group: {
type: Object,
default: () => ({ name: 'components' })
},
fallbackClass: String
},
emits: ['update:moduleValue', 'update:drag'],
setup(props, { emit }: SetupContext) {
@ -57,17 +66,12 @@ export default defineComponent({
const dragOptions = computed(() => ({
animation: 200,
group: 'components',
disabled: false,
ghostClass: 'ghost'
}))
const log = () => {
// console.log('', evt)
// console.log('', state.VMBlocks)
}
return {
...toRefs(state),
log,
dragOptions
}
}

View File

@ -1,49 +1,59 @@
<template>
<DraggableTransitionGroup
v-model:drag="drag"
v-model="currentPage.blocks"
style="min-height: 500px"
>
<template #item="{ element: outElement }">
<div
class="list-group-item"
:data-label="outElement.label"
:class="{
focus: outElement.focus,
focusWithChild: outElement.focusWithChild,
drag,
['has-slot']: !!Object.keys(outElement.props.slots || {}).length
}"
@contextmenu.stop.prevent="onContextmenuBlock($event, outElement)"
@mousedown="selectComp(outElement)"
>
<comp-render
:key="outElement._vid"
:config="visualConfig"
:element="outElement"
:style="{
pointerEvents: Object.keys(outElement.props?.slots || {}).length ? 'auto' : 'none'
}"
<div class="simulator-container">
<div class="simulator-editor">
<div class="simulator-editor-content" :style="pageStyle">
<DraggableTransitionGroup
v-model:drag="drag"
v-model="currentPage.blocks"
style="min-height: 500px"
>
<template v-for="(value, slotKey) in outElement.props?.slots" :key="slotKey" #[slotKey]>
<SlotItem
v-model:children="value.children"
v-model:drag="drag"
:slot-key="slotKey"
:config="visualConfig"
:on-contextmenu-block="onContextmenuBlock"
:select-comp="selectComp"
:delete-comp="deleteComp"
/>
<template #item="{ element: outElement }">
<div
class="list-group-item"
:data-label="outElement.label"
:class="{
focus: outElement.focus,
focusWithChild: outElement.focusWithChild,
drag,
['has-slot']: !!Object.keys(outElement.props.slots || {}).length
}"
@contextmenu.stop.prevent="onContextmenuBlock($event, outElement)"
@mousedown="selectComp(outElement)"
>
<comp-render
:key="outElement._vid"
:config="visualConfig"
:element="outElement"
:style="{
pointerEvents: Object.keys(outElement.props?.slots || {}).length ? 'auto' : 'none'
}"
>
<template
v-for="(value, slotKey) in outElement.props?.slots"
:key="slotKey"
#[slotKey]
>
<SlotItem
v-model:children="value.children"
v-model:drag="drag"
:slot-key="slotKey"
:config="visualConfig"
:on-contextmenu-block="onContextmenuBlock"
:select-comp="selectComp"
:delete-comp="deleteComp"
/>
</template>
</comp-render>
</div>
</template>
</comp-render>
</DraggableTransitionGroup>
</div>
</template>
</DraggableTransitionGroup>
</div>
</div>
</template>
<script lang="tsx">
import { defineComponent, reactive, toRefs } from 'vue'
import { defineComponent, reactive, computed, toRefs } from 'vue'
import { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils'
import DraggableTransitionGroup from './draggable-transition-group.vue'
import { $$dropdown, DropdownOption } from '@/visual-editor/utils/dropdown-service'
@ -62,14 +72,22 @@ export default defineComponent({
},
emits: ['on-selected'],
setup() {
const { globalProperties } = useGlobalProperties()
const { currentPage, visualConfig, setCurrentBlock } = useVisualData()
const { globalProperties } = useGlobalProperties()
const state = reactive({
drag: false
})
const pageStyle = computed(() => {
const { bgImage, bgColor } = currentPage.value.config
return {
backgroundImage: `url(${bgImage})`,
backgroundColor: bgColor
}
})
//
//@leafId id
//@nodes Json
@ -196,6 +214,7 @@ export default defineComponent({
...toRefs(state),
currentPage,
visualConfig,
pageStyle,
deleteComp,
selectComp,
onContextmenuBlock
@ -206,12 +225,52 @@ export default defineComponent({
<style lang="scss" scoped>
@import './func.scss';
.simulator-container {
display: flex;
width: 100%;
height: 100%;
padding-right: 240px;
align-items: center;
justify-content: center;
@media (max-width: 1314px) {
padding-right: 0;
}
}
.simulator-editor {
width: 560px;
height: 640px;
min-width: 560px;
padding: 10px 100px;
overflow: hidden auto;
background: #fafafa;
border-radius: 5px;
transform: translate(0);
box-sizing: border-box;
background-clip: content-box;
contain: layout;
&::-webkit-scrollbar {
width: 0;
}
&-content {
min-height: 100%;
box-shadow: 0 8px 12px #ebedf0;
}
}
.list-group-item {
position: relative;
padding: 3px;
cursor: move;
transform: translate(0);
> div {
position: relative;
}
&.focus {
content: '';
outline: 2px solid #006eff;

View File

@ -5,7 +5,16 @@
* @descriptionuseVisualData
* @update: 2021/5/6 11:59
*/
import { reactive, inject, readonly, computed, watch, ComputedRef, DeepReadonly } from 'vue'
import {
reactive,
inject,
readonly,
computed,
watch,
ComputedRef,
InjectionKey,
DeepReadonly
} from 'vue'
import { useRoute, useRouter } from 'vue-router'
import {
VisualEditorModelValue,
@ -21,7 +30,7 @@ import { CacheEnum } from '@/enums'
export const localKey = CacheEnum.PAGE_DATA_KEY
// 注入jsonData的key
export const injectKey = Symbol('injectKey')
export const injectKey: InjectionKey<string> = Symbol()
interface IState {
currentBlock: VisualEditorBlockData // 当前正在操作的组件
@ -43,17 +52,26 @@ export interface VisualData {
setCurrentBlock: (block: VisualEditorBlockData) => void // 设置当前正在操作的组件
}
/**
* @description
*/
export const createNewPage = ({ title = '新页面', path = '/' }) => ({
title,
path,
config: {
bgColor: '',
bgImage: ''
},
blocks: []
})
const defaultValue: VisualEditorModelValue = {
container: {
width: 360,
height: 960
},
pages: {
'/': {
title: '首页',
path: '/',
blocks: []
}
'/': createNewPage({ title: '首页' })
}
}
@ -106,7 +124,7 @@ export const initVisualData = (): VisualData => {
}
// 添加page
const incrementPage = (path = '', page: VisualEditorPage) => {
state.jsonData.pages[getPrefixPath(path)] ??= page ?? { title: '新页面', path, blocks: [] }
state.jsonData.pages[getPrefixPath(path)] ??= page ?? createNewPage({ path })
}
// 删除page
const deletePage = (path = '', redirectPath = '') => {

View File

@ -13,9 +13,7 @@
</el-aside>
<el-main>
<!-- 中间编辑区域start -->
<Simulator>
<simulator-editor />
</Simulator>
<simulator-editor />
<!-- 中间编辑区域end -->
<!-- 右侧属性面板start -->
@ -31,7 +29,6 @@ import Header from './components/header/index.vue'
import LeftAside from './components/left-aside/index.vue'
import RightAttributePanel from './components/right-attribute-panel'
import SimulatorEditor from './components/simulator-editor/simulator-editor.vue'
import Simulator from './components/common/simulator.vue'
</script>
<style lang="scss">

View File

@ -84,7 +84,12 @@ export function createEditorInputNumberProp({
/*---------------------------------------color-------------------------------------------*/
export function createEditorColorProp(label: string, defaultValue?: string): VisualEditorProps {
interface EditorColorProp {
label: string
defaultValue?: string
}
export function createEditorColorProp({ label, defaultValue }: EditorColorProp): VisualEditorProps {
return {
type: VisualEditorPropsType.color,
label,

View File

@ -2,6 +2,9 @@ import { VisualEditorProps } from './visual-editor.props'
import { inject, provide } from 'vue'
import { useDotProp } from '@/visual-editor/hooks/useDotProp'
/**
* @description
*/
export interface VisualEditorBlockData {
_vid: string // 组件id 时间戳
moduleName: keyof ComponentModules // 组件所属的模块(基础组件、容器组件)
@ -21,14 +24,26 @@ export interface VisualEditorBlockData {
animations?: Animation[] // 动画集
[prop: string]: any
}
/**
* @description
*/
export interface PageConfig {
bgImage: string // 背景图片
bgColor: string // 背景颜色
}
/**
* @description
*/
export interface VisualEditorPage {
title: string // 页面标题
path: string // 页面路径
isDefault?: boolean // 404是重定向到默认页面
config: PageConfig // 页面配置
blocks: VisualEditorBlockData[] // 当前页面的所有组件
}
/**
* @description =>
*/
export interface VisualEditorPages {
[path: string]: VisualEditorPage
}

View File

@ -29,9 +29,10 @@ export default ({ mode }: ConfigEnv): UserConfig => {
targets: ['defaults', 'not IE 11']
}),
ViteComponents({
globalComponentsDeclaration: true,
// 自动导入组件(还不够完善,可能会有样式丢失)
// valid file extensions for components.
extensions: ['vue', 'tsx'],
extensions: ['vue', 'tsx', 'js'],
customComponentResolvers: [ElementPlusResolver(), VantResolver()]
}),
styleImport({

630
yarn.lock

File diff suppressed because it is too large Load Diff