经过前面九篇文章的讲解和实现,目前我们的低代码已经初具雏形了。
如果你是第一次看到这一篇文章, 建议先看一下第一节内容:
从零实现一套低代码(保姆级教程) — 【1】初始化项目,实现左侧组件列表
那我们可以回忆一下之前我们是怎么实现属性面板的。
本质上就是在画布区选中节点后,在redux保存。在通过右侧属性面板,修改节点的属性,从而引起组件的重新渲染。
那如果我们想实现一个样式面板,整体思路不就和属性面板一模一样嘛。
甚至,InputComponent这个组件我们都可以复用!!!!!
OK,现在我们在项目中实现一下样式面板。
之前我们有一个attributeMap用来保存所有组件的属性配置,现在我们在它的同级目录新增一个styleMap用来保存所有组件分样式配置。
之前我们有一个comAttribute用来保存各个组件的属性列表,现在新建一个comStyle用来保存各个组件的样式列表。
这里我们先给Button组件的配置好:
XinBuilder2\src\pages\builder\rightPart\staticUtils\styleMap.ts
import { buttonStyle } from "./comStyle/buttonStyle"
interface StyleMap {
[key: string]: Style[]
}
export interface Style {
label: string,
value: string,
type: string,
options?: Array<any>,
defaultValue?: string,
modalType?: string
}
const styleMap: StyleMap = {
Button: buttonStyle
}
export {
styleMap
}
XinBuilder2\src\pages\builder\rightPart\staticUtils\comStyle\buttonStyle.ts
给按钮配置两个样式属性,高度和宽度.
import { Style } from "../styleMap"
const buttonStyle: Style[] = [
{
label: '设置宽度',
value: 'width',
type: 'number'
},
{
label: '设置高度',
value: 'height',
type: 'number'
}
]
export {
buttonStyle
}
OK,现在我们的基础配置就完事了。
对于样式面板,我们只需要照着属性面板进行修改即可,来到rightPart下的index.tsx中。
const items: TabsProps['items'] = [
{
key: 'attributePanel',
label: <div style={{fontSize:'18px',width:'100px',textAlign:'center'}}>属性</div>,
children: getAttributePanel(),
},
{
key: 'stylePanel',
label: <div style={{fontSize:'18px',width:'100px',textAlign:'center'}}>样式</div>,
children: getStylePanel(),
}
];
OK,现在我们实现getStylePanel方法:
const getStylePanel = () => {
const comType = selectNode?.comType || '';
const styleList = styleMap[comType] || []
return <div>
{
styleList.map((item,index) => {
return <div key={index} className='attributeItem'>
<label className='attributeLabel'>{item.label}</label>
<div className='attributeItemValue'>
<InputComponent selectNode={selectNode} {...item} onChange={changeComAttribute(item.value)}/>
</div>
</div>
})
}
</div>
}
getStylePanel 方法和 getAttributePanel方法区别点,只在于遍历的List不同,一个是属性的列表,一个是样式的列表。
OK,现在我们的右侧样式面板就可以看到我们刚才设置的高度和宽度了。
但是有一个问题是什么,如果我给width设置了200,这对组件是无效的。只有200px才会对组件生效。所以这里,我们就再写一个change的方法。不用changeComAttribute了!
const getStylePanel = () => {
// 其他代码
<InputComponent onChange{changeComStyle( item.value)}/>
}
const changeComStyle = (value: string) => {
return (e: any) => {
let attribute = e;
if(typeof e === 'object') {
attribute = e.target.value;
}
if(['width', 'height'].includes(value)) {
attribute += 'px'
}
if(selectNode) {
if(!selectNode.comStyle) {
selectNode.comStyle = {}
}
selectNode.comStyle[value] = attribute;
}
Store.dispatch({type: 'changeComList', value:comList})
}
}
对于宽度和高度,我们给最后组件的样式值加上px。这样就能对组件生效了。
但是右侧属性面板的回显可能就会有问题了(因为number组件接收的是一个字符串了),现在我们再修改一下。
const getComponent = () => {
switch (type) {
// 对于number组件,将组件的字符串值转换为number类型
case 'number': {
return <Input type="number" value={selectNode[value] || parseInt(selectNode?.comStyle?.[value] || '') || '0'} style={{width:'120px'}} defaultValue={defaultValue} onChange = {onChange}/>
}
}
}
现在我们来到组件里,只需要将组件的width和height渲染到组件上即可。
import { Button as AntButton } from 'antd'
export default function Button(props: any) {
const { caption, danger, disabled, ghost, shape, size, type, comStyle } = props
const IconComponent = require('@ant-design/icons')[type]
return (
<div>
<AntButton
style={{...comStyle}}
danger={danger}
disabled={disabled}
ghost={ghost}
shape={shape}
size={size}
icon={type ? <IconComponent /> : null}
>
{caption || '按钮'}
</AntButton>
</div>
)
}
现在我们就可以给Button组件设置宽高了:
现在我们思考一个问题,不管是组件的背景颜色还是字体颜色,都离不开配置颜色。
所以我们需要一个新的属性类型,就是color类型:
const buttonStyle: Style[] = [
{
label: '设置宽度',
value: 'width',
type: 'number'
},
{
label: '设置高度',
value: 'height',
type: 'number'
},
// 新增两种颜色属性
{
label: '字体颜色',
value: 'color',
type: 'color'
},
{
label: '背景颜色',
value: 'backgroundColor',
type: 'color'
}
]
之前我们新增过弹窗类型,现在我们新增一个色板的类型。
const getComponent = () => {
switch (type) {
case 'color': {
case 'color': {
return <ColorPicker disabledAlpha showText value={selectNode?.comStyle?.[value] || ''} style={{width:'120px'}} defaultValue={defaultValue} onChangeComplete = {onChange}/>
}
}
}
然后我们再修改一下changeComStyle方法:
const changeComStyle = (value: string) => {
return (e: any) => {
let attribute = e;
if(typeof e === 'object') {
if(['color', 'backgroundColor'].includes(value)) {
attribute = e.toHexString()
}else{
attribute = e.target.value;
}
}
if(['width', 'height'].includes(value)) {
attribute += 'px'
}
if(selectNode) {
if(!selectNode.comStyle) {
selectNode.comStyle = {}
}
selectNode.comStyle[value] = attribute;
}
Store.dispatch({type: 'changeComList', value:comList})
}
}
OK,现在我们就可以通过色板去调整Button组件的字体颜色和背景颜色了。
OK, 现在我们给Button组件再补充一些常见的样式属性。
import { Style } from "../styleMap"
const buttonStyle: Style[] = [
{
label: '设置宽度',
value: 'width',
type: 'number'
},
{
label: '设置高度',
value: 'height',
type: 'number'
},
{
label: '字体颜色',
value: 'color',
type: 'color'
},
{
label: '背景颜色',
value: 'backgroundColor',
type: 'color'
},
{
label: '边框宽度',
value: 'borderWidth',
type: 'number'
},
{
label: '边框颜色',
value: 'borderColor',
type: 'color'
},
{
label: '边框样式',
value: 'borderStyle',
type: 'select',
options: [
{
value: 'solid',
label: '实线'
},
{
value: 'dotted',
label: '点线'
},
{
value: 'dashed',
label: '虚线'
}
],
defaultValue: 'solid'
}
]
export {
buttonStyle
}
OK,Button组件的样式面板我们就已经完成了。
相关的代码提交在github上:
https://github.com/TeacherXin/XinBuilder2
commit: 第十节: 实现样式面板并支持Button组件的样式配置
如果你已经对Button组件的样式面板比较了解,希望你能把其他组件的样式配置也写出来,这一部分的内容,不会用文章来叙述了。
只会出现在以下的github提交记录上:
https://github.com/TeacherXin/XinBuilder2
commit: 第十节: 支持Form组件的样式配置
https://github.com/TeacherXin/XinBuilder2
commit: 第十节: 支持Icon组件的样式配置
https://github.com/TeacherXin/XinBuilder2
commit: 第十节: 支持Input组件的样式配置