This article is a supplement to the background management system can drag type component design ideas article, recently, the code to do a simple arrangement, has been uploaded to GitHub.

  • Making the address

The server is down, so there is no online demo, you can clone or fork the code and run it directly, with a page demo built in.

Configuration page effect:

Render page effect:

The last article introduced the general idea of drag-and-drop components:

  • Assembly of data structures
  • Component list selection
  • Component drag and drop handling
  • Component configuration information Configuration
  • Processing of requests
  • Pull-down option data processing
  • Table component design
  • Handle buttons and pop-ups
  • Popover and table data linkage
  • Custom slot

Point of quadratic optimization

The following three points are optimized:

  • Multifunctional, open text component
  • Data processing at the page level
  • The design idea of internationalization configuration

Text Component

Text, the text component, was not thought through at first, so in the first version, it was left untouched. The scene that came to mind at that time was the view of row data. When I first thought of this function, it was actually with editing, and then I added a read-only state to the popover property to enable the view function.

Like this:

Then I thought of the “title” and “content” format of message details, so I made it into an open component, involving functions:

  • Static data
  • Dynamic data
  • Custom text

Static data

It’s something we’re going to give users, something the developers have written on the page, like:

Then give it a custom style function, which is written like normal CSS:

This allows us to completely open up the functionality to the user to customize the content and styles.

Dynamic data

Text component adds a property, this property corresponds to the data field, interface data returned by the database, you can set custom formatting methods, interface data format conversion.

Effect:

Custom text

The kinetic energy of custom text is mainly used for processing interface data, such as data addition, subtraction, multiplication and division, and field stitching.

Configuration:

Effect:

As long as the internal custom function is js support can be written, the second parameter of the above custom function, is used to deal with the drop-down data display, specific usage, can go to see the built-in demo, table state column useful to.

Data processing at the page level

If the system itself does not do the routing TAB cache (vue keep-alive), the previous data design idea is OK.

Previously, I used a global Map object to cache the global data inside the component. If a new render request is made each time, this Map object will be updated to the latest. However, if the page caching mode is used, then the newly rendered component global properties will overwrite all the previous global properties, making it almost useless.

So in a later update, we added a page-level attribute to the Map object. All global attributes of the new page will be placed under pageId.

import globalMap from './global-map'

const globalParams = {
  global: {
    pageId: props.pageId,
    pageCode: props.pageCode,
    langCode: ' '
  },
  searchData: {},
  options: {},
  dialogMap: new Map
}
globalMap.set(props.pageId, globalParams)
Copy the code

When we do variable substitution, we avoid data isolation between different pages.

export const getApiInfo = (
  url = ' ',
  params = ' ',
  vals: Record<string.any> = {},
  pageId: string
) = > {
  // Get global data for the current page
  const globalParams = globalMap.get(pageId)
  constp = { ... vals, ... globalParams.global, ... globalParams.searchData }const newUrl = url.replace(/ \ {(. *?) \}/g.(a: string, b: string) = > {
    return Object.prototype.hasOwnProperty.call(p, b) ? p[b] : a
  })
  const newParams = params.replace(/ \ {(. *?) \}/g.(a: string, b: string) = > {
    return Object.prototype.hasOwnProperty.call(p, b) ? p[b] : a
  })
  const obj: Record<string.string> = {}
  newParams.replace(/([a-zA-Z0-9]+?) = (. *?) (&|$)/g.(a: string, b: string, c: string) = > {
    obj[b] = c
    return a
  })

  return {
    url: newUrl,
    params: obj
  }
}
Copy the code

The design idea of internationalization configuration

First of all, the configuration of internationalization, VUE project generally use VUE-I18N plug-in, general front-end in the configuration of internationalization, is to write the language package in the local. Since the page is dynamically configured, we do not know which fields need to be configured during project configuration. Moreover, this design idea can configure the page directly online without repackaging and publishing, so we have to design a scheme to support the above scenarios.

The first step is to dynamically add language packs.

import { createI18n } from 'vue-i18n'

import EnMessage from './en'
import CnMessage from './zh-cn'
import HkMessage from './zh-hk'

