VUE implements Studio management background (end): tag type input, name value pair input, dialog box (modal dialog)

In a week, almost every day, I have to work for more than ten hours. Knock code + write composition. The interface prototype is finished. The next step is to write the HTML processing engine of the kernel, pure JS implementation. This demonstration is over. Let's continue to share when the next version of RXEditor is finished.
The remaining functions: tag type input, name value pair input, dialog box (modal dialog), border input, all completed.
css class input, style and attribute input, effect:

Dialog box (model dialog effect)

Overview of previous functional effects:

The tag input box is used to input CSS class. The name is as good as ever. Let's call it RxLabelInput.
The input value is an Array, because there are many places to operate the Array, such as add, delete, change, clone, compare, etc. A better way is to rewrite the Array class in an inherited way and add this writing method to it. However, the rxeidor kernel is implemented in pure JS and placed in an iFrame. It can only pass data through windows message to the main interface. Classes with methods cannot be passed as messages. For the moment, this method is not used, only relevant functions are extracted into independent functions and placed in valueOperate.js.
If the amount of array operations is larger in the future, consider turning it into a general array class.
As introduced in previous issues, the calculation attribute changed is used to identify whether the data has been modified. In the changed calculation attribute, it is necessary to compare whether two values are equal. There is no problem with ordinary strings. To compare arrays, it is most convenient to sort, convert to strings, and compare strings first

aValue.sort().toString() === bValue.sort().toString()

The sort method of the array will change the original array value, which will cause data refresh, and then call the calculation property again, forming a dead cycle. After a long time of debugging, even an empty array will be dead cycle. Therefore, we need to make a copy of the data, and then compare:

if(Array.isArray(a) && Array.isArray(b)){
  //Replicated array
  let aValue = a.concat()
  //Replicated array
  let bValue = b.concat()
  //Comparison array
  return aValue.sort().toString() === bValue.sort().toString() 
}

 

Component code:

<template>
  <div class="label-list">
    <div 
      class="label-item"
      v-for = "val in inputValue"
    >
      {{val}} 
      <span 
        class="remove-button"
        @click="remove(val)"
      >×</span>
    </div>
    <div style="width: 100%"></div>
    <div class="add-button"
      @click="addClick"
    >+</div>
    <div style="width: 100%"></div>
    <input 
      v-show="isAdding" 
      v-model="newValue" 
      autofocus="autofocus" 
      :placeholder="$t('widgets.enter-message')"
      @keyup.13 = "finishAdd"
      ref="inputControl"
    />
  </div>
</template>

<script>
import {addToArray, removeFromArray} from './valueOperate'

export default {
  props:{
    value:{ default:[] }, 
  },
  computed:{
    inputValue: {
      get:function() {
        return this.value;
      },
      set:function(val) {
        this.$emit('input', val);
      },
    },
  },
  data () {
    return {
      isAdding : false,
      newValue : '',
    }
  },
  methods: {
    addClick(){
      this.isAdding = true; 
      this.$refs.inputControl.style.display = 'block'
      this.$refs.inputControl.focus()
    },
    finishAdd(){
      if(this.newValue){
        this.newValue.split(' ').forEach((val)=>{
          if(val){
            addToArray(val, this.inputValue)
          }
        })
        this.newValue = ''
      }

      this.isAdding = false
    },
    remove(val){
      removeFromArray(val, this.inputValue)
    }

  },
}
</script>

<style>
 .label-list{
    background: rgba(0,0,0, 0.15);
    display: flex;
    flex-flow: row;
    flex-wrap: wrap;
    padding:10px;
  }

  .label-list .label-item{
    padding:0 3px;
    background: rgba(255,255,255, 0.15);
    margin:1px;
    border-radius: 3px;
    height: 24px;
    display: flex;
    align-items: center;
  }

  .label-list .remove-button{
    cursor: pointer;
    margin-left: 2px;
  }

  .label-list .add-button{
    background: rgba(255,255,255, 0.15);
    width: 24px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 3px;
    margin: 1px;
    margin-top:3px;
    font-size: 16px;
    padding-bottom:3px;
    cursor: pointer;
  }

  .label-list input{
    outline: 0;
    border: 0;
    background: transparent;
    color: #fff;
    margin-top:4px;
  }
</style>

The name value used to input html attributes and style s also has a pull name for the input control: RxNameValueInput.

