vue3+ElementUI picture material management component

Foreword

Many backend management systems use a large number of images. In order to optimize the problem of repeated uploads, most of them will first upload an image for repeated use. Use the ID of the material to select the reference. Today I will share a simple small component that I made. How to build this component step by step. Because everyone has differences in practice, I will focus on the ideas and finally attach the source code (pulled from my project, so the interface and other things vary from person to person). , it cannot be used directly by copying it, it can be used after subtraction)

Technology stack: vue3 combined api + Element-Plus (vue3 version of elemetUI)

Interface: Adding, deleting, and modifying resource categories, uploading, modifying, and deleting resources

Page structure analysis

Normal page

 <!-- Use direct page routing -->
  <div>
    <el-card shadow="hover" :body-style="{ padding: '20px' }">
      <chooseResource></chooseResource>
    </el-card>
  </div>

First-level structure

the whole frame

This component is divided into a picture selector parent component and a classification sub-component. The reason why classification is separated into a sub-component is to facilitate the operation of adding and deleting categories on another page (deleting top-level nodes is not allowed in the picture selector)

This can be divided into two sides of the structure

 <el-row :gutter="20">
      <!-- Category on the left -->
      <el-col v-bind="grid">
         <!-- Classification subcomponent -->
      </el-col>

      <!-- Image operations on the right -->
      <el-col v-bind="grid2">
            <!-- Detailed operation of the picture on the right -->
        </el-col>
 </el-row>

Secondary structure

The classification group on the left is an example of the official website. It is very simple with the custom editing menu at the end.

Three-layer structure on the right: first layer operation menu; middle layer: picture display; not fixed height; use flex layout; flex-wrap to change lines

Next level paging

Each picture consists of el-image, input box and modification icon

Pop-up window structure

<!-- Select the pop-up window method -->
  <div>
    <el-dialog
      title="Select image"
      v-model="resourceVisible"
      show-close
      close="setResourceVisible(false)"
      width="70%">
      <chooseResource v-if="resourceVisible" :visible="resourceVisible" :choose_type="choose_type" @chooseSingle="changeSingle" @chooseList="changeList" />
      <template #footer>
        <span>
          <el-button @click="setResourceVisible(false)">Cancel</el-button>
        </span>
      </template>
    </el-dialog>
  </div>

Roughly the same as the normal page structure, except that the operation menu determines whether selection is being made based on the incoming choose_type to display the “Select Image” button.

Practical operation

Category subcomponent

The classification subcomponent on the left uses simple addition, deletion, modification and search

<el-tree
  ref="treeRef"
  :data="dataSource"
  node-key="value"
  :filter-node-method="filterNode"
  default-expand-all
  :expand-on-click-node="false"
  :highlight-current="true"
  >
    <template #default="{ node, data }">
      <div
        class="custom-tree-node"
        @click.stop="handleNodeClick(data)"
      >
        <div>
          <span>{<!-- -->{ node.label }}</span>
        </div>
        <div class="flexRowAlign" v-if="data.value" style="font-size:1.2rem">
          <!-- Add -->
          <el-icon ><Plus /></el-icon>

          <!-- Modify -->
          <el-icon><EditPen /></el-icon>

          <!-- Delete -->
          <el-icon><Delete /></el-icon>
        </div>
      </div>
    </template>
 </el-tree>
<script setup lang="ts">
    //Throw out the category ID for image upload and modification management
    const handleNodeClick=(data)=>{
      console.log('Select category',data)
     emit('selectCG',data.value)
    }

</script>

The Add component and Update component in the source code both assist in adding and modifying categories. You can change the interface on the outer layer and modify the displayed and submitted data on the inner layer.

Picture selection parent component

Parameters passed in

Choose mode: choose_type

single single selection picture

list multiple selection pictures

Callback

@chooseSingle: Select a single picture and return the id and url of the picture

@chooseList: Returns the image id array and url array

Select image operation

  • v-for generates a picture list imgList, and uses isSelect in the item to determine whether it is selected. Each time a picture is selected, it is saved in the array. If it is already selected and selected again, it will be deselected and removed from the array.
//Select the picture
const imgIDList=ref([])
const imgSrcList=ref([])
const imgInfoList=ref([])
const chooseImg=(item)=>{
  if(!item.isSelect){//Determine whether multiple selections can be made by choose_type
    if(imgIDList.value.length==1 & amp; & amp;props.choose_type=='single'){
      ElMessage.warning('Only one picture can be selected')
      return
    }
    item.isSelect = true;
    imgIDList.value.push(item.resource_id)
    imgSrcList.value.push(item.resource_url)
    imgInfoList.value.push(item)
  }else{
    item.isSelect = false;
    const index=imgIDList.value.indexOf(item.resource_id)//Get the index
    if(index==-1)return
    imgIDList.value.splice(index,1)
    imgSrcList.value.splice(index,1)
    imgInfoList.value.splice(index,1)
  }
  console.log('picture list',imgIDList.value,imgSrcList.value)
  // chosenImgList.value.push(row.resource_id)
}

Change name operation

Since each picture is separate, editId is needed to record the operation of changing the name. Use the item’s id and editId to determine whether to display the input modification box.

<!-- Picture list -->
<div class="list flexRow">
  <template v-if="imgList.length!=0">
    <div class="list_item flexCol"
      v-for="(item,index) of imgList"
      :key="index">
        <el-image
            class="list_img"
          :src="item.resource_url"
          @click.stop="chooseImg(item)"
          :class="item.isSelect?'list_img_choose':''"
        />
        <!-- Input box -->
        <div class="flexRowAlign">
          <el-input v-model="editName" v-if="editId === item.resource_id"></el-input>
          <div class="itemName flexRowCenter" style="width: 80%;" v-else>{<!-- -->{item.resource_title}}</div>
          <!-- Modify -->
          <el-icon class="mx-1 hoverLight"
          @click.stop="changeName(item)"><EditPen /></el-icon>
        </div>
    </div>
  </template>
  <template v-else>
    <span>There are no pictures in this category</span>
  </template>
</div>
const editId=ref(0)
const editName=ref('')
const changeName=(data)=>{
  console.log('modified data',data)
  if(editId.value==data.resource_id){
    if(data.resource_title!=editName.value){
      if(!editName){
        ElMessage.warning('Please enter the picture name first')
        return
      }
      let obj=Object.assign({},data)
      obj.resource_title=editName.value
      updateResourceApi(obj).then(res=>{
        if(res.code==200){
          ElMessage.success('Modification successful')
        }
        data.resource_title=editName.value
      }).catch(err=>{
        ElMessage.error(err.message)
      }).finally(()=>{//Reset the selected id
        editId.value=0
        editName.value=''
      })
    }else{//Cancel modification
      editId.value=0
      editName.value=''
    }
  }else{//Select the modified picture
    editId.value=data.resource_id
    editName.value=data.resource_title
  
  }
}

When using pictures, emit triggers the callback and returns the data.

//Use pictures
const emit=defineEmits(['chooseSingle','chooseList'])

const usePhoto=()=>{
  if(props.choose_type=='single'){
    emit('chooseSingle',imgIDList.value[0],imgSrcList.value[0])
  }else if(props.choose_type=='list'){
    emit('chooseList',imgIDList.value,imgSrcList.value)
  }
}

Other operations

For uploading, deleting, and moving, just connect to your own interface. For moving and deleting, use an array loop to connect to the interface.

More

You can optimize loading speed by selecting images by loading thumbnails, as well as various style optimizations.

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Vue entry skill treevue3 basics (JS)Vue3 component communication 39441 people are learning the system