likes
comments
collection
share

vue弹窗补坑帖

作者站长头像
站长
· 阅读数 19

摘要

先上效果

调用方式,这里我着重处理了确定时的非空验证及请求返回关闭的坑,一个表单的弹窗就这样轻松搞定,这里只是一个便捷的弹窗调用,结合我之前进行了两次尝试,最终我在formilyjs中找到了想要的答案

vue弹窗补坑帖

vue弹窗补坑帖 注意jsx写法上的差异, 如果你使用了El-Form的mode属性,在jsx扩展中默认会把v-mode写法与属性mode写法搞成一样的,区别请参考

vue弹窗补坑帖

效果图 vue弹窗补坑帖

回顾

最开始的封装,当然这块在去年经受住了考验,如果你看过去年发布的内容,自动化生成结合此项,给我带来了极大的便利 vue弹窗补坑帖

vue弹窗补坑帖 但Page的curd本来就是为了快速开发而来,弹窗这块的处理因为进度的原因没有深究,只是做了简单封装,勉强够用,后来觉得有点儿随意又尝试进行了 Promise 形式的封装,对JQ之前的$.Deferred的实现进行了一次探索,觉得思路挺新奇,但针对表单验证不通过的情况,没有进行后续的补充处理,之前实在太忙,一直没有时间去补这里的坑,因此这两天趁着时间把之前的留坑补充完善。

function generateDeferredPromise() {
return (() => {
  let resolve;
  let reject;
  let p = new Promise((res, rej) => {
    resolve = res;
    reject = rej;
  });

  return {
    promise: p,
    reject,
    resolve
  };
})();
}

之前主要用Promise的延迟处理去进行实现的,但如果验证不通过,此项延迟无法再生成新的,本质就是没有增加队列,处理的有点儿潦草

vue弹窗补坑帖 因此给予一些封装完善的补充。

主体内容

关键的实现代码如下


import {
    applyMiddleware
} from './comm'
import {  Loading } from 'element-ui'
import Vue, { Component, VNode, h } from 'vue'

import "./index.css"
/**
 * 前置判断逻辑
 */
const isType =(type) =>
  (obj) =>getType(obj) === `[object ${type}]`
export const isFn = (val)=> typeof val === 'function'
export const isArr = Array.isArray
export const isPlainObj = isType('Object')
export const isStr = isType('String')
export const isBool = isType('Boolean')
export const isNum = isType('Number')

export const stylePrefix = 'ProDrawer-element';

export const loading = async (
    loadingText = 'Loading...',
    processor= () => {}
) => {
    let loadingInstance = null
    let loading = setTimeout(() => {
        loadingInstance = Loading.service({
            text: loadingText,
            background: 'transparent',
        })
    }, 100)
    try {
        return await processor()
    } finally {
        loadingInstance?.close()
        clearTimeout(loading)
    }
}
export function isVnode(element) {
    return (
        element &&
        typeof element === 'object' &&
        'componentOptions' in element &&
        'context' in element &&
        element.tag !== undefined
    )
}
export function isVueOptions(options) {
    return (
        options &&
        (typeof options.template === 'string' ||
            typeof options.render === 'function')
    )
}
export function isValidElement(element) {
    return (
        isVueOptions(element) ||
        (element &&
            typeof element === 'object' &&
            'componentOptions' in element &&
            'context' in element &&
            element.tag !== undefined)
    ) // remove text node
}


export const resolveComponent = (
    child,
    props
  ) => {
    if (child) {
      if (typeof child === 'string' || typeof child === 'number') {
        return child
      } else if (typeof child === 'function') {
        return (child)(props)
      } else if (isVnode(child)) {
        return child
      } else {
        return h(toRaw(child), { props })
      }
    }
  
    return null
  }


