Vue 사용자 정의 render 통합 프로젝트 그룹 탄 상자 기능

1.본문 수확
pick
2.왜 탄 틀 을 통일 적 으로 포장 해 야 합 니까?
어떻게 포장 해 야 합 니까?
일반적인 탄창 을 예 로 들 어 쓰 는 방법.보통 한 페이지 를 팝 업 하려 면 페이지normalDialog.vue패키지dialogBody.vue(탄 상자 주체)를 만들어 야 한 다 는 것 을 알 수 있 습 니 다.parent.vueflag 제어 탄 상 자 를 설정 하여 숨 기 고normalDialog.vue닫 을 때parent.vue대응flag을 설정 해 야 합 니 다.단점:절차 가 복잡 하고 배치 가 번 거 롭 고 유연성 이 없 으 며 스타일 이 일치 하지 않 고 매개 변수 전달 이 번 거 로 운 등.한 항목 의 탄창 이 많 을 때 단점 이 더욱 뚜렷 해 지고 대량의isXxxDialogShow,대량의vue파일 이 있 을 것 이다.따라서 프로젝트 팀 은 탄 상 자 를 간단하게 설정 할 수 있 는API이 급히 필요 하 다.
1.일반적인 탄 틀 표기 법dialoBody.vue(탄 틀 주체)은 이곳 에서Composition API의 표기 법 을 사용한다.간단 한 페이지 만 만 만 들 었 습 니 다.검증 을 포함 하고 데 이 터 를 저장 하 는 일반적인 논 리 를 추출 합 니 다.

<template>
 <div class="dialog-body">
 <div class="item">
 <div>  </div>
 <el-input v-model="name"></el-input>
 </div>
 <div class="item">
 <el-radio-group v-model="attention">
 <el-radio label="   "></el-radio>
 <el-radio label="    "></el-radio>
 </el-radio-group>
 </div>
 <div class="item">
 <el-radio-group v-model="like">
 <el-radio label="   "></el-radio>
 <el-radio label="    "></el-radio>
 </el-radio-group>
 </div>
 </div>
</template>

<script>
import { reactive, toRefs } from '@vue/composition-api'
import pick from 'lodash/pick'
import { Message } from 'element-ui'

export default {
 props: {
 defaultName: String,
 },
 setup(props, ctx) {
 const ATTENTIONED = '   '
 const LIKED = '   '
 const state = reactive({
 name: props.defaultName, //   
 attention: '   ', //   
 like: '   ', //   
 })
 /*************************************************************
 *        
 *     :
 * 1.   methods  
 * 2.            ,           
 *     methods.onXxx = ()=>{ //      }     
 *   1: onXxx                  
 *   2:       ...methods    setup    
 *   3:        ,           ,      ;
 *                  ;
 *        API handleXxx(methods,state),     methods;
 *   Vue2    ,        .
 */
 const methods = {}
 //     
 methods.onNameBlur = () => {}

 // ************************      API ************************
 const apiMethods = {
 //      
 isCanSave() {
 if (state.attention !== ATTENTIONED || state.like !== LIKED) {
  Message.error('       ,    ,  ')
  return false
 }
 return true
 },
 //       
 getSaveData() {
 // ******* lodash pick         
 return pick(state, ['name', 'attention', 'like'])
 },
 }
 return {
 ...toRefs(state),
 ...methods,
 apiMethods,
 }
 },
}
</script>

<style lang="less">
.dialog-body {
 width: 100%;
 height: 100px;
}
</style>
2.normalDialog.vue소포 탄 틀 주체dialoBody.vue

<template>
 <el-dialog 
 title="  ,  ,    " 
 :visible.sync="isShow" 
 width="30%" 
 :before-close="onClose"
 >
 <dialog-body default-name="       " ref="inner"></dialog-body>
 <span slot="footer" class="dialog-footer">
 <el-button @click="onClose">   </el-button>
 <el-button type="primary" @click="onOK">   </el-button>
 </span>
 </el-dialog>
</template>

<script>
import dialogBody from './dialogBody.vue'
export default {
 components: {
 dialogBody,
 },
 data() {
 return {
 isShow: true,
 }
 },
 methods: {
 onClose() {
 // ***********   parent.vue ********
 this.$parent.isNormalDialogShow = false
 },
 // *******        ********
 onOK() {
 const inner = this.$refs.inner
 //         
 if (inner.apiMethods.isCanSave()) {
 //       
 const postData = inner.apiMethods.getSaveData()
 console.log('>>>>> postData >>>>>', postData)
 //          
 this.onClose()
 }
 },
 },
}
</script>
parent.vue

// html   
<normal-dialog v-if="isNormalDialogShow" />