The incoming value v-model of this control is an object. As an object, it's a little inconvenient to dynamically add and delete attributes and sort them. Therefore, when processing inside the component, convert this object into a two-dimensional array:

mounted () {
  for(var name in this.inputValue){
    this.valueArray.push([name, this.inputValue[name]])
  }
},

Then watch is an array. When it changes, it is conversed into an object, which is equivalent to completing a two-way binding and conversed into code:

watch: {
  valueArray() {
    this.inputValue = {}
    for(var i = 0; i < this.valueArray.length; i++){
      let name = this.valueArray[i][0]
      let value = this.valueArray[i][1]
      this.inputValue[name] = value
    }
  }
}

 

Code of the whole component:

<template>
  <div class="name-value-box">
    <div class="name-value-row"
      v-for="(item, i) in valueArray"
    >
      <div class="name-input">
        <input v-model="item[0]"
          @blur = "nameBlur(i)"
        >
      </div>
      <div class="separator">:</div>
      <div class="value-input">
        <input v-model="item[1]">
      </div>
      <div class="clear-button"
        @click="remove(i)"
      >×</div>
    </div>
    <div class="name-value-row">
      <div class="name-input">
        <input 
          v-model="newName"
          @keyup.13 = "addNew"
          @blur = "newBlur"
          ref="newName"
        >
      </div>
      <div class="separator">:</div>
      <div class="value-input">
        <input 
          v-model="newValue"
          @keyup.13 = "addNew"
          @blur = "newBlur"
        >
      </div>
      <div class="button-placeholder"
      ></div>
    </div>
  </div>
</template>

<script>
export default {
  props:{
    value:{ default:{} }, 
  },
  computed:{
    inputValue: {
      get:function() {
        return this.value;
      },
      set:function(val) {
        this.$emit('input', val);
      },
    },
  },
  data () {
    return {
      valueArray : [],
      newName : '',
      newValue : '',
    }
  },
  mounted () {
    for(var name in this.inputValue){
      this.valueArray.push([name, this.inputValue[name]])
    }
  },
  methods: {
    addClick(){
    },

    nameBlur(i){
      this.valueArray[i][0] = this.valueArray[i][0].trim()
      if(!this.valueArray[i][0]){
        this.remove(i)
      }
    },

    remove(i){
      this.valueArray.splice(i, 1)
    },

    addNew(){
      this.newName = this.newName.trim()
      if(this.newName && !this.exist(this.newName)){
        this.valueArray.push([this.newName, this.newValue])
        this.newName = ''
        this.newValue = ''
        this.$refs.newName.focus()
      } 
    },

    newBlur(){
      this.newName = this.newName.trim()
      this.newValue = this.newValue.trim()
      if(this.newName && this.newValue){
        this.addNew()
      }
    },

    exist(name){
      for(var i = 0; i < this.valueArray.length; i++){
        if(this.valueArray[i][0] === name){
          return true
        }
      }
      return false
    }
  },
  watch: {
    valueArray() {
      this.inputValue = {}
      for(var i = 0; i < this.valueArray.length; i++){
        let name = this.valueArray[i][0]
        let value = this.valueArray[i][1]
        this.inputValue[name] = value
      }
    }
  }

}
</script>

<style>
 .name-value-box{
    background: rgba(0,0,0, 0.15);
    display: flex;
    flex-flow: column;
    padding:10px;
  }

  .name-value-box .add-button{
    background: rgba(255,255,255, 0.15);
    width: 24px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 3px;
    margin: 1px;
    margin-top:3px;
    font-size: 16px;
    padding-bottom:3px;
    cursor: pointer;
  }

  .name-value-row{
    width: 100%;
    display: flex;
    flex-flow: row;
    height: 24px;
    align-items: center;
    font-size: 11px;
  }

  .name-value-row .name-input input, .name-value-row .value-input input{
    width: 100%;
    background: transparent;
    color:#bababa;
    outline: 0;
    border: 0;
  }

  .name-value-row .separator{
    width: 5px;
    display: flex;
    justify-content: center;
    flex-shrink: 0;
    color: #bababa;
  }

  .name-value-row .name-input{
    flex: 1;
  }

  .name-value-row .value-input{
    flex: 1.5;
    padding-left:3px;
  }

  .name-value-row .clear-button{
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 17px;
    background: rgba(255,255,255,0.1);
    border-radius: 3px;
    margin:1px;
    font-size: 12px;
    padding-bottom: 3px;
    cursor: pointer;
  }

  .name-value-row .button-placeholder{
    width: 20px;
    height: 20px;
    background: transparent;
  }

