Vue + rich text editor: print template design

Foreword:

Some projects require printing. If only a few places require printing, generally you only need to fix the template for printing. However, our projects are always different. It is clear that as long as the template is fixed, the requirements can be completed. Adding a flexible printing template, which also involves dragging and filling in text, really kills so many brain cells!

need:

The front-end page is a simple three-column layout, as shown in the figure below. It is basically like this. In the left frame is a search box, plus the content that needs to be dragged into the rich text editor, and in the middle is the rich text used to edit the template. Editor, on the right side are various settings (the same as the real one ヽ(ー_ー)ノ,)

The most important requirement is to drag the fields on the left into the rich text editor. The fields are divided into basic fields and some table fields. Because the project's printing template is mostly used to edit various types of printing documents, it is useful. There are many tables, and the basic fields can only be dragged to other areas of the rich text and cannot be placed in the table. The fields of the table can only be placed in the table and cannot be dragged and dropped in other areas; (Note: This kind of project requirements generally require What is used is to determine the target label for dragging and dropping. If it is this label, place it. If not, just return the prompt.)

I won’t say much about the front-end layout. I believe this layout is a piece of cake for everyone (┓(;´_`)┏)

Core content: Field drag and drop filling rich text editing

Preparation: Version of wangeditor v4 (I originally planned to use the v5 version. I used v4 for experiments when I was writing before, and then I forgot to change it, because it is more troublesome to operate the editing part of the editor according to the template settings on the right. All I’m too lazy to change the version)

Official website : Introduction · wangEditor User Documentation

Rendering of drag-and-drop fields: Because the rich text editor ends up with html fragments, which need to be used in other places, the front-end cannot fill in the data if it is used again, so here we let the back-end fill in the data. We use the back-end to fill in the data. The returned "field name" and "variable name" (defined by the backend and used to match the filling data), edit the template, the display is the "field name", and the drag and drop filling is the "variable name" (hidden) (^_ −)☆.

Example:

There is another important point: how to distinguish between "basic fields" and "table fields" when dragging and dropping

The method I use here is to perform data processing when obtaining the back-end data, because the data provided by the back-end is separate.

For example: [{Basic field},{BOM field},{Process field}...]

Therefore, when processing data, an identifier is added to the 'basic field' and 'other fields' to distinguish which field is dragged; the basic field is added: source-field, the other fields are added: BOM, and finally This logo is rendered into the class name of the drag element: class=source-field (specifically shown in the picture above)

     async searchList(){
       let data=this.listQuery
      // 模板字段以及模糊查询
        await templateField(data).then(res=>{
         let datas=[]
         for (const key in res.data) {
               datas.push({name:key,every:res.data[key]})
         }
         let m=[]
         datas.forEach(item=>{
           if(item.name.includes('基础信息字段')){
            item.every.forEach((item,index)=>{
               item.class='source-field'  //添加标识
               item.id=index+1
            })
             m.unshift(item)
           }else{
            item.every.forEach((item,index)=>{
               item.class='BOM'     //添加标识
               item.id=index+1
            })
            m.push(item)
           }
        })
         this.fieldList=m
         })
      },

After the field is rendered, it is filled by dragging:

Common drag and drop fills include: h5 native drag and sortablejs, etc.

h5 drag and drop: HTML5 drag and drop

sortablejs website: Sortable.js Chinese website

At first, I used sortablejs for drag and drop. Finally, I found that the text of the dragged element could not be placed in the rich text editor. Because the encapsulation of sortablejs and the encapsulation of the rich text editor seemed to conflict, so I was forced to give up. After a long time, I finally gave up (╬◣д◢)) and finally used native drag. Native drag needs to be added to the label: draggable=true, which means drag can be performed. Native drag also has events. , I won’t go into details here, you can check out the official website.

 Because we need to drag and drop text, we need to process the data here and splice it into the format required by the backend;

Basic field filling: Example: Order number: {$orderNumber}

Table field filling: Example: {$orderNumber}

The native drag event we use at this time is: when drag starts: @dragstart="onDragStart"

   onDragStart(event) {
         let flag=event.target.classList.contains('source-field')
         let BOM=event.target.classList.contains('BOM')
         event.dataTransfer.setData("flag",flag)
         event.dataTransfer.setData("BOM",BOM)
         let text=event.target.innerText
         let children= event.target.children[0].innerText
         // 判断数据拼接的方式
         if(flag){
            this.itemText = text+':${'+children+'}';
            this.$refs.editor.getItemText(this.itemText)
         }
         if(BOM) {
           this.itemText = '${'+children+'}';
           this.$refs.editor.getItemText(this.itemText)
         }
      },

When we trigger an event, we determine what element triggers the event (e.target)
1. Determine the content innerHTML innerText
2. Tag name nodeName (when judging, the tag name needs to be capitalized)
3. Attribute e.target.hasAttribute("Attribute name"); if it is true, it is false if it is not
4. Class name e.target.classList.contains("class name"); if it is true, if it is not, it is false


The class name we judge here: Because we need to judge what field is being dragged based on the identifier I added when processing the data before, and decide what kind of splicing method to place the text (as shown in the code above)

 At the same time, we add an identifier to the event object based on judgment, which will be used to determine the placement location later (because the requirement form fields can only be placed in the form, and the basic fields cannot be placed in the form). At this point, dragging is basically completed, and the next step is placement; ❥(ゝω・✿ฺ)

 Here we need to go to the component written with rich text editing and use monitoring to monitor drag and drop events: drgover and drop. Drgover needs to block the default event.

 // 监听拖放事件
       this.$refs.editor.addEventListener("dragover", this.onDragOver);
       this.$refs.editor.addEventListener("drop", this.onDrop);

 // 拖放事件
      onDragOver(event) {
         event.preventDefault();
      },
      // 拖放事件
      onDrop(event) {
         event.preventDefault();
         let Td=event.target.tagName
         let flag=event.dataTransfer.getData('flag')
         let BOM=event.dataTransfer.getData('BOM')

         // 判断基础信息字段不能拖动到表格中
         if (flag==='true') {
            if (flag==='true'&&Td=='TD'||flag==='true'&&Td=='TH') {
               this.$message.error('基础信息字段不能拖动到表格中');
             return
            }
             this.fillUp()
         }

        // 判断子列表信息只能放在表格中
         if (BOM==='true') {
            if (BOM==='true'&&Td=='TD') {
              this.fillUp()
              return
            } 
            this.$message.error('子列表字段只能拖拽到表格中,请先创建表格');
         }
      },
      // 富文本编辑器内容填充方法
      fillUp(){
           const selection = window.getSelection();
           let k=selection.anchorNode
           console.log(k.parentNode.tagName);
        //    console.log(k);
           if(!k.tagName&&k.parentNode.tagName!=='P'&&k.parentNode.tagName!=='SPAN'&&k!=='#text'&&k.parentNode.tagName!=='FONT'&&k.parentNode.tagName!=='B'){
                this.$message.error('拖放位置已有内容!');
           };

           if(k.tagName==='P'||k.parentNode.tagName==='P'||k.tagName==='TH'||k.tagName=='TD'||k=='#text'
              ||k.parentNode.tagName=='SPAN'||k.parentNode.tagName==='FONT'||k.parentNode.tagName=='B'){
              const range = selection.getRangeAt(0);
              const text = this.itemText;
              const node = document.createTextNode(text);
              range.deleteContents();
              range.insertNode(node);
            }
      },

Here we first look at the [method of filling rich text editor content] in the above code.

window.getSelection(), returns a Selection object, representing the text range selected by the user or the position of the cursor.

Here we use the Selection object attribute: anchorNode (returns the node where the starting point of the selection is) to get the node at the cursor position. Here I use it to determine whether the drag and drop position has content, because if the original text is selected in the table , the style of the table will be confused when dragging and dropping, so a block is made here, and it can only be placed when there is no text.

When placing, we need to use the getRangeAt method to return the node where the selection starts. Because usually, the user can only select a range, so there is only one selection, so this method is generally: getRangeAt(0)

After returning the node, you need to get the original dragged and dropped text this.itemText, create a node based on the dragged and dropped text, then use the deleteContents() method to delete all the contents of the selected range from the document tree, and then insert the created node into The previously returned node; at this point we have completed drag-and-drop filling in rich text editing; (๑>ڡ<)☆

Detailed explanation of window.getSelection(): About window.getSelection_xiao xu’s blog-CSDN blog

After the rich text filling is completed, we now need to determine the position of the field drag and drop.

         event.preventDefault();
         let Td=event.target.tagName
         let flag=event.dataTransfer.getData('flag')
         let BOM=event.dataTransfer.getData('BOM')

         // 判断基础信息字段不能拖动到表格中
         if (flag==='true') {
            if (flag==='true'&&Td=='TD'||flag==='true'&&Td=='TH') {
               this.$message.error('基础信息字段不能拖动到表格中');
             return
            }
             this.fillUp()
         }

        // 判断子列表信息只能放在表格中
         if (BOM==='true') {
            if (BOM==='true'&&Td=='TD') {
              this.fillUp()
              return
            } 
            this.$message.error('子列表字段只能拖拽到表格中,请先创建表格');
         }

The above code is part of the drag and drop event. The complete code is above.

First determine that the basic fields cannot be placed in the table:

Because the tag we finally placed in the table is <td></td>, here we need to get the tagName of the target tag, and then we use the logo to distinguish the basic field and the table field before getting it. The main purpose is to use the logo to judge. , if the identification of the basic field is true and the tagName of the target tag is TD, a pop-up window will prompt: "The basic field cannot be dragged into the table" and return, otherwise the rich text filling method will be called;

The method for judging that table fields can only be placed in the table is the same as above. For details, you can see the code, which is omitted here ❥(ゝω・✿ฺ)

Finally: At this point, the entire core description has been completed. Many details may not be mentioned (such as template settings to control the editing area), because I am also crossing the river by feeling the stones, and it took me many days to write it. If there is something wrong, I hope everyone can help me. Please correct me if you don’t like it, thank you! I will also write about the use of editing templates later, which is another big problem (when printing) (⌒.−)=★

Guess you like

Origin blog.csdn.net/Xhfc_pf1996/article/details/130383986