2-2 LText 组件初步实现
<template>
<div class="editor-container">
<a-layout>
<a-layout-sider width="300" style="background: #fff">
<div class="sidebar-container">组件列表</div>
</a-layout-sider>
<a-layout style="padding: 0 24px 24px">
<a-layout-content class="preview-container">
<p>画布区域</p>
<!--引用的是component-->
<component
class="preview-list"
id="canvas-area"
v-for="item in components"
:key="item.id"
:is="item.name"
v-bind="item.props"
>
</component>
</a-layout-content>
</a-layout>
<a-layout-sider
width="300"
style="background: #fff"
class="settings-panel"
>
组件属性
</a-layout-sider>
</a-layout>
</div>
</template>
<script lang="ts">
import { GloabalDataProps } from '@/store'
import { defineComponent, computed } from 'vue'
import { useStore } from 'vuex'
import LText from '@/components/LText.vue'
export default defineComponent({
components: { LText },
setup() {
const store = useStore<GloabalDataProps>()
return {
components: store.state.editor.components,
}
},
})
</script>
<style lang="less" scoped>
.editor-container .preview-container {
padding: 24px;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.editor-container .preview-list {
padding: 0;
margin: 0;
border: 1px solid #efefef;
background: #fff;
overflow-x: hidden;
overflow-y: auto;
position: fixed;
}
</style>
<template>
<!--component 的tag是一个的动态组件-->
<component :is="tag" :style="styleProps" class="l-text-component">
{{ text }}
</component>
</template>
<script>
import { computed, defineComponent, PropType } from 'vue'
import _ from 'lodash-es'
export default defineComponent({
name: 'l-text',
props: {
text: {
type: String,
},
fontSize: {
type: String,
},
tag: {
type: String,
default: 'div',
},
},
setup(props) {
console.log(props)
const styleProps = computed(() => _.pick(props, ['fontSize']))
return {
styleProps,
}
},
})
</script>
<style lang="less" scoped>
h2.l-text-component,
p.l-text-component {
margin-bottom: 0;
}
button.l-text-component {
padding: 5px 10px;
cursor: pointer;
}
.l-text-component {
box-sizing: border-box;
white-space: pre-wrap;
position: relative !important;
width: 100%;
height: 100%;
}
</style>
2-3 LText 添加通用属性
配置组件的通用默认属性以及 l-text 组件的特有默认属性。
...
<script>
import { computed, defineComponent, PropType } from 'vue'
import _ from 'lodash-es'
import {
transformToComponentProps,
textDefaultProps,
textStylePropNames,
} from '@/ts/defaultProps'
const defaultProps = transformToComponentProps(textDefaultProps)
//增加其他通用属性
export default defineComponent({
name: 'l-text',
props: {
tag: {
type: String,
default: 'div',
},
...defaultProps,
},
setup(props) {
const styleProps = computed(() => _.pick(props, textStylePropNames))
return {
styleProps,
}
},
})
</script>
...
import _, { without } from 'lodash-es'
export interface CommonComponentProps {
// actions
actionType: string
url: string
// size
height: string
width: string
paddingLeft: string
paddingRight: string
paddingTop: string
paddingBottom: string
// border type
borderStyle: string
borderColor: string
borderWidth: string
borderRadius: string
// shadow and opacity
boxShadow: string
opacity: string
// position and x,y
position: string
left: string
top: string
right: string
}
export const commonDefaultProps: CommonComponentProps = {
// actions
actionType: '',
url: '',
// size
height: '',
width: '373px',
paddingLeft: '0px',
paddingRight: '0px',
paddingTop: '0px',
paddingBottom: '0px',
// border type
borderStyle: 'none',
borderColor: '#000',
borderWidth: '0',
borderRadius: '0',
// shadow and opacity
boxShadow: '0 0 0 #000000',
opacity: '1',
// position and x,y
position: 'absolute',
left: '0',
top: '0',
right: '0',
}
export interface TextComponentProps extends CommonComponentProps {
text: string
fontSize: string
fontFamily: string
fontWeight: string
fontStyle: string
textDecoration: string
lineHeight: string
textAlign: string
color: string
backgroundColor: string
}
//extends 追加
export interface TextComponentProps extends CommonComponentProps {
text: string
fontSize: string
fontFamily: string
fontWeight: string
fontStyle: string
textDecoration: string
lineHeight: string
textAlign: string
color: string
backgroundColor: string
}
export const textDefaultProps: TextComponentProps = {
// basic props - font styles
text: '正文内容',
fontSize: '14px',
fontFamily: '',
fontWeight: 'normal',
fontStyle: 'normal',
textDecoration: 'none',
lineHeight: '1',
textAlign: 'left',
color: '#000000',
backgroundColor: '',
...commonDefaultProps,
}
export interface ImageComponentProps extends CommonComponentProps {
src: string
}
export const imageDefaultProps: ImageComponentProps = {
src: 'test.url',
...commonDefaultProps,
}
// 排除非样式属性
export const textStylePropNames = without(
Object.keys(textDefaultProps),
'actionType',
'url',
'text',
)
// 转换成组件的props属性
export const transformToComponentProps = (props: { [key: string]: any }) => {
return _.mapValues(props, (item) => {
return {
type: item.constructor,
default: item,
}
})
}
2-4 LText 使用 hooks 重用逻辑
封装一个 hooks ,挑选出样式属性,并返回一个点击事件处理函数。
import { computed } from 'vue'
import _ from 'lodash-es'
export default function useComponentCommon<T extends { [key: string]: any }>(
props: T,
picks: string[],
) {
const styleProps = computed(() => _.pick(props, picks))
const handleClick = () => {
if (props.actionType === 'url' && props.url) {
window.location.href = props.url
}
}
//事件类型:无|跳转URL下拉菜单
return { styleProps, handleClick }
}
<template>
<!--component 的tag是一个的动态组件-->
<component
:is="tag"
:style="styleProps"
class="l-text-component"
@click="handleClick"
>
{{ text }}
</component>
</template>
<script>
import { computed, defineComponent, PropType } from 'vue'
import _ from 'lodash-es'
import {
transformToComponentProps,
textDefaultProps,
textStylePropNames,
} from '@/ts/defaultProps'
import useComponentCommon from '@/hooks/useComponentCommon'
const defaultProps = transformToComponentProps(textDefaultProps)
export default defineComponent({
name: 'l-text',
props: {
tag: {
type: String,
default: 'div',
},
...defaultProps,
},
setup(props) {
console.log(props)
const { styleProps, handleClick } = useComponentCommon(
props,
textStylePropNames,
)
return {
styleProps,
handleClick,
}
},
})
</script>
...