</style>

 

It also implements a border input control. This control has no potential to grow into a general control, so I will not introduce it. I am interested in looking at the source code directly. The name is RxBorderInput.

At present, there are two places to use it: one is the topic selection dialog, and the other is about dialog.

The two places share the common dialog box Modal. The value displayed in the control dialog box is passed in through the v-model, and the content of the dialog box is passed in through the Slot slot. The Modal code is as follows:

<template>
  <div v-if="inputValue" class="modal-mask" @click="inputValue = false">
    <div 
      class="modal"
      :style="{
        top : top,
        left : left,
        width :width,
        height : height,
      }" 
      @click="modalClick"
    >
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Modal',
  props:{
    value:{ default:'' }, 
    width:{ default: '800px'},
    height:{ default: 'calc(100vh - 80px)'},
    top:{default: '40px'},
    left:{default: 'calc(50% - 400px)'},
  },
  computed:{
    inputValue: {
      get:function() {
        return this.value;
      },
      set:function(val) {
        this.$emit('input', val);
      },
    },

  },
  data () {
    return {
    }
  },

  methods: {
    modalClick(event){
      event.stopPropagation()
    },
  },
}
</script>

<style>
.modal-mask{
  position: fixed;
  z-index: 9999;
  top:0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(20, 20, 20, 0.9);
}
.modal-mask .modal{
  position: fixed;
  top:50%;
  left:50%;
  background: #fff;
  box-shadow: 3px 3px 6px 3px rgba(0, 0, 0, 0.1); 
  transform: all 0.3s;
  display: flex;
  flex-flow: column;
  color: #474747;
}

</style>

 

You can also pass in dialog box width, height, location and other information through properties. Call the sample, which is also the code of about dialog box:

<template>
  <Modal v-model="inputValue" 
    width='600px'
    height='400px'
    top ="calc(50% - 200px)"
    left ="calc(50% - 300px)"
  >
    <div class="dialog-head">
      <div><i class="fas fa-question-circle"></i> {{$t('about.about-title')}} </div>
      <span 
        class="close-button"
        @click="inputValue = false"
      >×</span>
    </div>
    <div class="dialog-body about-content">
      //This program is the interface prototype of the second version of RXEditor.<br/>
      //Based on the VUE implementation, the code has been transferred to the RXeditor project.<br />
      //This prototype is no longer maintained, only for reference.<br />
      RXEditor It's an open source, visual, HTML Editing tools, based on Bootstrap Realization.<br />
      RXEditor Code address:<a href="https://github.com/vularsoft/rxeditor" target="_blank">https://github.com/vularsoft/rxeditor</a>
      //Demo address:<a href="https://vular.cn/rxeditor/" target="_blank" >https://vular.cn/rxeditor</a>
    </div>
    <div class="dialog-footer">
      <div class="dialog-button confirm-btn"
        @click="inputValue = false"
      >{{$t('about.close')}}</div>
    </div>
  </Modal>
</template>

<script>
import Modal from './Modal.vue'
export default {
  name: 'AboutDialog',
  components:{
    Modal,
  },
  props:{
    value:{ default:'' }, 
  },
  computed:{
    inputValue: {
      get:function() {
        return this.value;
      },
      set:function(val) {
        this.$emit('input', val);
      },
    },
  },
}
</script>

<style>
.about-content{
  display: flex;
  justify-content: center;
  align-items:flex-start;
  font-size:14px;
  line-height: 32px;
  padding-left: 40px;
}

.about-content a{
  color: #75b325;
}

.about-content a:hover{
  color: #60921e;
  text-decoration: underline;
}
</style>

 

So far, this is the completion of all practical projects. Thank you for your reading and attention. Next, we will apply these codes to RxEditor. Whether to share RxEditor kernel depends on personal energy and time in the future.

Please refer to Github for the full code of this demonstration project: https://github.com/vularsoft/studio-ui
If you have any questions, please leave a message.

Tags: Javascript github Attribute Vue Windows

Posted on Mon, 09 Mar 2020 20:02:19 -0700 by jgh84