// Js  
data(){
	isNormalDialogShow:false
}
methods:{
 onDialogShow(){ // ******      *****
 this.isNormalDialogShow = true
 }
}
2.어떻게 포장 할 지
2.1 API 요구 사항:

isXxxDialogShow
el-dialog
2.2 이상 API:

import dialogBody from './dialogBody.vue'
const dialog = new JSDialog({
 comonent: dialogBody, 
 dialogOpts: { //      
 title: 'JSDialog       ',
 width: '400px'
 },
 props: {
 defaultName: 'JSDialog     ',
 },
 onOK() {
 const inner = dialog.getInner() //    dialogBody   
 //     
 if (inner.apiMethods.isCanSave()) {
 //       
 const postData = inner.apiMethods.getSaveData()
 console.log('>>>>> postData >>>>>', postData)
 //     
 dialog.close()
 }
 },
 onCancel() {
 dialog.close() //     
 },
})
dialog.show() //     
3.어떻게 포장 합 니까?
동적 제어 디 스 플레이 내용,머 릿 속 에 떠 오 르 는 세 가지 방안:카드 슬롯,동적 구성 요소 와 render.다음은 동적 탄 틀 장면 에서 세 가지 방안 을 간단하게 비교 합 니 다.
  • slot(슬롯)는 el-dialog 원리 와 유사 하 며,한 층 만 더 봉 인 했 을 뿐,normalDialog.vue 파일 을 적 게 정의 했다.단점:호출 이 복잡 하고 원활 하지 않다.닫 힌 프로 세 스 를 제어 하기 가 쉽 지 않 습 니 다.template 에서 만 정의 할 수 있 습 니 다.
  • component(동적 구성 요소),comonDialog.vue 를 만 들 고 App.vue 에 통일 적 으로 걸 어 놓 습 니 다.동적 으로 탄 상자 주 체 를 전환 합 니 다.comonDialog.vue 감청 componentId 변 화 를 이용 하여 탄 상자 주 체 를 전환 합 니 다.단점:모든 탄 상자 의 주체 구성 요 소 를 comonDialog.vue 페이지 의 components 에 미리 등록 해 야 합 니 다.vuex 에 의존 하여 침입 성 이 비교적 강하 다.순수 js 파일 은 vuex 를 통 해 탄 상 자 를 꺼 내 는 것 이 상대 적 으로 복잡 하고 유연 하지 않 습 니 다.
  • render 를 다시 씁 니 다.render 는 Vue 가 휠 개발 자 에 게 열 린 뒷문 입 니 다.동적 탄 상 자 는 독립 된 기능 모듈 로 내부 에 new Vue 를 통 해 render 제어 렌 더 링 내용 을 다시 쓸 수 있 습 니 다.독립 Vue 인 스 턴 스 를 미리 만 들 수 있 고 모든 위치 에서 탄 상 자 를 제어 할 수 있 으 며 유연 하고 뚜렷 합 니 다.단점:없 음
  • 1.전체 코드
    먼저 코드 를 전체적으로 미리 보고 다음 에 세분 화하 여 설명 하 겠 습 니 다.
    
    import Vue from 'vue'
    import merge from 'lodash/merge'
    import orderBy from 'lodash/orderBy'
    
    //         
    function btnBuilder(options) {
     const defaultBtn = {
     text: '  ', //     
     clickFn: null, //     
     type: 'default', //   
     isHide: false, //     
     order: 2 //   
     }
     return { ...defaultBtn, ...options }
    }
    
    export default class JSDialog {
     constructor(originOptions) {
     this.options = {}
     this.vm = null
     this._mergeOptions(originOptions)
     this._initVm()
     }
     //     
     _mergeOptions(originOptions) {
     const defaultOptions = {
     component: '', //     vue  
     //    el-dialog  api     ,   aaaBbbCcc
     dialogOpts: {
     width: '40%',
     title: '    '
     },
     //       vue     
     props: {},
     //       
     onOK: () => {
     console.log('JSDialog default OK'), this.close()
     },
     //       
     onCancel: () => {
     console.log('JSDialog default cancel'), this.close()
     },
     footer: {
     ok: btnBuilder({
      text: '  ',
      type: 'primary',
      order: 0
     }),
     cancel: btnBuilder({
      text: '  ',
      order: 1
     })
     }
     }
     //      this.options
     merge(this.options, defaultOptions, originOptions)
     const footer = this.options.footer
     Object.entries(footer).forEach(([key, btnOptions]) => {
     //          
     if (['ok', 'cancel'].includes(key)) {
     const clickFn = key === 'ok' ? this.options.onOK : this.options.onCancel
     //          : footer   clickFn > options   onOK onCancel
     btnOptions.clickFn = btnOptions.clickFn || clickFn
     } else {
     //     
     //     
     footer[key] = btnBuilder(btnOptions)
     }
     })
     }
     _initVm() {
     const options = this.options
     const beforeClose = this.options.footer.cancel.clickFn //            
     this.vm = new Vue({
     data() {
     return {
      //         
      footer: options.footer, //     
      visible: false //        
     }
     },
     methods: {
     show() {
      //     
      this.visible = true
     },
     close() {
      //     
      this.visible = false
     },
     clearVm() {
      //   vm  
      this.$destroy()
     }
     },
     mounted() {
     //    body 
     document.body.appendChild(this.$el)
     },
     destroyed() {
     //  body   
     document.body.removeChild(this.$el)
     },
     render(createElement) {
     //     
     const inner = createElement(options.component, {
      props: options.props, //     
      ref: 'inner' //   
     })
     //         
     const showBtns = Object.values(this.footer).filter(btn => !btn.isHide)
     //       
     const sortBtns = orderBy(showBtns, ['order'], ['desc'])
     //      jsx   
     const footer = (
      <div slot="footer">
      {sortBtns.map(btn => (
      <el-button type={btn.type} onClick={btn.clickFn}>
      {btn.text}
      </el-button>
      ))}
      </div>
     )
     //     
     const elDialog = createElement(
      'el-dialog',
      {
      // el-dialog    
      props: {
      ...options.dialogOpts,
      visible: this.visible,
      beforeClose
      },
      // ****    ,visible  false ,el-dialog      *****
      on: {
      closed: this.clearVm
      },
      ref: 'elDialog'
      },
      //     :       
      [inner, footer]
     )
     return elDialog
     }
     }).$mount()
     }
     //   API
     //     
     close() {
     this.vm.close()
     }
     //     
     show() {
     this.vm.show()
     }
     //         ,         
     getInner() {
     return this.vm.$refs.inner
     }
    }
    2.매개 변수 통합
    API 요구 사항 을 수행 하려 면 호출 이 간단 하고 전송 이 간편 하 며 확장 가능 한 제어 탄 상자 스타일 을 사용 해 야 합 니 다.매개 변수 통합 은 원가 가 가장 적은 실현 방안 으로 TS 에 맞 추 는 효과 가 더욱 좋다.기본 매개 변 수 를 정의 합 니 다.lodash 의 merge 를 통 해 심층 속성 을 합 칩 니 다.매개 변수 통합 을 통 해 사용자 정의 footer 단 추 를 누 르 고 텍스트,스타일,순서 와 리 셋 을 제어 할 수 있 습 니 다.
    
    //     
    _mergeOptions(originOptions) {
     const defaultOptions = {
     component: '', //     vue  
     //    el-dialog  api     ,   aaaBbbCcc
     dialogOpts: {
     width: '40%',
     title: '    '
     },
     //       vue     
     props: {},
     //       
     onOK: () => {
     console.log('JSDialog default OK'), this.close()
     },
     //       
     onCancel: () => {
     console.log('JSDialog default cancel'), this.close()
     },
     footer: {
     ok: btnBuilder({
     text: '  ',
     type: 'primary',
     order: 0
     }),
     cancel: btnBuilder({
     text: '  ',
     order: 1
     })
     }
     }
     //      this.options
     merge(this.options, defaultOptions, originOptions)
     const footer = this.options.footer
     Object.entries(footer).forEach(([key, btnOptions]) => {
     //          
     if (['ok', 'cancel'].includes(key)) {
     const clickFn = key === 'ok' ? this.options.onOK : this.options.onCancel
     //          : footer   clickFn > options   onOK onCancel
     btnOptions.clickFn = btnOptions.clickFn || clickFn
     } else { //     
     //     
     footer[key] = btnBuilder(btnOptions)
     }
     })
    }
    3.렌 더 링 함수
    렌 더 링 함수&JSX 공식 문서 에서 render 에 대한 설명 을 추출 합 니 다.Vue 는 대부분의 경우 템 플 릿 을 사용 하여 HTML 을 만 드 는 것 을 추천 합 니 다.그러나 일부 장면 에서 자 바스 크 립 트 의 완전한 프로 그래 밍 능력 이 필요 하 다.이 때 는 템 플 릿 보다 컴 파일 러 에 더 가 까 운 렌 더 링 함 수 를 사용 할 수 있 습 니 다.공식 문 서 는 렌 더 링 함수 에 대한 쓰기,매개 변수,JSX 쓰기 에 대한 소개 가 상세 하 므 로 더 이상 군말 하지 않 습 니 다.다음 코드 는 최신 vue-cli 생 성 프로젝트 에서 실 행 됩 니 다.JS 매개 변수 생 성 요소 와 JSX 생 성 요소 두 가지 쓰기 방법 을 시도 하 였 습 니 다.
    
    render(createElement) {
     //     
     const inner = createElement(options.component, {
     props: options.props, //     
     ref: 'inner' //   
     })
     //         
     const showBtns = Object.values(this.footer).filter(btn => !btn.isHide)
     //       
     const sortBtns = orderBy(showBtns, ['order'], ['desc'])
     //      jsx   
     const footer = (
     <div slot="footer">
     {sortBtns.map(btn => (
     <el-button type={btn.type} onClick={btn.clickFn}>
      {btn.text}
     </el-button>
     ))}
     </div>
     )
     //     
     const elDialog = createElement(
     'el-dialog',
     {
     // el-dialog    
     props: {
     ...options.dialogOpts,
     visible: this.visible
     },
     on: {
     closed: this.clearVm
     },
     ref: 'elDialog'
     },
     //     :       
     [inner, footer]
     )
     return elDialog
    }
    4.봉인 API
    일시 적 으로 세 개의 API 만 봉 인 했 고 서로 다른 장면 에 따라 API 를 확장 할 수 있 습 니 다.예 를 들 어 탄 상자 가 숨 김,탄 상자 새로 고침 등 입 니 다.
    show(),탄 상자 표시
    디 스 플레이 는 주로 el-dialog 의 visible 을 true 로 수정 하여 body 에 마 운 트 된 탄 상자 디 스 플레이 를 제어 합 니 다.
    
    show() {
     this.vm.show()
    }
    close(),탄 상자 닫 기
    처리 프로 세 스 닫 기:el-dialog 의 visible 을 false 로 수정 합 니 다.el-dialog 의 closed 이벤트 실행 하기;clearVm 실행 하기;vm 의$destroy()실행 하기;destroyed()리 셋 에서$el 을 body 에서 제거 합 니 다.
    
    close() {
     this.vm.close()
    }
    getInner(),탄 상자 의 주체 인 스 턴 스 를 가 져 옵 니 다.인 스 턴 스 에 접근 하 는 방법,제어 단추 프로 세 스 에 사용 할 수 있 습 니 다.
    
    getInner() {
     return this.vm.$refs.inner
    }
    어떻게 사용
    1.가장 간단 한 장면,페이지 만 설정
    단추 이벤트 리 셋 은 기본 리 셋 을 사용 합 니 다.확인 과 취소 단 추 를 누 르 면 탄 상 자 를 닫 을 수 있 습 니 다.
    
    import dialogBody from './renderJsx/dialogBody'
    const dialog = new JSDialog({
     component: dialogBody,
    })
    dialog.show() //     
    효 과 는 다음 과 같 습 니 다:
     
    2.탄 틀 스타일 제어 및 절차 확정
    el-dialog 가 지원 하 는 설정 항목 을 사용자 정의 할 수 있 습 니 다.Dialog 대화 상 자 를 보십시오.예 를 들 어 title,custom Class.customeClass 를 통 해 프로젝트 내 탄 틀 의 스타일 을 통일 적 으로 제어 할 수 있 습 니 다.취소 버튼 코드 리 셋 을 제어 할 수 있 습 니 다.
    
    import dialogBody from './renderJsx/dialogBody'
    const dialog = new JSDialog({
     component: dialogBody,
     dialogOpts: {
     title: '  ,     ',
     customClass:'js-dialog'
     },
     props: {
     defaultName: 'JSDialog     '
     },
     onOK() {
     const inner = dialog.getInner() //    dialogBody   
     //     
     if (inner.apiMethods.isCanSave()) {
      //       
      const postData = inner.apiMethods.getSaveData()
      console.log('>>>>> postData >>>>>', postData)
      //     
      dialog.close()
     }
     },
     onCancel() {
     dialog.close() //     
     }
    })
    효 과 는 다음 과 같 습 니 다:
     
    3.사용자 정의 footer
    사용자 정의 단 추 를 누 르 면 리 셋,스타일,순서,표시 와 숨 김 을 제어 할 수 있 습 니 다.
    
    import dialogBody from './renderJsx/dialogBody'
    const dialog = new JSDialog({
     component: dialogBody,
     footer: {
     ok: { //       
      text: '  '
     },
     cancel: { //       
      isHide: true
     },
     add: { //     
      text: '   ',
      clickFn() {
      dialog.close()
      },
      order: -1 //       ,order       
     },
     add2: {
      text: '    2',
      clickFn() {
      dialog.close()
      },
      order: 3
     }
     }
    })
    dialog.show() //     
    효 과 는 다음 과 같 습 니 다:

    총결산
    Vue 사용자 정의 render 통합 프로젝트 그룹 탄 상자 기능 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 Vue 사용자 정의 render 프로젝트 그룹 탄 상자 내용 은 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 지원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기