├── .gitignore ├── README-CN.md ├── README.md ├── babel.config.js ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ ├── Content1.vue │ ├── Content2.vue │ ├── Content3.vue │ ├── GlComponent.vue │ └── Glayout.vue ├── main.ts ├── shims-vue.d.ts └── ts │ └── predefined-layouts.ts ├── tsconfig.json ├── vue.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | # vue3-golden-layout-virtualcomponent 2 | Gloden Layout官网:[http://golden-layout.com](http://golden-layout.com) 3 | 4 | Golden Layout Git:[https://github.com/golden-layout/golden-layout](https://github.com/golden-layout/golden-layout) 5 | 6 | 本Demo演示了Golden Layout布局库的最灵活自由但也是最复杂的一个用法:Virtual Component,使用这种用法,你可以跟其它的任何框架结合,而不会受限于Golden Layout的写法,GL的另外三种用法或多或少会跟其它框架冲突,因为GL默认是直接修改DOM树,而其它框架,比如vue,是将自己的结构渲染成DOM树 7 | 8 | 本Demo不演示弹窗(popup)! 9 | 如果你使用弹窗功能发现了bug,不要来找我。当然,我一般也不上github,所以有啥问题直接去官方git发issue 10 | 11 | 本Demo由vue-cli4创建,使用的Golden Layout版本:2.3.0 12 | 13 | 如果你不了解Golden Layout,只是被比如官网首页的演示吸引过来的,那么我建议你先阅读一下官方git仓库里的README 14 | 15 | 注:当前时间2021/09/20,GL官网依然是v1版本的案例和教程,但git仓库已更新至v2版本,v2由于加入了虚拟组件(即Virtual Component),改写了大量底层代码,但并不代表官网的文档完全不能使用,至少配置项有哪些还是可以看的,如果你想更好地学习代码的写法,那目前只能看官方git的README和apitest目录下的例子 16 | 17 | ## Demo如何运行 18 | 1. 下载该仓库,git拉取或zip下载都行 19 | 2. 在项目根目录运行命令行:`yarn` 20 | 3. 命令行运行:`yarn serve` 21 | 4. 浏览器打开:`http://localhost:8080/` 22 | 23 | ## Demo已将GL封装成vue3组件,可在其它项目中直接使用! 24 | > 但是,组件是用TS写的,如果你不用TS,那么你只能自己翻译一下了 25 | 26 | `src/components/`内的`Glayout.vue`和`GlComponent.vue`就是全部的组件,它们必须一起使用,其它文件都是为了展示这个demo 27 | 28 | 当然,除此之外你还需要从npm下载golden-layout 29 | 30 | **Glayout.vue** 31 | 32 | 这是GL布局组件,掌管整个布局 33 | 34 | 它有三个接口函数: 35 | 36 | addGLComponent(componentType: string, title: string) 37 | loadGLLayout(layoutConfig: LayoutConfig | ResolvedLayoutConfig) 38 | getLayoutConfig() 39 | 40 | 具体用法详见下方 41 | 42 | **GlComponent.vue** 43 | 44 | 这是GL布局中的单个内容容器,用于存放并展示你的自定义内容 45 | 46 | 比如像Visual Studio那样打开了很多代码的标签页,一个容器就是存放了某一个标签页里所展示的内容,包含代码、行数字、滚动条等,标签只是跟这个容器绑定,但不归这个容器管 47 | 48 | **注:你可能需要更改这两个组件以满足你自己的需求,毕竟这两个组件只是最基本地使用GL** 49 | 50 | ### 具体用法 51 | 查看`src/App.vue`以了解详细的写法,这里只是简单说明 52 | 53 | 60 | 61 | 70 | 71 | 72 | 73 | 74 | glayout就是你唯一需要关心的组件,将该组件放到任何你想要展示GL布局的地方 75 | 76 | ##### glayout的3个Attr 77 | - ref:vue3的写法,用于script中获取这个组件,参考官方文档 [ref](https://v3.cn.vuejs.org/api/special-attributes.html#ref) 78 | - glc-path:你的自定义内容组件的根目录,在本Demo中就是Content1.vue、Content2.vue和Content3.vue所在的目录,由于Glayout.vue在同目录,所以填写`./` 79 | - style:css样式,一定要给width和height赋值,决定了该GL布局的占用面积,如果宽高不赋值,那么宽度可能是100%,高度就是0了,最终应该什么都不会显示,如果是要做单页应用,即该GL组件需要撑满浏览器窗口,那么除了这里height赋值100%,记得html、body以及所有的父DOM都要设置高度100% 80 | 81 | ##### 自定义内容 82 | 比如Content1.vue 83 | 84 | 87 | 88 | 就这么简单,这里样式写了白色是因为我用了黑色风格的GL布局,否则字就看不见了 89 | 90 | 你可以写成任何vue组件,按理都应该能够展示出来 91 | 92 | ##### 将自定义内容添加到GL布局中 93 | 调用组件的`addGLComponent(componentType: string, title: string)`方法 94 | - componentType:一般是组件名,比如 "Content1" 95 | 96 | 最终加载的内容组件为:glcPath + componentType + ".vue" 97 | 98 | 在本例中就是 "./Content1.vue" 99 | 100 | 由于是组合路径,当然你也可以写 "SomePath/Content1",最后会加载 "./SomePath/Content1.vue" 101 | 102 | ##### 保存当前布局 103 | 调用组件的`getLayoutConfig()`方法以获取布局配置对象,然后可以用`JSON.stringify`转换成字符串,就能保存了,比如保存到`localStorage` 104 | 105 | ##### 加载布局 106 | 无论是你自己写的初始布局配置,还是恢复成保存的配置,都可以通过`loadGLLayout(layoutConfig: LayoutConfig | ResolvedLayoutConfig)`方法加载布局 107 | 108 | LayoutConfig是最终这个方法内部加载组件和内容用的配置类型 109 | 110 | ResolvedLayoutConfig是保存的布局配置类型,你获取到保存的字符串后,得先用`JSON.parse`转换回配置对象,然后再传入,这个方法内部会再将其转换成LayoutConfig 111 | 112 | ##### LayoutConfig 113 | 这是GL定义的一个结构,官网的文档有 114 | 115 | 本Demo里有个最简单的版本:`src/ts/predefined-layouts.ts`,是从官方的apitest里抄过来的,所以详细的可以去看官方的案例 116 | 117 | 配置里的componentType要写你的自定义内容组件的文件名,即 "Content1"这种,如果不在glcPath下,还得加上额外路径 118 | 119 | ##### 引入官方css样式 120 | 121 | 122 | 123 | 124 | 官方样式在node_modules内,你还能找到更多主题 125 | 126 | 第1行是基础样式 127 | 128 | 第2行是主题样式,主题样式可换,不过我这种写法应该是换不了的,得用import之类动态导入 129 | 130 | 之所以样式没有集成到Glayout组件里,是为了方便你控制样式,而且GL的虚拟组件本身就是为了跟其它框架结合使用的,如果你用了其它UI框架或库,一般来说都会跟GL官方样式不和谐,这时你就会需要自定义GL的样式 131 | 132 | ### 注意事项 133 | 1. 如果你要修改或另写一个Glayout组件,那么注意GL的根DOM必须在你的GlComponent组件上面,否则GL管理器将会覆盖在内容组件上面,如果管理器有背景色(用了主题样式就肯定有),那么内容就完全看不见了,具体顺序看Glayout.vue的template 134 | 2. GL相关的样式无法使用scoped,因为GL是动态加载的,无法被vue所解析,但对于IDE类的应用来说,一般也只需要一种样式 135 | 3. 对于单页应用,一般都不想要页面滚动条,因此需要给body加上样式`overflow:hidden`,否则拖动标签页的时候若移出边界就会出现滚动条,你可以把demo里body的overflow样式删掉,然后拖动标签页到边界看看效果 136 | 4. LayoutConfig的componentState可能你用不到,而且Glayout组件也没有对外开放该值,但是有坑所以还是要说一下,首先它虽然能赋任意的值,但个人只建议undefined和{ }对象,这个值在Glayout组件里被用作传递非常关键的ref索引,没有这个ref,将会导致找不到具体的GlComponent,所以我的处理,如果componentState不是对象,那么直接用对象覆盖,如果是,则把ref索引添加进去,key是refId 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-golden-layout-virtualcomponent 2 | 3 | [中文文档](https://github.com/chyj4747/vue3-golden-layout-virtualcomponent/blob/master/README-CN.md) 4 | ------------ 5 | 6 | 7 | Gloden Layout official site: [http://golden-layout.com](http://golden-layout.com) 8 | 9 | Golden Layout Git: [https://github.com/golden-layout/golden-layout](https://github.com/golden-layout/golden-layout) 10 | 11 | This demo shows the most flexible but also the most complex way of using Golden Layout, which is the Virtual Component. By using virtual component, you can integrate golden layout with any other framework such as Vue. 12 | 13 | This demo does not show Popup! 14 | So if you find a bug when using popup, don't ask me. Try to send an issue to the GL git for help. 15 | 16 | This demo is created by vue-cli4, and use Golden Layout v2.3.0 17 | 18 | If you don't know golden layout, but want to use its nice features, then the first thing I will recommend is the README in the GL git. 19 | 20 | Note: current date is 2021/09/20. GL official site still doesn't have tutorial and documentation for v2, although git repo is already updated to v2.3. The virtual component is added since GL v2.x, which changes a lot of the base codes. But it doesn't mean the documentation in the official site is useless. At least you can learn the config params. If you want to learn GL, currently you can just read the README and the codes in the folder apitest of GL git repo. 21 | 22 | ## How to run the demo 23 | 1. git pull or download this demo repo 24 | 2. run `yarn` in the root path of the repo 25 | 3. run `yarn serve` 26 | 4. open `http://localhost:8080/` in your broswer 27 | 28 | ## This demo has already integrated GL as vue3 components, they can be used in other projects directly! 29 | > But, these conponents are wrote in TS, if you don't use ts, then you have to translate by yourself. 30 | 31 | In the path `src/components/`, `Glayout.vue` and `GlComponent.vue` are the components. They must be used together. Other files in this repo are for demo. 32 | 33 | Of course, you need to download golden-layout from npm. 34 | 35 | **Glayout.vue** 36 | 37 | This is the layout component, which controls and manages the whole layout. 38 | 39 | It exports 3 methods: 40 | 41 | addGLComponent(componentType: string, title: string) 42 | loadGLLayout(layoutConfig: LayoutConfig | ResolvedLayoutConfig) 43 | getLayoutConfig() 44 | 45 | See below for more details. 46 | 47 | **GlComponent.vue** 48 | 49 | This is the container for custom content. It is also the conponent defined in GL document. 50 | 51 | **Note: you may need to modify these components for your own demand. These two components just made for the basic usage of GL.** 52 | 53 | ### Usage of the components 54 | Check `src/App.vue` for the complete code. Here just shows the simplest code. 55 | 56 | 63 | 64 | 73 | 74 | 75 | 76 | 77 | glayout is the only component you will use in code. Put it anywhere you wanna show the golden layout. 78 | 79 | ##### glayout's 3 Attrs 80 | - ref: used in vue3, for getting the component in script. See Vue3 doc [ref](https://v3.vuejs.org/api/special-attributes.html#ref) 81 | - glc-path: the path of your custom content components. In this demo, it is the path which has Content1.vue, Content2.vue and Content3.vue. Because the Glayout.vue is in the same path, glc-path here is `./` 82 | - style: must give value to width and height, which decide how many spaces GL will occupy. If they are undefined, then width may be 100%, but the height will be 0, which will cause the GL to be hidden. When making a single page app, that is you want GL to be full screen, not only to set height 100% in this style, but also you need to set the height of html, body and any parent doms to be 100%. 83 | 84 | ##### Custom content 85 | E.g. Content1.vue 86 | 87 | 90 | 91 | It's simple. Here the white color is for the dark theme, otherwise the words can not be seen. 92 | 93 | You can write any vue component. It should work with this demo. 94 | 95 | ##### Add custom content to GL 96 | Call `addGLComponent(componentType: string, title: string)` 97 | - componentType: usually it is the component's file name, such as "Content1" 98 | 99 | The component to be loaded is `glcPath + componentType + ".vue"` 100 | 101 | In this case it's "./Content1.vue" 102 | 103 | Because it combines the paths, you can write "SomePath/Content1", then "./SomePath/Content1.vue" will be loaded. 104 | 105 | ##### Save current layout 106 | Call `getLayoutConfig()` to get the config object, then use `JSON.stringify` to change it to string. You can then save the config string locally or in localStorage 107 | 108 | ##### Load layout 109 | Call `loadGLLayout(layoutConfig: LayoutConfig | ResolvedLayoutConfig)` to load your initial or saved layout config 110 | 111 | LayoutConfig is finally used in this method to load components. 112 | 113 | ResolvedLayoutConfig is the saved config type. After you get the saved config string, using `JSON.parse` to get the config object. Pass the object to this method. The config object will be changed to LayoutConfig internally. 114 | 115 | ##### LayoutConfig 116 | It is defined by GL, you can find it on the official site. 117 | 118 | There is a simple version of the config in this demo, which is `src/ts/predefined-layouts.ts`. 119 | 120 | It is copied from GL official repo's apitest, so you can see the official repo for more details. 121 | 122 | "componentType" in the config is the same as the first param of the method `addGLComponent`. 123 | You need to write the file name of content component like "Content1". 124 | 125 | ##### Import GL's style 126 | 127 | 128 | 129 | 130 | They are in node_modules, and you can find more themes. 131 | The first line is the basic style. 132 | The second line is theme. If you want to dynamically change it, you may need something like `import` 133 | 134 | The reason for not integrating GL style in Glayout is that it is easier to use your own style. 135 | The virtual component of GL is designed for using with other frameworks or libs. If you use other UI framework or lib, usually the style is not in tune with GL style, so you may need to customize style for GL. 136 | 137 | ### Notes 138 | 1. If you want to modify or write a new component like Glayout, then make sure the root DOM of GL is above of your GlComponent. Otherwise the GL container will be rendered on top of all content components, so you can't see any content. See the order in the template of Glayout.vue. 139 | 2. The style related to GL can not use `scoped`. Because GL is dynamically added to the dom tree, which cannot be resovled by vue. But for app like IDE, usually one style is enough. 140 | 3. For single page app, scroll bar is not needed. `overflow:hidden` should be added to `body`, or otherwise the scroll bar will appear when dragging the tab to the border of body. You can delete the body's overflow of this demo to see the effect. 141 | 4. The componentState in LayoutConfig may not useful to you, also Glayout does not export it, but it has a trap so I have to mention it here. Basically it can be set to any value type. But I just recommend to set it to undefined and object. It is used for an impotant ref, without this ref, Glayout cannot find any target GlComponent. What I did is if componentState is not object, it will be set to { refId }, or if it is object, then refId will be it's new key. 142 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-golden-layout-virtualcomponent", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "golden-layout": "^2.3.0", 13 | "vue": "^3.0.0", 14 | "vue-class-component": "^8.0.0-0" 15 | }, 16 | "devDependencies": { 17 | "@typescript-eslint/eslint-plugin": "^4.18.0", 18 | "@typescript-eslint/parser": "^4.18.0", 19 | "@vue/cli-plugin-babel": "~4.5.0", 20 | "@vue/cli-plugin-eslint": "~4.5.0", 21 | "@vue/cli-plugin-typescript": "^4.5.13", 22 | "@vue/cli-service": "~4.5.0", 23 | "@vue/compiler-sfc": "^3.0.0", 24 | "@vue/eslint-config-typescript": "^7.0.0", 25 | "babel-eslint": "^10.1.0", 26 | "eslint": "^6.7.2", 27 | "eslint-plugin-vue": "^7.0.0", 28 | "typescript": "~4.1.5" 29 | }, 30 | "eslintConfig": { 31 | "root": true, 32 | "env": { 33 | "node": true 34 | }, 35 | "extends": [ 36 | "plugin:vue/essential", 37 | "eslint:recommended", 38 | "@vue/typescript" 39 | ], 40 | "parserOptions": { 41 | "parser": "@typescript-eslint/parser" 42 | }, 43 | "rules": { 44 | "no-unused-vars": "off" 45 | } 46 | }, 47 | "browserslist": [ 48 | "> 1%", 49 | "last 2 versions", 50 | "not dead" 51 | ] 52 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chyj4747/vue3-golden-layout-virtualcomponent/5ce17d3ec7a93914bd08ea56cfc455f95608812b/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 70 | 71 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chyj4747/vue3-golden-layout-virtualcomponent/5ce17d3ec7a93914bd08ea56cfc455f95608812b/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/Content1.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/Content2.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/Content3.vue: -------------------------------------------------------------------------------- 1 | 55 | -------------------------------------------------------------------------------- /src/components/GlComponent.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 55 | -------------------------------------------------------------------------------- /src/components/Glayout.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 304 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /src/ts/predefined-layouts.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentItemConfig, 3 | ItemType, 4 | LayoutConfig, 5 | StackItemConfig, 6 | } from "golden-layout"; 7 | 8 | const miniRowConfig: LayoutConfig = { 9 | root: { 10 | type: ItemType.row, 11 | content: [ 12 | { 13 | type: "component", 14 | title: "Title 1st", 15 | header: { show: "top" }, 16 | isClosable: false, 17 | componentType: "Content1", 18 | width: 10, 19 | componentState: undefined, 20 | } as ComponentItemConfig, 21 | { 22 | type: "component", 23 | title: "I'm wide", 24 | header: { show: "top", popout: false }, 25 | componentType: "Content2", 26 | componentState: { abc: 123 }, 27 | } as ComponentItemConfig, 28 | ], 29 | }, 30 | }; 31 | 32 | export const prefinedLayouts = { 33 | miniRow: miniRowConfig, 34 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: "./", 3 | runtimeCompiler: true 4 | }; 5 | --------------------------------------------------------------------------------