const messages: Record<string.any> = {
  'en': EnMessage,
  'zh-cn': CnMessage,
  'zh-tw': HkMessage
}

const map: Record<string.string> = {
  'zh-CN': 'zh-cn'.'zh-HK': 'zh-tw'.'en-US': 'en',}const langtype = window.localStorage.getItem('headerLang')
const localeType = map[langtype || 'zh-CN'] || langtype
let i18n: any = null, hasLoadLang = false

export default function setupI18n() {
  if (i18n) {
    return i18n
  }
  i18n = createI18n({
    locale: localeType as string.fallbackLocale: map['en-US'],
    messages
  })

  return i18n
}

export function loadLocaleMessages(gbl: any, globalI18n: Record<string.any>) {
  if (hasLoadLang) {
    return
  }
  hasLoadLang = true
  // set locale and locale message
  constpreConfig = gbl.getLocaleMessage(localeType) gbl.setLocaleMessage(localeType, { ... preConfig, ... globalI18n.getLocaleMessage(localeType) }) }Copy the code

The load is performed on the index.vue page

import { useI18n } from 'vue-i18n'
import setupI18n, { loadLocaleMessages } from '.. /.. /locale'

const i18n = setupI18n()
// Load multilingual configuration
loadLocaleMessages(i18n.global, useI18n())
Copy the code

So we add an I18N key where we need to translate, and then walk through all components to extract the I18N key.

Configuration page:

// ...
// Component properties
{
  "type": "column"."properties": {
    "label": "Number of comments"."i18n": "article.commentNum".// Internationalized key
    "align": "left"."type": "default"."fixed": "none"."customText": "function fn(row, parse) {\n\n}"."prop": "commentNum"
  },
  "id": "1b8f11a0-ce85-4487-ba84-29eb0cc0cd9d"
}
// ...
Copy the code

The optimization is that adding all the i18N fields is a hassle, so make a convention on the language package.

  • The structure of the language pack data is only divided into two layers, one is page level and the other is field level
  • Add a prefix of all i18n to the page properties, and then parse pageJson again to extract all the internationalized fields

Configuration page Processing

// Page properties
{
  "type": "common"."properties": {
    "title": "Article Management"."code": "articleManage"."langCode": "pageLangCode"."writeVariable": []}}/ / child component[{"type": "column"."properties": {
      "label": "Number of comments"."i18n": "commentNum".// Internationalized key
      "align": "left"."type": "default"."fixed": "none"."customText": "function fn(row, parse) {\n\n}"."prop": "commentNum"
    },
    "id": "1b8f11a0-ce85-4487-ba84-29eb0cc0cd9d"
  }
  // ...
]

// Parse the result{pageLangCode: {commentNum: 'Number of comments'
    // ...}}Copy the code

Rendering page processing

Static field processing<el-button
  type="primary"
  :size="data.properties.size"
  :round="data.properties.round"
  :icon="(Icons as any)[data.properties.searchIcon || 'Search']"
  @click="confirmSearch"
>{{ $tr('dp.search') }}</el-button>// Dynamic field processing<el-form-item
:label="$tr(field.properties, pageId)"
:prop="field.properties.prop"
:required="field.properties.required"
:label-width="field.properties.labelWidth && (field.properties.labelWidth + 'px')"
>
</el-form-item>
Copy the code

The language conversion method here uses the i18N configuration as a component for its own use. It is not used with the project’s I18N because the component may be packaged separately in the component library, which will be separated from the project’s configuration. If packaged with the project, we also need to wrap it to handle dynamic loading scenarios.

// Multi-language conversion processing
export const $tr = (
  props: Record<string.any> | string, pageId = ' ', label = 'label', prop = 'i18n'
) = > {
  if(! t) { t = setupI18n().global.t }if (typeof props === 'string') {
    return t(props)
  }
  const langCode = pageId && globalMap.get(pageId).global.langCode
  if (props[prop] && langCode) {
    return t(`${langCode}.${props[prop]}`)}return props[label]
}
Copy the code

At the end

The component functions are roughly like this. The functions are added bit by bit according to the requirements of our project. In addition, the time is relatively tight, and the code may be messy.

Interested partners, if there is a better method, you can optimize together.

reading

Background management system can be dragged component design ideas