export function FormDrawer(
    title,
    content
  ){
    const prefixCls = `${stylePrefix}-form-drawer`
    const env = {
      root: document.createElement('div'),
      form: null,
      promise: null,
      instance: null,
      openMiddlewares: [],
      confirmMiddlewares: [],
      cancelMiddlewares: [],
    }
  
    document.body.appendChild(env.root)
  
    const props = {title:title};
    const drawerProps = {
      ...props,
      onClosed: () => {
        props.onClosed?.()
        env.instance.$destroy()
        env.instance = null
        env.root?.parentNode?.removeChild(env.root)
        env.root = undefined
      },
    }
  
    const render = (visible = true, resolve, reject) => {
      if (!env.instance) {
        const ComponentConstructor = Vue.extend({
          props: ['drawerProps'],
          data() {
            return {
              visible: false,
            }
          },
          render() {
            const {
              onClose,
              onClosed,
              onOpen,
              onOpend,
              onOK,
              onCancel,
              title,
              footer,
              okText,
              cancelText,
              okButtonProps,
              cancelButtonProps,
              ...drawerProps
            } = this.drawerProps

            return (<el-drawer
            {...this.drawerProps}
            {...{ on: {
              'update:visible': (val) => {
                this.visible = val
              },
              close: () => {
                onClose?.()
              },

              closed: () => {
                onClosed?.()
              },
              open: () => {
                onOpen?.()
              },
              opend: () => {
                onOpend?.()
              },
            },}}
              title={title}
              class={prefixCls}
              size="80%"
              // modal="modal"
              visible={this.visible}
            >
              {resolveComponent(content,{ref:"form"})}
          
              
              {/* {h(content, { form: env.form}, {}) } */}
              <div  class="drawer__footer">
                <el-button type="primary" icon="el-icon-circle-check" on-click={ (e) => {
                                        onOK?.(e)
                                        resolve()
                                      }}>
                  确 定
                </el-button>
                <el-button icon="el-icon-circle-close" on-click={(e) => {
                                        onCancel?.(e)
                                        reject()
                                      }}>
                  取 消
                </el-button>
              </div>
            </el-drawer>);
          },
        })
        env.instance = new ComponentConstructor({
          propsData: {
            drawerProps,
          },
          //parent: getProtalContext(id as string | symbol),
        })
        env.instance.$mount(env.root)
      }
  
      env.instance.visible = visible
    }
  
    const formDrawer = {
      forOpen: (middleware) => {
        if (isFn(middleware)) {
          env.openMiddlewares.push(middleware)
        }
        return formDrawer
      },
      forConfirm: (middleware) => {
        if (isFn(middleware)) {
          env.confirmMiddlewares.push(middleware)
        }
        return formDrawer
      },
      forCancel: (middleware) => {
        if (isFn(middleware)) {
          env.cancelMiddlewares.push(middleware)
        }
        return formDrawer
      },
      open: (props) => {
        if (env.promise) return env.promise

        env.promise = new Promise(async (resolve, reject) => {
          try {
            props = await loading(drawerProps.loadingText, () =>
              applyMiddleware(props, env.openMiddlewares)
            )
          
          } catch (e) {
            reject(e)
          }
  

          render(
            true,
            async () => {

                //回调逻辑
                const callbck=async(val)=>{
                  const rec=  await applyMiddleware(env.form, env.confirmMiddlewares)
                  if(rec!=false){
                    resolve(val)
                  if (drawerProps.beforeClose) {
                    setTimeout(() => {
                      drawerProps.beforeClose(() => {
                        formDrawer.close()
                      })
                    })
                  } else {
                    formDrawer.close()
                  }
                }
                return true;
                }
                //检索路径下是否有form表单
            var form=  env.instance.$children[0].$slots.default.find(x=>x.componentOptions&&x.componentOptions.tag=="ProForm");
            if(form){
              env.form = form.componentInstance
            }
              if(env.form){
                env.form.validate(async (valid) => {
                  if(valid){
                const res=  await  callbck(env.form.model)
                 return res;
                  }else{
                    //验证不通过
                    return false;
                  }
              
                })
              // .catch(() => {})
              }else{
                //没有检索到form时,正常处理
                callbck()
              }
             
            },
            async () => {
              await loading(drawerProps.loadingText, () =>
                applyMiddleware(env.form, env.cancelMiddlewares)
              )
  
              if (drawerProps.beforeClose) {
                drawerProps.beforeClose(() => {
                  formDrawer.close()
                })
              } else {
                formDrawer.close()
              }
            }
          )
        })
        return env.promise
      },
      close: () => {
        if (!env.root) return
        render(false)
      },
    }
    return formDrawer
  }
export default FormDrawer

此处先重点说一下回调部分,处理了表单验证非空的处理,因为Form表单的封装本身又一个validate回调验证,而我们使用的弹窗又存在了表单和非表单两种情况,因此在此处做了重点的区分,若找到表单,则执行相关的表单验证,若没有,则正常进行逻辑处理,之所以有此项是因为去年在实际使用过程中,经常遇到非表单的弹窗情况,封装的略显尴尬、 因为本身我这边用的是自己封装的ProForm,所以找的是该组件,如果是自用可改为查找Form vue弹窗补坑帖

最好再重点说一下此项Promise实现的机制

vue弹窗补坑帖 可以看到,这里的回调事件根Emitter那种事件机制差不多,通过点击追加Promise处理的方式,解决了之前回调处理的坑。 vue弹窗补坑帖

总结

之前留的没处理完的内容终于补齐了,算是有了初步的交代,因为之前的贴浏览量还行,怕影响判断,因此在此处不全。