Commit 25dbd43c9d1f99bea1bfe5f00270864517b57ca4

Authored by ly525
1 parent 7b6a433f

refactor(core editor): render canvas、preview、props editor、plugin shortcuts in single file

front-end/h5/src/components/core/editor/canvas/edit.js 0 → 100644
  1 +export default {
  2 + props: ['elements', 'handleElementClick'],
  3 + methods: {
  4 + /**
  5 + * #!zh: renderCanvas 渲染中间画布
  6 + * elements
  7 + * @param {*} h
  8 + * @param {*} elements
  9 + * @returns
  10 + */
  11 + renderCanvas (h, elements) {
  12 + return (
  13 + <div style={{ height: '100%' }}>
  14 + {
  15 + elements.map((element, index) => {
  16 + const data = {
  17 + style: element.getStyle(),
  18 + props: element.pluginProps, // #6 #3
  19 + nativeOn: {
  20 + click: () => this.handleElementClick(element)
  21 + }
  22 + }
  23 + return h(element.name, data)
  24 + })
  25 + }
  26 + </div>
  27 + )
  28 + }
  29 + },
  30 + render (h) {
  31 + return this.renderCanvas(h, this.elements)
  32 + }
  33 +}
... ...
front-end/h5/src/components/core/editor/canvas/preview.js 0 → 100644
  1 +export default {
  2 + props: ['elements'],
  3 + methods: {
  4 + renderPreview (h, elements) {
  5 + return (
  6 + <div style={{ height: '100%' }}>
  7 + {elements.map((element, index) => {
  8 + return (() => {
  9 + const data = {
  10 + style: element.getStyle(),
  11 + props: element.pluginProps, // #6 #3
  12 + nativeOn: {}
  13 + }
  14 + return h(element.name, data)
  15 + })()
  16 + })}
  17 + </div>
  18 + )
  19 + }
  20 + },
  21 + render (h) {
  22 + return this.renderPreview(h, this.elements)
  23 + }
  24 +}
... ...
front-end/h5/src/components/core/editor/edit-panel/props.js 0 → 100644
  1 +export default {
  2 + props: ['editingElement'],
  3 + methods: {
  4 + /**
  5 + * 将插件属性的 自定义增强编辑器注入 属性编辑面板中
  6 + */
  7 + mixinEnhancedPropsEditor (editingElement) {
  8 + const { components } = editingElement.editorConfig
  9 + for (const key in components) {
  10 + if (this.$options.components[key]) return
  11 + this.$options.components[key] = components[key]
  12 + }
  13 + },
  14 + renderPropsEditorPanel (h, editingElement) {
  15 + const propsConfig = editingElement.editorConfig.propsConfig
  16 + return (
  17 + <a-form ref="form" label-width="100px" size="mini" label-position="left">
  18 + {
  19 + Object.keys(propsConfig).map(propKey => {
  20 + const item = propsConfig[propKey]
  21 + // https://vuejs.org/v2/guide/render-function.html
  22 + const data = {
  23 + props: {
  24 + ...item.prop,
  25 + // https://vuejs.org/v2/guide/render-function.html#v-model
  26 + value: editingElement.pluginProps[propKey] || item.defaultPropValue
  27 + },
  28 + on: {
  29 + // https://vuejs.org/v2/guide/render-function.html#v-model
  30 + // input (e) {
  31 + // editingElement.pluginProps[propKey] = e.target ? e.target.value : e
  32 + // }
  33 + change (e) {
  34 + // TODO fixme: update plugin props in vuex with dispatch
  35 + editingElement.pluginProps[propKey] = e.target ? e.target.value : e
  36 + }
  37 + }
  38 + }
  39 + return (
  40 + <a-form-item label={item.label}>
  41 + { h(item.type, data) }
  42 + </a-form-item>
  43 + )
  44 + })
  45 + }
  46 + </a-form>
  47 + )
  48 + }
  49 + },
  50 + render (h) {
  51 + const ele = this.editingElement
  52 + if (!ele) return (<span>请先选择一个元素</span>)
  53 + this.mixinEnhancedPropsEditor(ele)
  54 + return this.renderPropsEditorPanel(h, ele)
  55 + }
  56 +}
