3

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 have defaultValue 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 in template
 <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 or watch 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:


星星在线
26 声望2 粉丝