中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

vue?usePop彈窗控制器如何實現

發布時間:2023-04-17 15:31:47 來源:億速云 閱讀:149 作者:iii 欄目:開發技術

這篇文章主要介紹“vue usePop彈窗控制器如何實現”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“vue usePop彈窗控制器如何實現”文章能幫助大家解決問題。

    當UI庫彈窗無法滿足自定義需求時,需要我們自己開發簡單的彈窗組件。彈窗組件與普通業務組件開發沒有太大區別,重點在多彈窗之間的關系控制。例如: 彈窗1,彈窗2 由于觸發時機不同,需要不同的層疊關系,后觸發的始終在最前端,點擊彈窗頭改變層疊關系。 單一彈窗多處調用等。這里封裝基礎的管理鉤子,簡化這些問題的處理。

    功能目標

    • 單例,多例彈窗

    • 可配置彈窗自定義參數

    • 可接收彈窗自定義事件

    • 層級控制

    • 自定義定位

    該鉤子的目的主要為了處理彈窗之間的控制關系,具體如何渲染交由調用方

    快速使用

    // 主容器
    import { usePopContainer, buildDefaultPopBind, position } from '@/hooks/usePop'
    import UserInfoPop form './UserInfoPop.vue'
    // 快捷工具,將內部鉤子通過依賴注入,共享給子組件
    const [popMap, popTools] = usePopContainer()
    const popBind = buildDefaultPopBind(popTools, popTools.componentsCache)
    
    
    const userPop = popBind('userInfo', UserInfoPop, {
      position: { // 組件定位
        top: 200
      },
      userId: 'xxx', // 組件porps
      @close(){ // 組件事件
        console.log('close')
      }
    })
    
    
    // 調用
    userPop.open()
    setTimeout(userPop.close, 1000 * 3)
    
    
    
    
    // template
    <template v-for="(pop, popId) of popMap">
      // 渲染彈窗列表
       <component
         :is="pop.component"
         :key="popId"
         v-bind="pop.props"
         v-on="pop.on"
       >
       </component>
    </template>

    多處調用

    同一彈窗,多實例 add

    // 容器注冊
    const [popMap, popTools] = usePopContainer()
    // 新增彈窗1
    popTools.add(popId1, {
       component: UserPop, // 彈窗組件
       useId: 'xxx', // 彈窗Props
       '@close': () => { ... } //彈窗事件
    })
    
    
    // 新增彈窗2
    popTools.add(popId2, {
       component: UserPop, // 彈窗組件
       useId: 'xxx', // 彈窗Props
       '@close': () => { ... } //彈窗事件
    })
    // 覆蓋彈窗1
    // popId 為彈窗唯一標識, 如果popId相同,組件配置將被替換
    popTools.add(popId1, {
       component: UserPop, // 彈窗組件
       useId: 'yyy', // 彈窗Props
       '@close': () => { ... } //彈窗事件
    })

    所有彈窗都通過popId,查找or判斷唯一性。

    配置參數:以@ 開頭的都將組為組件的事件被綁定, 除了 @[事件名] component 其他屬性都將作為props,包括 style 等屬性

    移除 remove

    const [popMap, popTools] = usePopContainer()
    popTools.add(popId, {
       component: UserPop, // 彈窗組件
       useId: 'xxx', // 彈窗Props
       '@close': () => { ... } //彈窗事件
    })
    // 移除
    popTools.remove(popId)

    替換 replace

    // 主容器
    const [popMap, popTools] = usePopContainer()
    
    
    // 子組件A
    popTools.replace(popId, {
       component: UserPop, // 彈窗組件
       useId: 'xxx', // 彈窗Props
       '@close': () => { ... } //彈窗事件
    })
    
    
    // 子組件B
    popTools.replace(popId, {
       component: UserPop, // 彈窗組件
       useId: 'xxx', // 彈窗Props
       '@close': () => { ... } //彈窗事件
    })

    當有多處調用同一彈窗,而只需要最新的觸發彈窗時,使用 replace. 該方法其實就是 remove add 的包裝方法

    更新 update

    const [popMap, popTools] = usePopContainer()
    popTools.replace(popId, {
       component: UserPop, // 彈窗組件
       useId: 'xxx', // 彈窗Props
       '@close': () => { ... } //彈窗事件
    })
    // 更新參數
    popTools.update(popId, {
       useId: 'yyy'
    
    })

    通過popId 查詢彈窗,將新傳入的參數與原配置做合并

    預注冊 componentsCache

    const [popMap, popTools] = usePopContainer()
    // 局部預先注冊
    // 注冊只是預先緩存
    popTools.componentsCache.add(popId, {
      component: UserPop,
      id: 'xxx',
      '@cloes': () => {...}
    })
    // 調用
    popTools.add(popId, { component: popId })
    // of
    popTools.replace(popId, { component: popId })
    // add將從componentsCache查詢預注冊配置

    除了局部緩存, componentsCache, 模塊還導出了 globalComponentsCache 全局公共緩存。

    依賴注入

    為了方便父子組件調用,提供了usePopContainer usePopChildren 方法,

    // 父組件
    const [popMap, popTools] = usePopContainer()
    // 子組件
    const { popTools } = usePopChildren()
    popTools.add({
       ...
    })

    函數接收依賴注入標識, 為傳入標識時,使用默認標識

    usePop 工具函數

    • add(popId, options) 創建彈窗

    • update(popId, options) 更新彈窗配置(定位, props,events)

    • remove(popId) 移除彈窗

    • replace(popId, options) 替換,如果多處調用同一彈窗,希望只顯示唯一同類彈窗時,

     使用該函數,多個彈窗公用相同的popId

    • clearAllPop() 清空所有彈窗

    • updateIndex(popId) 更新彈窗層級

    • downIndex(popId) 層級下降一級

    • topIndex(popId) 層級置頂

    core 實現

    import { shallowRef, unref, provide, inject } from 'vue'
    import { merge } from 'lodash-es'
    import { splitProps, counter } from './utils'
    
    
    export const DEFAULT_POP_SIGN = 'DEFAULT_POP_SIGN'
    
    
    // 全局層級累加器
    export const counterStore = counter()
    
    
    /**
     * 預先pop注冊表
     * @summary
     * 便捷多處pop調用, 調用pop顯示方法時,
     * 直接通過名稱查詢對應的組件預設
     * 將調用與事件配置解耦
     * @returns
     */
    function componentsRegistry () {
      let componentsCache = new Map([])
    
    
      function has (componentName) {
        return componentsCache.has(componentName)
      }
    
    
      function add (componentName, options) {
        componentsCache.set(componentName, options)
      }
    
    
      function remove (componentName) {
        if (has(componentName)) {
          componentsCache.delete(componentName)
        }
      }
    
    
      function fined (componentName) {
        return componentsCache.get(componentName)
      }
    
    
      function clear () {
        componentsCache = new Map([])
      }
    
    
      function getComponents () {
        return [...componentsCache.values()]
      }
    
    
      function getComponentNames () {
        return [...componentsCache.keys()]
      }
    
    
      return {
        has,
        add,
        remove,
        fined,
        clear,
        getComponents,
        getComponentNames
      }
    }
    
    
    export const globalComponentsCache = componentsRegistry()
    
    
    /**
     * 彈窗控制器
     * @summary
     * 提供多彈窗控制邏輯:
     * 1. 單例, 多例: 通過不同的 popId 控制彈窗實例的個數
     * 2. 參數接收: open接收初始傳給pop的事件和參數配置, update 提供參數更新
     * 3. 事件回調: options 配置屬性 { @[事件名稱]:事件回調 } 將作為事件綁定到pop上
     * 4. 動態疊加: 內部將為組件配置 zIndex, 組件內需要自定義接收該參數,判斷如何處理層疊關系
     * 5. 定位: 定位需要彈窗組件接收 position props 內部綁定樣式
     *
     * @tips
     *  這里定位為了兼容 useMove做了接口調整,原接口直接輸出定位樣式。當前出position屬性,
     *  組件內需要自行處理定位樣式。 這里存在 style 合并和透傳的的問題, 通過透傳的style與
     *  props 內定義的style將分開處理, 即最終的結果時兩個style的集合, 且透傳的style優先級高于
     *  prop。所以如果直出定位樣式,通過透傳綁定給彈窗組件,后續的useMove拖拽樣式將始終被透傳樣式覆蓋
     *
     * @api
     * - add(popId, options) 創建彈窗
     * - update(popId, options) 更新彈窗配置(定位, props,events)
     * - remove(popId) 移除彈窗
     * - replace(popId, options) 替換,如果多處調用同一彈窗,希望只顯示唯一同類彈窗時,
     *  使用該函數,多個彈窗公用相同的popId
     * - clearAllPop() 清空所有彈窗
     * - updateIndex(popId) 更新彈窗層級
     * - downIndex(popId) 層級下降一級
     * - topIndex(popId) 層級置頂
     *
     * @example01 - 一般使用
     *
     * const [
     *  pops,
     *  popTools
     * ]  = usePop()
     *
     *
     * // 容器組件
     * <component
     *  v-for='(pop, popId) of pops'
     *  :is='pop'
     *  v-bind='pop.props' // 接收定位樣式
     *  v-on='pop.on' // 接收回調事件
     *  :key='popId'>
     * </component>
     *
     * // 調用彈窗
     * popTools.add('popId', {
     *  component: POP, // 彈窗組件
     *  position: { top: 200 } // 彈窗定位
     *  title: 'xxx', // 彈窗自定義props
     *  @click(e){  // 彈窗事件
     *     ....
     *  }
     * })
     *
     *
     * @example02 - 預注冊
     * 通過預注冊組件,再次調用時,只需要傳入對應注冊名稱,而不需要具體的配置項
     * const [ pops, popTools ] = usePop()
     *
     * // 注冊本地彈窗
     * popTools.componentsCache.add('userInfo', {
     *  component: CMP,
     *  opsition: { ... }
     *  ...
     * })
     *
     * // 調用
     * popTools.add('userInfo', { component: 'userInfo' })
     *
     */
    export function usePop () {
      const components = shallowRef({})
      const componentsCache = componentsRegistry()
    
    
      function has (popId) {
        return !!unref(components)[popId]
      }
    
    
      /**
       * 添加pop
       * @param popId
       * @param options
       * @returns
       */
      function add (popId, options = {}) {
        if (has(popId)) {
          return false
        }
    
    
        let {
          component,
          ..._options
        } = options
    
    
        // 全局緩存
        if (globalComponentsCache.has(component)) {
          const { component: cacheComponents, ...cacheOptions } = globalComponentsCache.fined(component)
          component = cacheComponents
          _options = { ...cacheOptions, ..._options }
        }
    
    
        // 局部緩存
        if (componentsCache.has(component)) {
          const { component: cacheComponents, ...cacheOptions } = componentsCache.fined(component)
          component = cacheComponents
          _options = { ...cacheOptions, ..._options }
        }
    
    
        counterStore.add()
        const newOptions = splitProps({ ..._options, zIndex: counterStore.getCount() })
    
    
        components.value = {
          ...components.value,
          [popId]: {
            popId,
            component,
            ...newOptions
          }
        }
      }
    
    
      /**
       * 更新組件參數
       * @param {*} popId
       * @param {*} options
       * @returns
       */
      function update (popId, options = {}) {
        if (!has(popId)) {
          return false
        }
    
    
        const { component, ...oldOptions } = components.value[popId]
        const newOptions = splitProps(options)
        components.value = {
          ...components.value,
          [popId]: {
            component,
            ...merge(oldOptions, newOptions)
          }
        }
      }
    
    
      /**
       * 移除pop
       * @param popId
       */
      function remove (popId) {
        if (has(popId)) {
          const newCmp = components.value
          delete newCmp[popId]
          components.value = {
            ...newCmp
          }
        }
      }
    
    
      /**
       * 多處調用同一pop時, 替換原顯示pop。
       * @param popId
       * @param options
       */
      function replace (popId, options) {
        remove(popId)
        add(popId, options)
      }
    
    
      function clearAllPop () {
        components.value = {}
      }
    
    
      /**
      * 向上一層級
      * @param popId
      * @returns
      */
      function updateIndex (popId) {
        if (!has(popId)) {
          return
        }
        const currentComponent = unref(components)[popId]
        const upComponent = Object.values(unref(components)).fined(i => i.zIndex > currentComponent.zIndex)
        const currentIndex = currentComponent.zIndex
        const upIndex = upComponent.zIndex
        update(currentIndex.popId, {
          zIndex: upIndex
        })
        update(upComponent.popId, {
          zIndex: currentIndex
        })
      }
    
    
      /**
       * 向下一層級
       * @param {*} popId
       * @returns
       */
      function downIndex (popId) {
        if (!has(popId)) {
          return
        }
        const currentComponent = unref(components)[popId]
        const upComponent = Object.values(unref(components)).fined(i => i.zIndex < currentComponent.zIndex)
        const currentIndex = currentComponent.zIndex
        const upIndex = upComponent.zIndex
        update(currentIndex.popId, {
          zIndex: upIndex
        })
        update(upComponent.popId, {
          zIndex: currentIndex
        })
      }
    
    
      /**
       * 頂層
       * @param popId
       * @returns
       */
      function topIndex (popId) {
        if (!has(popId)) {
          return
        }
        counterStore.add()
        update(popId, {
          zIndex: counterStore.getCount()
        })
      }
    
    
      return [    components,    {      has,      add,      remove,      update,      replace,      clearAllPop,      topIndex,      updateIndex,      downIndex,      componentsCache    }  ]
    }
    
    
    /**
     * 嵌套結構下的彈窗鉤子
     */
    
    
    // 容器鉤子
    export function usePopContainer (provideKey = DEFAULT_POP_SIGN) {
      const [popMap, popTools] = usePop()
      provide(
        provideKey,
        {
          popTools
        }
      )
      return [    popMap, popTools  ]
    }
    
    
    // 子容器鉤子
    export function usePopChildren (provideKey = DEFAULT_POP_SIGN) {
      return inject(provideKey, {})
    }

    core utils

    export function isEvent (propName) {
      const rule = /^@/i
      return rule.test(propName)
    }
    
    // @click => click
    export function eventNameTransition (name) {
      return name.replace('@', '')
    }
    
    // 拆分事件與屬性
    export function splitProps (cmpProps) {
      return Object.entries(cmpProps).reduce((acc, [propName, propValue]) => {
        if (isEvent(propName)) {
          // 自定義事件
          acc.on[eventNameTransition(propName)] = propValue
        } else {
          acc.props[propName] = propValue
        }
    
        return acc
      }, { on: {}, props: {} })
    }
    
    export function counter (initCount = 0, step = 1) {
      let count = initCount
    
      function add (customStep) {
        count += customStep || step
      }
    
      function reduce (customStep) {
        count -= customStep || step
      }
    
      function reset (customStep) {
        count = customStep || initCount
      }
    
      function getCount () {
        return count
      }
    
      return {
        add,
        reduce,
        reset,
        getCount
      }
    }

    工具

    import { merge } from 'lodash-es'
    
    /**
     * 注冊并返回彈窗快捷方法
     * @param {*} popTools
     * @returns
     */
    export function buildDefaultPopBind (popTools, componentsCache) {
      return (popId, component, options) => {
        componentsCache.add(popId, {
          component,
          // 默認定位
          position: position(),
          ...bindDefaultEvents(popTools, popId),
          ...options
        })
    
    
        return {
          open (options) {
            popTools.add(popId, { component: popId, ...options })
          },
          close () {
            popTools.remove(popId)
          },
          update (options) {
            popTools.update(popId, { component: popId, ...options })
          },
          replace (options) {
            popTools.replace(popId, { component: popId, ...options })
          }
        }
      }
    }
    
    
    export const DEFAULT_POSITION = {
      top: 240,
      left: 0,
      right: 0
    }
    
    
    export function position (options = DEFAULT_POSITION) {
      return merge({}, DEFAULT_POSITION, options)
    }
    
    
    export function bindDefaultEvents (popTools, popId) {
      return {
        '@headerMousedown' () {
          popTools.topIndex(popId)
        },
        '@close' (e) {
          popTools.remove(popId)
        }
      }
    }

    關于“vue usePop彈窗控制器如何實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    vue
    AI

    枝江市| 奇台县| 黄梅县| 溧阳市| 济阳县| 沅江市| 莱芜市| 南丹县| 婺源县| 昌吉市| 田东县| 新乐市| 手游| 商城县| 大连市| 海南省| 滦平县| 武鸣县| 台北市| 长宁区| 衡南县| 陆良县| 顺平县| 岱山县| 丹阳市| 赣州市| 万年县| 横峰县| 房山区| 营口市| 裕民县| 息烽县| 望城县| 杭州市| 环江| 西充县| 祁门县| 科技| 井研县| 固安县| 博白县|