One, foreword
This article will give you a quick understanding of how to build a code-free platform. Here is the difference between a code-free and a low-code platform:
- Code-free development platform refers to the practice system development without any code development, only need to pull pull, suitable for operators and other people who are not familiar with programming
- Low code development platform is the use of a small amount of the simplest code can be completed program development
Usage Scenarios:
- Code-free development platforms are ideal for building for specific scenarios, such as surveys, home page content, double 12 events, etc
- Low code development platforms are not only suitable for specific small applications, but can be more flexible for customization.
The content of this picture is the content of the specific building block platform, which is also the focus of this article
Ii. Platform structure
We can see that the building platform is divided into four parts: the head toolbar, the left material library, the middle display area and the right dynamic modification area.
Materials warehouse on the left
The warehouse stores our various active component libraries. But because our modular platform structures involved in the activities and build the scene very diverse (such as: our agencies show home page content page, website content, marketing activities, analysis of questionnaire survey page, reading course, page, etc), each activity has its own special component libraries, so how to reasonable design of data structure is very important.
The general data structure is as follows:
{id: 'XXX ', title: 'OrgIntro', type: 'OrgIntro', icon:' httpXXXX. PNG ', belong: ['home'], limit: 1, identity:3,}, {id: 'XXX ', title: 'Banner', icon:' httpXXXX. PNG ', belong: ['sitesEditor','questionnaire'...] , limit: 'infinity', identity:1, },Copy the code
Let me outline the overall structure:
-
Title Indicates the title of the component
-
Type is the type of each material. Correspond to the Type of the Component in our middle display area
<component :is='item.type'></componet> Copy the code
-
An icon is an icon
-
Belong represents the current activity scene. We have a corresponding type for each scene. For example, home represents the home page, questionnaire represents the questionnaire type and so on. Therefore, an array can be used to represent the current activity scene to dynamically decide whether to display the material
v-show="item.belong.includes(sitesPath)" Copy the code
Item represents each material and sitesPath represents the current activity scenario.
-
Limit is used to limit the amount of material that can be added to the middle display area. Because there are some scenarios where we don’t want to add a material more than once, limit is a good way to limit it. Here the principle is to drag the material to the middle display area, according to the material type to determine how many same material in the middle display area, and compare with the limit can be limited.
-
Identity can be understood as user level. 1 to 3 represent experience users, ordinary members, and advanced VIPs. And the user level is different corresponding function also is different. Such as:
- At higher levels, the limit may not be limited
- The higher the level, the more customizations the user can have on the current component
- .
Three, the middle display area
Let’s see how to render the middle display:
<draggable
:disabled="isDisable"
:list="items"
:group="{ name: 'sites-editor', pull: false }"
@add="onAdd"
@sort="onSort"
>
<transition-group class="item-list" id='item-list'>
<div
:class="{ item: true, active: activeItemId === item.id }"
v-for="(item, index) in items"
:key="index"
@click="activeItem(item)"
@contextmenu.prevent="onContextmenu"
>
<component :is="item.type" :moduleProp="item"></component>
<item-action :index="index" :total="total" @doAction="doAction"></item-action>
</div>
</transition-group>
</draggable>
Copy the code
Analysis is as follows:
3.1 vuedraggable
The VueDraggable library we use for drag is an excellent open source library. Specific use you can see the official website, here is not much introduction
3.2 Dynamic Component
The rendering component adopts the dynamic component in VUE, depending on the material library each material has a unique type to render the corresponding component.
Also, lazy loading of the components rendered here is recommended
components: {
OrgIntro: () = > import('.. /modules/home/OrgIntro/Playground'),
Banner: () = > import('.. /modules/common/Banner/Playground'),
// ...
}
Copy the code
3.3 item-action Selected component
When we click the component, there will be a small icon on the right side of the move up and down and delete (move up and down can also be triggered by dragging).
Item-action represents the currently selected component and can be moved up, down, and deleted.
<div class="item-action-wrap">
<a-icon type="up-circle" @click="$emit('doAction', 'up', index)" v-if=! "" isFirst" />
<a-icon type="down-circle" @click="$emit('doAction', 'down', index)" v-if=! "" isLast" />
<a-icon class="close" type="close-circle" @click="$emit('doAction', 'delete', index)" />
</div>
<script>
export default {
props: {
index: {
type: Number.default: 0,},total: {
type: Number.default: 0,}},computed: {
isFirst() {
if (this.index === 0) {
return true
}
return false
},
isLast() {
if (this.index === this.total - 1) {
return true
}
return false}},}</script>
Copy the code
Analysis:
- IsFirst and isLast because the first component and the last component are
Unable to move up or down
- Finally, they all send an event externally
doAction
, this function does:- It’s not hard to delete or move things externally
splice
operate Records the currently selected component object
.Modify the content of the data in the right half
This is where it was delivered
- It’s not hard to delete or move things externally
3.4 onContextmenu
A VUE-ContextMenuJS library is recommended to easily display the right click popup menu. The specific writing is as follows
onContextmenu(item,index) {
this.$contextmenu({
zIndex: 99999999.items: [{label: The 'top'.onClick: () = > this.onTop(index)
},
{
label: 'after'.onClick: () = > this.onBack(index)
},
{
label: 'copy'.onClick: () = > this.onCopy(item,index)
},
{
label: 'delete'.onClick: () = > this.onDelete(index)
},
{
label: 'paste'.disabled:! (this.copyItem&&this.copyItem.type)
onClick: () = > this.onCopy(item,index)
},
],
event,
customClass: 'class-a'.minWidth: 230.zIndex:9999,})return false
}
Copy the code
Analysis:
-
The top, back, and delete are essentially splice methods to replace or delete components
-
Replication requires a deep copy of the current element
this.copyItem = _.cloneDeep(item) Copy the code
CloneDeep here is the deep-copy method of the classic loadsh library
-
The premise of pasting is that it has already been copied, so we can judge based on whether there is a copyItem
Iv. Content modification area on the right
CurrentEdit is generated by the doAction method mentioned in the middle display area. CurrentEdit is the corresponding component when clicked, and the corresponding content modification area is rendered by the dynamic component of vue.
<component :is="`${currentEdit.type}RightSider`" :moduleProp="currentEdit"></component>
Copy the code
5. Top toolbar
Toolbar can greatly improve our efficiency, there are download template, upload template, undo, forward, copy, delete, preview poster + save poster functions. So let’s look at one by one how do we do that
5.1 Undo Forward
First, understand how undo moves forward:
- Undo advance is actually
The snapshot
The principle of. - Let’s use one
An array of
To store the components after the current various operations (this involves sorting, adding, deleting, pasting, topping, and pasting)Tabular data
- And then rely on
Pointer to the index
To keep adding plus one or minus one, keep goingforward
andback
And get the data of various statesTo assign a value
- It must be
Pay attention to
One point: is when the undo, and after a new operation, this time need to putData was cleared before the original undo
Instead,New to join
List data after the operation
Take a look at the illustration:
Various operations:
Undo advance:
After undoing and performing various operations:
Then look at how the code is implemented:
const snapshot = [] // Snapshot array
let currentSnapshotIndex = -1 // Snapshot index
/ / cancel
undo() {
if (this.currentSnapshotIndex >= 0) {
this.currentSnapshotIndex--
this.items = _.cloneDeep(this.snapshot[this.currentSnapshotIndex])
}
},
/ / to go forward
forward() {
if (this.currentSnapshotIndex < this.snapshot.length - 1) {
this.currentSnapshotIndex++
this.items = _.cloneDeep(this.snapshot[this.currentSnapshotIndex])
}
},
// Add a new snapshot
addSnapshot() {
this.snapshot[++this.currentSnapshotIndex] = _.cloneDeep(this.items)
if (this.currentSnapshotIndex < this.snapshot.length - 1) {
this.snapshot = this.snapshot.slice(0.this.currentSnapshotIndex + 1)}}Copy the code
Analysis:
-
The undo function reverses the currentSnapshotIndex pointer (-1), so consider that currentSnapshotIndex is less than 0. If the value is less than 0, it is the earliest operation and cannot be revoked.
-
If currentSnapshotIndex is greater than the length of the snapshot array, it is the latest operation.
-
AddSnapshot adds a record of the current action to the snapshot at the time of the action (sort, Add, Delete, Paste, top, and post-action) and is the basis for undo and forward.
-
Note that in addSnapshot, when the undo is performed and a new operation is performed, the data after the undo needs to be emptied. Therefore, if currentSnapshotIndex is less than the current snapshot number, the undo has been performed and the subsequent state should be empty. So just remember that addSnapshot is always up to date.
5.2 Import and Export
Because our platform is targeted at b-end users of various educational institutions, we provide various templates to facilitate the rapid establishment of pages by various institutions. The diagram below:
These templates are generated by following these steps
- Clicking the Export template will export the JSON file
- Clicking On the Save poster will save the image
- To template management, get the picture and JSON file form submission to the database
- The template library now has the template poster and corresponding JSON data
These processes do not need our front-end development to cooperate, relying on the operation personnel can be generated according to the activity arrangement, greatly freeing the front-end personnel.
Take a look at the export functionality
downTemplateJson() {
const blob = new Blob([JSON.stringify(this.items)], { type: ' ' })
saveAs(blob, 'template.json')}Copy the code
Analysis:
- Here’s a good library to use:file-saver. There is no native support
saveAs()
SaveAs () interface is implemented on the browser to save files - We put the currently displayed component list
serialization
And then save the download
Then look at the implementation of the import (is there a good GIF software? You can add mosaics according to the time)
Import I am using ant-Design upload
customRequest(date) {
const reader = new FileReader()
reader.onload = (e) = > {
const data = e.target.result
this.items = JSON.parse(data)
}
reader.readAsText(date.file)
}
Copy the code
The code is also very simple to read the contents of the file
5.3 Preview poster + Save Poster
First look at the preview poster:
Preview posters and save posters are used to generate images of the contents of the display area in the middlecanvas
To implement. It is recommended to usehtml2canvasLibrary, it can putDom elements
Generate the corresponding canvas.
Let’s look at the code first
<a-modal
:visible="imgPreviewSrc ! = = ""
@ok="savePoster"
@cancel="() => (imgPreviewSrc = '')"
width="500"
title="Poster display"
okText="Save the poster"
>
<img :src="imgPreviewSrc" class="postImage" />
</a-modal>
<script>
/ /...
methods: {showPoster() {
const itemListElement = document.getElementById('item-list')
html2canvas(itemListElement, {
dpi: window.devicePixelRatio,
useCORS: true.// Enable cross-domain configuration
scale: 1,
}).then((canvas) = > {
const url = canvas.toDataURL('image/png')
this.imgPreviewSrc = url
})
},
}
</script>
Copy the code
Analysis:
-
Generate canvas through html2Canvas
-
Then through toDataURL to base64 for display
-
ImgPreviewSrc pops up when it has a value
-
Note that HTML2Canvas has a bug when setting display: -webkit-box; Element content cannot be displayed when
Scenario: I set an ellipsis to overflow the text, which will not show the contents of the element
h4 { color: # 333333; width: 200px; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden; } Copy the code
Save the posters
You can see that when you open the popup to show the poster, there is a savePoster button
savePoster() {
saveAs(this.dataURLtoBlob(this.imgPreviewSrc), 'poster.png')}dataURLtoBlob(dataurl) {
const arr = dataurl.split(', ')
const mime = arr[0].match(/ : (. *?) ; /) [1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
}
Copy the code
Analysis:
- DataURLtoBlob can
base6
toblob
- And then get the BLOB
file-saver
It’s time to export the image