我写了一个Headless标签页管理器

504 阅读2分钟

最近我写了一个你用任何UI框架就能自己组装的界面标签页管理器,实现效果如下图 case.gif

项目地址

meogic-tab-manager

简单介绍下如何使用

以下仅用vue作为示例

如何定义数据

npm install @meogic/tab-manager-vue --save后创建对应的数据模型,比如

export class UserTabNode extends TabNode<Component> {  
    static getType(): string {  
        return 'user-tab';  
    }  
    private __user: User;  
    static clone(node: UserTabNode): UserTabNode {  
        const tabNode = new UserTabNode(node.__user, node.__active, node.__key)  
        return tabNode;  
    }  
  
  
    constructor(user:User, active?: boolean, key?: NodeKey) {  
        super(user.name, active, key);  
        this.__user = user  
    }  
  
    //region getters  
    getUser(): User {  
        const self = this.getLatest()  
        return self.__user  
    }  
    //endregion  
  
    //region mutations    
    setUser(value: User): this {  
        const self = this.getWritable()  
        self.__user = value  
        return this  
    }  
    //endregion  
  
    decorate(tabManager: TabManager, config: TabManagerConfig): Component {  
        return h(  
            UserDetail,  
            this.exportJSON()  
        );  
    }  
    static importJSON<T>(serializedNode: SerializedUserTabNode): UserTabNode {  
        const node = $createUserTabNode(serializedNode.user, serializedNode.active);  
        return node;  
    }  
  
    exportJSON(): SerializedUserTabNode {  
        return {  
            name: this.getName(),  
            active: this.getActive(),  
            user: this.getUser(),  
            type: 'tab',  
            version: 1,  
        };  
    }  
}
export function $createUserTabNode(user: User, active?: boolean) {  
    return new UserTabNode(user, active)  
}

UserTabNode

这里名为$createUserTabNode的函数,$开头是标识其只能在update回调函数中使用

如何定义页面结构

创建好了对应的数据后,接下来就是搭建页面

<TabManagerComposer :initial-config="config" @error="onError">
  <TabManagerRootElement/>
  <TabGroupBarPlugin/>
  <Decorators/>
  <TabGroupResizablePlugin/>
  <TabGroupDraggablePlugin/>
</TabManagerComposer>
  • 这里的TabManagerRootElement是你整个标签页管理器的根DOM对象
  • TabManagerComposer则是给整个子组件提供tabManager对象的上下文
  • 这里的config的用途除了可以指定对应主题要用到的css类名,以及注册的Node类等功能外,还可以设置初始化状态,比如
function prepared() {
  const root = $getRoot();
  const window = $createWindowNode()
  const tabGroupNode = $createResizableTabGroupNode()
  tabGroupNode.setActive(true)
  tabGroupNode.setPercent(33.33)
  const tabGroupBar = $createTabGroupBarNode()
  tabGroupNode.append(tabGroupBar)

  const tabGroupNode2 = $createResizableTabGroupNode()
  tabGroupNode2.setPercent(33.33)
  const tabGroupBar2 = $createTabGroupBarNode()
  tabGroupNode2.append(tabGroupBar2)

  const tabGroupNode3 = $createResizableTabGroupNode()
  tabGroupNode3.setPercent(33.33)
  const tabGroupBar3 = $createTabGroupBarNode()
  tabGroupNode3.append(tabGroupBar3)


  root.append(
      window.append(
          tabGroupNode,
          tabGroupNode2,
          tabGroupNode3
      )
  )
}
const config = {  
  tabManagerState: prepared,  
  namespace: 'Playground',  
  nodes: [...PlaygroundNodes],  
  tabNodes: [UserTabNode],  
  tabGroupNodes: [ResizableTabGroupNode],  
  theme  
}

App.vue

基础的交互

可以看到这里并没有用到上面定义的UserTabNode,那是因为添加Tab需要触发一系列操作

tabManager.update(() => {  
  const tabGroupNode = $getNodeByKey(tabGroupNodeKey)  
  if(!$isTabGroupNode(tabGroupNode)){  
    return  
  }  
  const userTabNode = $createUserTabNode(user)  
  tabGroupNode.append(userTabNode)  
  $updateTabGroupBarNode(tabGroupNode)  
  $activeTabNode(userTabNode)  
})

TabManagerOpenPlugin.vue 比如我们在点击了用户列表页里的一个项目时,需要在Tab中打开时

  1. 找到当前正在激活的tabGroupNode
  2. 将对应的userTabNode append 到 tabGroupNode
  3. 同时通过$updateTabGroupBarNode更新这个tabGroupNode对应的tabGroupBar,使其新增对应的标签页头部
  4. 最后通过$activeTabNode在激活当前新增的tabNode,同时隐藏其它的tabNode

直接本地试用

上面基本演示了如何定义数据、如何定义页面结构、以及基础的交互,可能你觉得还是有点懵,没关系,项目代码中已经有示例了。

git clone git@github.com:meogic-tech/meogic-tab-manager.git
npm i --force
npm run dev

然后你就能在lcoalhost上看到了

最后

由于时间与精力的限制,我只提供了vue的示例与工具库,如果你喜欢本项目且愿意贡献代码的话,欢迎你的PR。最后如果本项目对你有所帮助,请帮我点一个star,谢谢!