... ...
front-end/h5/src/components/core/editor/index.js
... ... @@ -2,42 +2,31 @@ import Vue from &#39;vue&#39;
2 2 import Element from '../models/element'
3 3 import '../styles/index.scss'
4 4  
  5 +import RenderEditCanvas from './canvas/edit'
  6 +import RenderPreviewCanvas from './canvas/preview'
  7 +import RenderPropsEditor from './edit-panel/props'
  8 +import RenderShortcutsPanel from './shortcuts-panel/index'
  9 +
  10 +const sidebarMenus = [
  11 + {
  12 + label: '组件列表',
  13 + value: 'pluginList',
  14 + antIcon: 'user'
  15 + },
  16 + {
  17 + label: '页面管理',
  18 + value: 'pageManagement',
  19 + antIcon: 'copy'
  20 + },
  21 + {
  22 + label: '免费模板',
  23 + value: 'freeTemplate',
  24 + antIcon: 'appstore'
  25 + }
  26 +]
5 27 export default {
6 28 name: 'Editor',
7   - components: {
8   - ShortcutButton: {
9   - functional: true,
10   - props: {
11   - faIcon: {
12   - required: true,
13   - type: String
14   - },
15   - title: {
16   - required: true,
17   - type: String
18   - },
19   - clickFn: {
20   - required: false,
21   - type: Function
22   - }
23   - },
24   - render: (h, { props, listeners, slots }) => {
25   - const onClick = props.clickFn || function () {}
26   - return (
27   - <a-button
28   - class="shortcut-button"
29   - onClick={onClick}
30   - >
31   - <i
32   - class={['shortcut-icon', 'fa', `fa-${props.faIcon}`]}
33   - aria-hidden='true'
34   - />
35   - <span>{ props.title }</span>
36   - </a-button>
37   - )
38   - }
39   - }
40   - },
  29 + components: {},
41 30 data: () => ({
42 31 activeMenuKey: 'pluginList',
43 32 pages: [],
... ... @@ -63,159 +52,8 @@ export default {
63 52 const editorConfig = this.getEditorConfig(name)
64 53 this.elements.push(new Element({ name, zindex, editorConfig }))
65 54 },
66   - mixinPluginCustomComponents2Editor () {
67   - const { components } = this.editingElement.editorConfig
68   - for (const key in components) {
69   - if (this.$options.components[key]) return
70   - this.$options.components[key] = components[key]
71   - }
72   - },
73 55 setCurrentEditingElement (element) {
74 56 this.editingElement = element
75   - this.mixinPluginCustomComponents2Editor()
76   - },
77   - /**
78   - * #!zh: 在左侧或顶部导航上显示可用的组件快捷方式,用户点击之后,即可将其添加到中间画布上
79   - * #!en: render shortcust at the sidebar or the header. if user click the shortcut, the related plugin will be added to the canvas
80   - * @param {Object} group: {children, title, icon}
81   - */
82   - renderPluginShortcut (group) {
83   - return group.children.length === 1
84   - ? this.renderSinglePluginShortcut(group)
85   - : this.renderMultiPluginShortcuts(group)
86   - },
87   - /**
88   - * #!zh 渲染多个插件的快捷方式
89   - * #!en render shortcuts for multi plugins
90   - * @param {Object} group: {children, title, icon}
91   - */
92   - renderMultiPluginShortcuts (group) {
93   - const plugins = group.children
94   - return <a-popover
95   - placement="bottom"
96   - class="shortcust-button"
97   - trigger="hover">
98   - <a-row slot="content" gutter={20} style={{ width: '400px' }}>
99   - {
100   - plugins.sort().map(item => (
101   - <a-col span={6}>
102   - <ShortcutButton
103   - clickFn={this.clone.bind(this, item)}
104   - title={item.title}
105   - faIcon={item.icon}
106   - />
107   - </a-col>
108   - ))
109   - }
110   - </a-row>
111   - <ShortcutButton
112   - title={group.title}
113   - faIcon={group.icon}
114   - />
115   - </a-popover>
116   - },
117   - /**
118   - * #!zh: 渲染单个插件的快捷方式
119   - * #!en: render shortcut for single plugin
120   - * @param {Object} group: {children, title, icon}
121   - */
122   - renderSinglePluginShortcut ({ children }) {
123   - const [plugin] = children
124   - return <ShortcutButton
125   - clickFn={this.clone.bind(this, plugin)}
126   - title={plugin.title}
127   - faIcon={plugin.icon}
128   - />
129   - },
130   - /**
131   - * #!zh: renderCanvas 渲染中间画布
132   - * elements
133   - * @param {*} h
134   - * @param {*} elements
135   - * @returns
136   - */
137   - renderCanvas (h, elements) {
138   - return (
139   - <div style={{ height: '100%' }}>
140   - {elements.map((element, index) => {
141   - return (() => {
142   - const data = {
143   - style: element.getStyle(),
144   - props: element.pluginProps, // #6 #3
145   - nativeOn: {
146   - click: this.setCurrentEditingElement.bind(this, element)
147   - }
148   - }
149   - return h(element.name, data)
150   - })()
151   - })}
152   - </div>
153   - )
154   - },
155   - renderPreview (h, elements) {
156   - return (
157   - <div style={{ height: '100%' }}>
158   - {elements.map((element, index) => {
159   - return (() => {
160   - const data = {
161   - style: element.getStyle(),
162   - props: element.pluginProps, // #6 #3
163   - nativeOn: {}
164   - }
165   - return h(element.name, data)
166   - })()
167   - })}
168   - </div>
169   - )
170   - },
171   - renderPluginListPanel () {
172   - return (
173   - <a-row gutter={20}>
174   - {
175   - this.groups.sort().map(group => (
176   - <a-col span={12} style={{ marginTop: '10px' }}>
177   - {this.renderPluginShortcut(group)}
178   - </a-col>
179   - ))
180   - }
181   - </a-row>
182   - )
183   - },
184   - renderPropsEditorPanel (h) {
185   - if (!this.editingElement) return (<span>请先选择一个元素</span>)
186   - const editingElement = this.editingElement
187   - const propsConfig = editingElement.editorConfig.propsConfig
188   - return (
189   - <a-form ref="form" label-width="100px" size="mini" label-position="left">
190   - {
191   - Object.keys(propsConfig).map(propKey => {
192   - const item = propsConfig[propKey]
193   - // https://vuejs.org/v2/guide/render-function.html
194   - const data = {
195   - props: {
196   - ...item.prop,
197   - // https://vuejs.org/v2/guide/render-function.html#v-model
198   - value: editingElement.pluginProps[propKey] || item.defaultPropValue
199   - },
200   - on: {
201   - // https://vuejs.org/v2/guide/render-function.html#v-model
202   - // input (e) {
203   - // editingElement.pluginProps[propKey] = e.target ? e.target.value : e
204   - // }
205   - change (e) {
206   - editingElement.pluginProps[propKey] = e.target ? e.target.value : e
207   - }
208   - }
209   - }
210   - return (
211   - <a-form-item label={item.label}>
212   - { h(item.type, data) }
213   - </a-form-item>
214   - )
215   - })
216   - }
217   - </a-form>
218   - )
219 57 }
220 58 },
221 59 render (h) {
... ... @@ -223,21 +61,7 @@ export default {
223 61 <a-layout id="luban-layout" style={{ height: '100vh' }}>
224 62 <a-layout-header class="header">
225 63 <div class="logo">鲁班 H5</div>
226   - {/* TODO we can show the plugins shortcuts here
227   - <a-menu
228   - theme="dark"
229   - mode="horizontal"
230   - defaultSelectedKeys={['2']}
231   - style={{ lineHeight: '64px', float: 'left', marginLeft: '30%', background: 'transparent' }}
232   - >
233   - {
234   - this.groups.sort().map((group, id) => (
235   - <a-menu-item key={id} class="transparent-bg">
236   - {this.renderPluginShortcut(group)}
237   - </a-menu-item>
238   - ))
239   - }
240   - </a-menu> */}
  64 + {/* TODO we can show the plugins shortcuts here */}
241 65 <a-menu
242 66 theme="dark"
243 67 mode="horizontal"
... ... @@ -252,22 +76,18 @@ export default {
252 76 <a-layout>
253 77 <a-layout-sider width="160" style="background: #fff">
254 78 <a-menu onSelect={val => { this.activeMenuKey = val }} mode="inline" defaultSelectedKeys={['pluginList']} style={{ height: '100%', borderRight: 1 }}>
255   - <a-menu-item key="pluginList">
256   - <a-icon type="user" />
257   - <span>组件列表</span>
258   - </a-menu-item>
259   - <a-menu-item key="2">
260   - <a-icon type="video-camera" />
261   - <span>页面管理</span>
262   - </a-menu-item>
263   - <a-menu-item key="3">
264   - <a-icon type="upload" />
265   - <span>更多模板</span>
266   - </a-menu-item>
  79 + {
  80 + sidebarMenus.map(menu => (
  81 + <a-menu-item key={menu.value}>
  82 + <a-icon type={menu.antIcon} />
  83 + <span>{menu.label}</span>
  84 + </a-menu-item>
  85 + ))
  86 + }
267 87 </a-menu>
268 88 </a-layout-sider>
269 89 <a-layout-sider width="240" theme='light' style={{ background: '#fff', padding: '0 12px' }}>
270   - { this.renderPluginListPanel() }
  90 + <RenderShortcutsPanel groups={this.groups} handleClickShortcut={this.clone} />
271 91 </a-layout-sider>
272 92 <a-layout style="padding: 0 24px 24px">
273 93 <a-layout-content style={{ padding: '24px', margin: 0, minHeight: '280px' }}>
... ... @@ -283,7 +103,8 @@ export default {
283 103 </a-radio-group>
284 104 </div>
285 105 <div class='canvas-wrapper'>
286   - { this.isPreviewMode ? this.renderPreview(h, this.elements) : this.renderCanvas(h, this.elements) }
  106 + {/* { this.isPreviewMode ? this.renderPreview(h, this.elements) : this.renderCanvas(h, this.elements) } */}
  107 + { this.isPreviewMode ? <RenderPreviewCanvas elements={this.elements}/> : <RenderEditCanvas elements={this.elements} handleElementClick={this.setCurrentEditingElement} /> }
287 108 </div>
288 109 </a-layout-content>
289 110 </a-layout>
... ... @@ -300,7 +121,8 @@ export default {
300 121 <a-icon type="apple" />
301 122 属性
302 123 </span>
303   - { this.renderPropsEditorPanel(h) }
  124 + {/* { this.renderPropsEditorPanel(h) } */}
  125 + <RenderPropsEditor editingElement={this.editingElement} />
304 126 </a-tab-pane>
305 127 <a-tab-pane label="动画" key='动画' tab='动画'>动画</a-tab-pane>
306 128 <a-tab-pane label="动作" key='动作' tab='动作'>动作</a-tab-pane>
... ...
front-end/h5/src/components/core/editor/shortcuts-panel/index.js 0 → 100644
  1 +import ShortcutButton from './shortcut-button'
  2 +export default {
  3 + props: {
  4 + groups: {
  5 + required: false,
  6 + type: Array,
  7 + default: () => []
  8 + },
  9 + handleClickShortcut: {
  10 + type: Function
  11 + }
  12 + },
  13 + methods: {
  14 + onClickShortcut (item) {
  15 + if (this.handleClickShortcut) {
  16 + this.handleClickShortcut(item)
  17 + }
  18 + },
  19 + /**
  20 + * #!zh 渲染多个插件的快捷方式
  21 + * #!en render shortcuts for multi plugins
  22 + * @param {Object} group: {children, title, icon}
  23 + */
  24 + renderMultiShortcuts (group) {
  25 + const plugins = group.children
  26 + return <a-popover
  27 + placement="bottom"
  28 + class="shortcust-button"
  29 + trigger="hover">
  30 + <a-row slot="content" gutter={20} style={{ width: '400px' }}>
  31 + {
  32 + plugins.sort().map(item => (
  33 + <a-col span={6}>
  34 + <ShortcutButton
  35 + clickFn={this.onClickShortcut.bind(this, item)}
  36 + title={item.title}
  37 + faIcon={item.icon}
  38 + />
  39 + </a-col>
  40 + ))
  41 + }
  42 + </a-row>
  43 + <ShortcutButton
  44 + title={group.title}
  45 + faIcon={group.icon}
  46 + />
  47 + </a-popover>
  48 + },
  49 + /**
  50 + * #!zh: 渲染单个插件的快捷方式
  51 + * #!en: render shortcut for single plugin
  52 + * @param {Object} group: {children, title, icon}
  53 + */
  54 + renderSingleShortcut ({ children }) {
  55 + const [plugin] = children
  56 + return <ShortcutButton
  57 + clickFn={this.onClickShortcut.bind(this, plugin)}
  58 + title={plugin.title}
  59 + faIcon={plugin.icon}
  60 + />
  61 + },
  62 + /**
  63 + * #!zh: 在左侧或顶部导航上显示可用的组件快捷方式,用户点击之后,即可将其添加到中间画布上
  64 + * #!en: render shortcust at the sidebar or the header. if user click the shortcut, the related plugin will be added to the canvas
  65 + * @param {Object} group: {children, title, icon}
  66 + */
  67 + renderShortCutsPanel (groups) {
  68 + return (
  69 + <a-row gutter={20}>
  70 + {
  71 + groups.sort().map(group => (
  72 + <a-col span={12} style={{ marginTop: '10px' }}>
  73 + {
  74 + group.children.length === 1
  75 + ? this.renderSingleShortcut(group)
  76 + : this.renderMultiShortcuts(group)
  77 + }
  78 + </a-col>
  79 + ))
  80 + }
  81 + </a-row>
  82 + )
  83 + }
  84 + },
  85 + render (h) {
  86 + return this.renderShortCutsPanel(this.groups)
  87 + }
  88 +}
... ...
front-end/h5/src/components/core/editor/shortcuts-panel/shortcut-button.js 0 → 100644
  1 +export default {
  2 + functional: true,
  3 + props: {
  4 + faIcon: {
  5 + required: true,
  6 + type: String
  7 + },
  8 + title: {
  9 + required: true,
  10 + type: String
  11 + },
  12 + clickFn: {
  13 + required: false,
  14 + type: Function
  15 + }
  16 + },
  17 + render: (h, { props, listeners, slots }) => {
  18 + const onClick = props.clickFn || function () {}
  19 + return (
  20 + <a-button
  21 + class="shortcut-button"
  22 + onClick={onClick}
  23 + >
  24 + <i
  25 + class={['shortcut-icon', 'fa', `fa-${props.faIcon}`]}
  26 + aria-hidden='true'
  27 + />
  28 + <span>{ props.title }</span>
  29 + </a-button>
  30 + )
  31 + }
  32 +}
... ...