OpenDataV plans to use sub-libraries to add sub-components, that is, each component is treated as a sub-library, and the sub-library has its own dependencies, while the dependencies of the project itself are only for the framework, so we develop each component as a sub-library. Next, I will take you step by step to develop a digital display component in detail.
Create component directories and files
- Go to the component library directory
All draggable components are stored in the src/resource/components
directory
cd src/resource/components
- Create directory based on component name
The default component directory is named after the name of the component. Of course, it can be named according to your own requirements. The component can be placed in the components
directory or in its subdirectory.
mkdir DigitalText
- Fixed files required to create components
The necessary files for each component include vue file xxx.vue
, configuration file config.ts
, export file index.ts
, each file has its own use, needless to say vue file It is the main body of component rendering. The name of the export file is fixed as index.ts
, mainly to export the information of the component for external reference. The configuration file is mainly the configuration item displayed on the right side of the editing page, which we will describe in detail later. So here we need to create three files: DigitalText.vue、config.ts、index.ts
Above we have created the files required for the component, and now we need to initialize the component
Initialize the component file
Because our components are imported in the form of sub-libraries, we need to initialize the package and execute the following command
cd src/resource/components/Text/DigitalText
npm init
Here we use npm
initialization package will let us choose to fill in some data.
Next, let's initialize the component file DigitalText.vue
, and initialize the simplest component data first
<template>
<div>数字展示</div>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped></style>
Then we have to initialize the component's configuration file config.ts
import { ComponentGroup, FormType } from '@/enum'
import type { PropsType } from '@/types/component'
import { BaseComponent } from '@/resource/models'
export const componentName = 'Digital'
class DigitalComponent extends BaseComponent {
constructor(id?: string, name?: string, icon?: string) {
super({
component: componentName,
group: ComponentGroup.TEXT,
name: name ? name : '数字文本',
id,
width: 100,
height: 30,
icon
})
}
}
export default DigitalComponent
The points to be explained here: componentName
is the registered name of the component in the project, so it must be unique, group
is the grouping of the components, and the grouping here is mainly displayed on the component dragging page , its type ComponentGroup
is fixed and can be added by yourself. The display position is as follows:
name
is the name of the component displayed on the dragged page, width
and height
are the initial size of the component dragged to the canvas
After configuring the component data, you can configure the export file index.ts
, mainly to export the component name, component object and configuration items.
import DigitalTextComponent, { componentName } from './config'
export default {
componentName,
component: () => import('./DigitalText.vue'),
config: DigitalTextComponent
}
At this point of initialization, our components can already be used normally on the edit page. Let's take a look at the effect:
On the edit page on the right, we see 样式
and 属性
, all components contain the basic style 位置大小
, including the upper and lower margins and width and height of the components, Attributes include public attributes, of which 组件
and 组件ID
cannot be modified, mainly for display, 名称
can be modified, and the name attribute is mainly displayed on the layer , after modifying the name, the modified name will be displayed on the layer in response.
This is the most basic component. It can only display fixed data and cannot perform any configuration. Next, we will do the configuration items of the component.
component configuration item
style configuration
As a text display component, the most basic font-related attribute configuration should have, such as 字体、字体颜色、字体大小、字体宽度
, the configuration item is still added in the configuration file, inherited from the private attribute of the basic component class _style
class DigitalTextComponent extends BaseComponent {
constructor(id?: string, name?: string, icon?: string) {......}
_style: PropsType[] = [
{
label: '字体设置',
prop: 'font',
children: [
{
prop: 'color',
label: '颜色',
type: FormType.COLOR,
componentOptions: {
defaultValue: 'skyblue'
}
},
{
prop: 'fontSize',
label: '字体大小',
type: FormType.NUMBER,
componentOptions: {
defaultValue: 20
}
},
{
prop: 'fontWeight',
label: '字体宽度',
type: FormType.FONT_WEIGHT,
componentOptions: {
defaultValue: 200
}
},
{
prop: 'fontFamily',
label: '字体',
type: FormType.FONT_STYLE,
componentOptions: {
defaultValue: 'Arial'
}
}
]
}
]
}
样式配置的格式已经定义好了,其中需要注意的是所有children
下的子项中prop
html
元素的css
--Attribute, specific css
css
attribute name is not what we filled in the style
file, but the corresponding name in js
, which can be found online Search: css3 中文手册
, similar to the following:
Let's elaborate on the meaning of each configuration item:
- label: configure the displayed group name
- prop: the only attribute to distinguish, this attribute should be different from other configurations in the same level
children: the configuration items under this property group
- label: the name of each property
- prop:
css
property value - type: The component type displayed when the property is edited, the currently displayed components are fixed, and the types are defined in
FormType
- componentOptions: Configuration items corresponding to attributes, different types of components have different configuration items, you can view the definition in
src/types/component.d.ts
, all configurations havedefaultValue
configuration, as the attribute initialization Defaults
After configuring the style, let's take a look at the effect on the edit page:
After figuring out the style configuration, let's talk about the attribute configuration. The attribute configuration is consistent with the style configuration format. There are some small details that need to be paid attention to.
property configuration
The attribute configuration is to inherit private variables _prop
, the configuration format is the same as the style, we configure an example attribute here:
class DigitalTextComponent extends BaseComponent {
constructor(id?: string, name?: string, icon?: string) {......}
_prop: PropsType[] = [
{
label: '数据配置',
prop: 'dataconfig',
children: [
{
prop: 'data',
label: '数据',
type: FormType.NUMBER,
componentOptions: {
defaultValue: 100000,
max: 99999999,
min: 0
}
}
]
}
]
}
The format is not explained here, here we use the numeric type, so the maximum and minimum values can be configured.
The next step is to use the attribute configuration in the vue
file. The attribute is not like the style. The style is html
The element itself supports it, so as long as we configure it, it will take effect. However, the attributes are exclusive to the component. It is up to us to write the logic for which attributes to produce. Therefore, after configuring the attributes, we will only see the attribute display and configuration on the edit page, but after the actual configuration, there will be no effect. The specific effect We implement it in vue
.
property usage
First we need to add a type definition file, because ts
the most basic advantage is type hinting, and the component base class we encapsulate is generic, so we need to use its own attribute type definition in each component, Defined as follows:
// DigitalText/type.ts
export interface DigitalType {
dataconfig: {
data: number
}
}
For accurate prompting, the type definition must be consistent with the attribute configuration, specifically children
below prop
as the attribute value, children
outside prop
As an attribute key, you can compare the configuration in ---746a42440387c605628884531dbee711 type.ts
with the configuration in _prop
.
The configuration information of components is passed in from the outside, so all components must receive external data, we have defined a fixed format
const props = defineProps<{
component: DigitalTextComponent
}>()
All component-related information will be passed in through component
. In order to monitor property changes and type hints, we encapsulate a hook to reduce common processing in each component, useProp
Use as follows:
const propChange = (prop: string, key: string, value: number) => {
console.log(prop, key, value)
}
const { propValue } = useProp<DigitalType>(props.component, propChange)
useProp
Receives three parameters, one is component
, mainly to add type hints, so a generic definition is also passed in here, that is, we are in type.ts
The type defined in , the other two parameters are the property change callback function and the style change callback function. Under normal circumstances, we only need to deal with the property change callback, and the style change will take effect automatically, so basically no need to deal with it, only if there are special needs.属性变化回调函数中有三个参数, prop
b94ea7e14ddfecf66c4137d3f4e6b29c---对应的prop
值, key
的是属性配置中children
The children
prop
value in ---, and value
is the value of the attribute change.
The final result of our attribute processing is as follows:
<template>
<div>{{ data }}</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import DigitalTextComponent from './config'
import { useProp } from '@/resource/hooks'
import { DigitalType } from './type'
const props = defineProps<{
component: DigitalTextComponent
}>()
const propChange = (prop: string, key: string, value: number) => {
if (prop === 'dataconfig' && key === 'data') {
data.value = value
}
}
const { propValue } = useProp<DigitalType>(props.component, propChange)
const data = ref<number>(propValue.dataconfig.data)
</script>
<style lang="less" scoped></style>
Take a look at the effect on the page:
Above, we used property callbacks to handle value change responses. In fact, there are other ways to handle them. We need to understand what is the fundamental requirement of property callbacks? The main reason is that after editing the corresponding properties, we can monitor the changes in the component and feed them back to the display. At the same point, there are more methods available.
- Directly use
props
the passed attribute value to render data intemplate
<template>
<div>{{ propValue.dataconfig.data }}</div>
</template>
<script lang="ts" setup>
import DigitalTextComponent from './config'
import { useProp } from '@/resource/hooks'
import { DigitalType } from './type'
const props = defineProps<{
component: DigitalTextComponent
}>()
const { propValue } = useProp<DigitalType>(props.component)
Because vue
is responsive, the data in props
can respond to changes, so we can use it directly in template
without any monitoring .
- Use
computed
orwatch
listen for property changes
This is the same as the above, vue
will automatically help us process the responsive data, as long as we use the computed attribute of vue
a1e7d0bbb58023776f487fe4c19adcd6--- or watch
to also monitor attribute changes .
<template>
<div>{{ data }}</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import DigitalTextComponent from './config'
import { useProp } from '@/resource/hooks'
import { DigitalType } from './type'
const props = defineProps<{
component: DigitalTextComponent
}>()
const { propValue } = useProp<DigitalType>(props.component)
const data = computed<number>(() => {
return propValue.dataconfig.data
})
</script>
<style lang="less" scoped></style>
- Another use of property change callback
<template>
<div>{{ data }}</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import DigitalTextComponent from './config'
import { useProp } from '@/resource/hooks'
import { DigitalType } from './type'
const props = defineProps<{
component: DigitalTextComponent
}>()
const propChange = () => {
data.value = propValue.dataconfig.data
}
const { propValue } = useProp<DigitalType>(props.component, propChange)
const data = ref<number>(propValue.dataconfig.data)
</script>
<style lang="less" scoped></style>
The property change callback accepts any parameters. We can choose to receive parameters or not. In some components with many configuration items, we do not want to judge the changed properties one by one in the property callback, so we can use this method. , in this way, we just use the attribute callback as a notification, that is, to notify us that the attribute has changed, and we don't care which attribute has changed, just modify all the attributes, although it sounds more troublesome, But it does work in some complex components. Here we have to understand that as long as the attributes change, the data in prop
must also change, so we can take the data in prop
at any time. It is the latest.
Summarize
At this point, the whole process of adding a component is finished. According to the current development progress, basically all parts have been covered. If someone finds any problems during use or is not clear enough, you can It is mentioned in the issue of the project, and it can also be reported in other ways.
Add WeChat Official Account for more information:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。