Commit ff410b224fafa2431a28d4f9c8e1912de160a373

Authored by ly525
1 parent a4b13f8d

refactor(vuex): add element module

front-end/h5/src/components/core/editor.js deleted 100644 → 0
1   -import Vue from 'vue'
2   -import Element from './models/element'
3   -import './styles/index.scss'
4   -
5   -export default {
6   - 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   - },
41   - data: () => ({
42   - activeMenuKey: 'pluginList',
43   - pages: [],
44   - elements: [],
45   - editingElement: null,
46   - isPreviewMode: false
47   - }),
48   - methods: {
49   - getEditorConfig (pluginName) {
50   - // const pluginCtor = Vue.options[pluginName]
51   - // const pluginCtor = this.$options.components[pluginName]
52   - const PluginCtor = Vue.component(pluginName)
53   - return new PluginCtor().$options.editorConfig
54   - },
55   - /**
56   - * !#zh 点击插件,copy 其基础数据到组件树(中间画布)
57   - * #!en click the plugin shortcut, create new Element with the plugin's meta data
58   - * pluginInfo {Object}: 插件列表中的基础数据, {name}=pluginInfo
59   - */
60   - clone ({ name }) {
61   - const zindex = this.elements.length + 1
62   - // const defaultPropsValue = this.getPropsDefaultValue(name)
63   - const editorConfig = this.getEditorConfig(name)
64   - this.elements.push(new Element({ name, zindex, editorConfig }))
65   - },
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   - setCurrentEditingElement (element) {
74   - 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   - on: {
149   - input ({ pluginName, value }) {
150   - if (pluginName === 'lbp-text') {
151   - element.pluginProps.text = value
152   - }
153   - }
154   - }
155   - }
156   - return h(element.name, data)
157   - })()
158   - })}
159   - </div>
160   - )
161   - },
162   - renderPreview (h, elements) {
163   - return (
164   - <div style={{ height: '100%' }}>
165   - {elements.map((element, index) => {
166   - return (() => {
167   - const data = {
168   - style: element.getStyle(),
169   - props: element.pluginProps, // #6 #3
170   - nativeOn: {}
171   - }
172   - return h(element.name, data)
173   - })()
174   - })}
175   - </div>
176   - )
177   - },
178   - renderPluginListPanel () {
179   - return (
180   - <a-row gutter={20}>
181   - {
182   - this.groups.sort().map(group => (
183   - <a-col span={12} style={{ marginTop: '10px' }}>
184   - {this.renderPluginShortcut(group)}
185   - </a-col>
186   - ))
187   - }
188   - </a-row>
189   - )
190   - },
191   - renderPropsEditorPanel (h) {
192   - const formLayout = {
193   - labelCol: { span: 5 },
194   - wrapperCol: { span: 8 }
195   - }
196   - if (!this.editingElement) return (<span>请先选择一个元素</span>)
197   - const editingElement = this.editingElement
198   - const propsConfig = editingElement.editorConfig.propsConfig
199   - return (
200   - <a-form ref="form" layout="horizontal">
201   - {
202   - Object.keys(propsConfig).map(propKey => {
203   - const item = propsConfig[propKey]
204   - // https://vuejs.org/v2/guide/render-function.html
205   - const data = {
206   - props: {
207   - ...item.prop,
208   - // https://vuejs.org/v2/guide/render-function.html#v-model
209   - value: editingElement.pluginProps[propKey] || item.defaultPropValue
210   - },
211   - on: {
212   - // https://vuejs.org/v2/guide/render-function.html#v-model
213   - // input (e) {
214   - // editingElement.pluginProps[propKey] = e.target ? e.target.value : e
215   - // }
216   - change (e) {
217   - editingElement.pluginProps[propKey] = e.target ? e.target.value : e
218   - }
219   - }
220   - }
221   - return (
222   - <a-form-item
223   - label={item.label}
224   - {...formLayout}
225   - >
226   - { h(item.type, data) }
227   - </a-form-item>
228   - )
229   - })
230   - }
231   - </a-form>
232   - )
233   - }
234   - },
235   - render (h) {
236   - return (
237   - <a-layout id="luban-layout" style={{ height: '100vh' }}>
238   - <a-layout-header class="header">
239   - <div class="logo">鲁班 H5</div>
240   - {/* TODO we can show the plugins shortcuts here
241   - <a-menu
242   - theme="dark"
243   - mode="horizontal"
244   - defaultSelectedKeys={['2']}
245   - style={{ lineHeight: '64px', float: 'left', marginLeft: '30%', background: 'transparent' }}
246   - >
247   - {
248   - this.groups.sort().map((group, id) => (
249   - <a-menu-item key={id} class="transparent-bg">
250   - {this.renderPluginShortcut(group)}
251   - </a-menu-item>
252   - ))
253   - }
254   - </a-menu> */}
255   - <a-menu
256   - theme="dark"
257   - mode="horizontal"
258   - defaultSelectedKeys={['2']}
259   - style={{ lineHeight: '64px', float: 'right', background: 'transparent' }}
260   - >
261   - <a-menu-item key="1" class="transparent-bg"><a-button type="primary" size="small">预览</a-button></a-menu-item>
262   - <a-menu-item key="2" class="transparent-bg"><a-button size="small">保存</a-button></a-menu-item>
263   - <a-menu-item key="3" class="transparent-bg"><a-button size="small">发布</a-button></a-menu-item>
264   - </a-menu>
265   - </a-layout-header>
266   - <a-layout>
267   - <a-layout-sider width="160" style="background: #fff">
268   - <a-menu onSelect={val => { this.activeMenuKey = val }} mode="inline" defaultSelectedKeys={['pluginList']} style={{ height: '100%', borderRight: 1 }}>
269   - <a-menu-item key="pluginList">
270   - <a-icon type="user" />
271   - <span>组件列表</span>
272   - </a-menu-item>
273   - <a-menu-item key="2">
274   - <a-icon type="video-camera" />
275   - <span>页面管理</span>
276   - </a-menu-item>
277   - <a-menu-item key="3">
278   - <a-icon type="upload" />
279   - <span>更多模板</span>
280   - </a-menu-item>
281   - </a-menu>
282   - </a-layout-sider>
283   - <a-layout-sider width="240" theme='light' style={{ background: '#fff', padding: '0 12px' }}>
284   - { this.renderPluginListPanel() }
285   - </a-layout-sider>
286   - <a-layout style="padding: 0 24px 24px">
287   - <a-layout-content style={{ padding: '24px', margin: 0, minHeight: '280px' }}>
288   - <div style="text-align: center;">
289   - <a-radio-group
290   - value={this.isPreviewMode}
291   - onInput={value => {
292   - this.isPreviewMode = value
293   - }}
294   - >
295   - <a-radio-button label={false} value={false}>Edit</a-radio-button>
296   - <a-radio-button label={true} value={true}>Preview</a-radio-button>
297   - </a-radio-group>
298   - </div>
299   - <div class='canvas-wrapper'>
300   - { this.isPreviewMode ? this.renderPreview(h, this.elements) : this.renderCanvas(h, this.elements) }
301   - </div>
302   - </a-layout-content>
303   - </a-layout>
304   - <a-layout-sider width="240" theme='light' style={{ background: '#fff', padding: '0 12px' }}>
305   - <a-tabs type="card" style="height: 100%;">
306   - {/*
307   - #!zh tab 标题:
308   - #!en tab title
309   - ElementUI:label
310   - Ant Design Vue:tab
311   - */}
312   - <a-tab-pane key="属性">
313   - <span slot="tab">
314   - <a-icon type="apple" />
315   - 属性
316   - </span>
317   - <div style={{ overflow: 'scroll', height: '100vh' }}>
318   - { this.renderPropsEditorPanel(h) }
319   - </div>
320   - </a-tab-pane>
321   - <a-tab-pane label="动画" key='动画' tab='动画'>动画</a-tab-pane>
322   - <a-tab-pane label="动作" key='动作' tab='动作'>动作</a-tab-pane>
323   - </a-tabs>
324   - </a-layout-sider>
325   - </a-layout>
326   - </a-layout>
327   - )
328   - }
329   -}
front-end/h5/src/components/core/editor/canvas/edit.js
  1 +import { mapState, mapActions } from 'vuex'
1 2 import Shape from '../../support/shape'
  3 +
2 4 export default {
3   - props: ['elements', 'editingElement', 'handleClickElementProp', 'handleClickCanvasProp'],
  5 + props: ['elements', 'handleClickElementProp', 'handleClickCanvasProp'],
4 6 data: () => ({
5 7 vLines: [],
6 8 hLines: [],
7 9 contextmenuPos: []
8 10 }),
  11 + computed: {
  12 + ...mapState('element', {
  13 + editingElement: state => state.editingElement
  14 + })
  15 + },
9 16 methods: {
  17 + ...mapActions('element', [
  18 + 'setEditingElement' // -> this.foo()
  19 + ]),
10 20 // TODO #!zh: 优化代码
11 21 // generate vertical line
12 22 drawVLine (newLeft) {
... ... @@ -86,6 +96,11 @@ export default {
86 96 hideContextMenu () {
87 97 this.contextmenuPos = []
88 98 },
  99 + handleClickCanvas (e) {
  100 + if (!e.target.classList.contains('element-on-edit-canvas')) {
  101 + this.setEditingElement()
  102 + }
  103 + },
89 104 /**
90 105 * #!zh: renderCanvas 渲染中间画布
91 106 * elements
... ... @@ -100,7 +115,7 @@ export default {
100 115 class="canvas-editor-wrapper"
101 116 onClick={(e) => {
102 117 this.hideContextMenu()
103   - this.handleClickCanvasProp(e)
  118 + this.handleClickCanvas(e)
104 119 }}
105 120 onContextmenu={e => {
106 121 this.bindContextMenu(e)
... ... @@ -117,7 +132,7 @@ export default {
117 132 props: element.pluginProps, // #6 #3
118 133 on: {
119 134 // 高亮当前点击的元素
120   - // click: () => this.handleClickElementProp(element)
  135 + // click: () => this.setEditingElement(element)
121 136 input: ({ value, pluginName }) => {
122 137 if (pluginName === 'lbp-text') {
123 138 element.pluginProps.text = value
... ... @@ -128,13 +143,12 @@ export default {
128 143 return (
129 144 <Shape
130 145 element={element}
131   - editingElement={this.editingElement}
132 146 active={this.editingElement === element}
133 147 handleMousedownProp={() => {
134 148 // 在 shape 上面添加 mousedown,而非 plugin 本身添加 onClick 的原因:
135 149 // 在 mousedown 的时候,即可激活 editingElement(当前选中元素)
136 150 // 这样,就不用等到鼠标抬起的时候,也就是 plugin 的 onClick 生效的时候,才给选中的元素添加边框等选中效果
137   - this.handleClickElementProp(element)
  151 + this.setEditingElement(element)
138 152 }}
139 153 handleElementMoveProp={this.handleElementMove}
140 154 >
... ...
front-end/h5/src/components/core/editor/edit-panel/props.js
  1 +import { mapState, mapActions } from 'vuex'
  2 +
1 3 export default {
2   - props: ['editingElement'],
  4 + computed: {
  5 + ...mapState('element', {
  6 + editingElement: state => state.editingElement
  7 + })
  8 + },
3 9 methods: {
  10 + ...mapActions('element', [
  11 + 'setEditingElement' // -> this.foo()
  12 + ]),
4 13 /**
5 14 * 将插件属性的 自定义增强编辑器注入 属性编辑面板中
6 15 */
... ...
front-end/h5/src/components/core/editor/index.js
... ... @@ -31,7 +31,6 @@ export default {
31 31 activeMenuKey: 'pluginList',
32 32 pages: [],
33 33 elements: [],
34   - editingElement: null,
35 34 isPreviewMode: false
36 35 }),
37 36 methods: {
... ... @@ -51,14 +50,6 @@ export default {
51 50 // const defaultPropsValue = this.getPropsDefaultValue(name)
52 51 const editorConfig = this.getEditorConfig(name)
53 52 this.elements.push(new Element({ name, zindex, editorConfig }))
54   - },
55   - setCurrentEditingElement (element) {
56   - this.editingElement = element
57   - },
58   - handleClickCanvas (e) {
59   - if (!e.target.classList.contains('element-on-edit-canvas')) {
60   - this.editingElement = null
61   - }
62 53 }
63 54 },
64 55 render (h) {
... ... @@ -113,9 +104,6 @@ export default {
113 104 ? <RenderPreviewCanvas elements={this.elements}/>
114 105 : <RenderEditCanvas
115 106 elements={this.elements}
116   - handleClickElementProp={this.setCurrentEditingElement}
117   - handleClickCanvasProp={this.handleClickCanvas}
118   - editingElement={this.editingElement}
119 107 />
120 108 }
121 109 </div>
... ... @@ -135,7 +123,7 @@ export default {
135 123 属性
136 124 </span>
137 125 {/* { this.renderPropsEditorPanel(h) } */}
138   - <RenderPropsEditor editingElement={this.editingElement} />
  126 + <RenderPropsEditor/>
139 127 </a-tab-pane>
140 128 <a-tab-pane label="动画" key='动画' tab='动画'>动画</a-tab-pane>
141 129 <a-tab-pane label="动作" key='动作' tab='动作'>动作</a-tab-pane>
... ...
front-end/h5/src/components/core/support/shape.js
... ... @@ -13,7 +13,7 @@ const directionKey = {
13 13 const points = ['lt', 'rt', 'lb', 'rb', 'l', 'r', 't', 'b']
14 14  
15 15 export default {
16   - props: ['element', 'active', 'editingElement', 'handleMousedownProp', 'handleElementMoveProp'],
  16 + props: ['element', 'active', 'handleMousedownProp', 'handleElementMoveProp'],
17 17 methods: {
18 18 /**
19 19 * 通过方位计算样式,主要是 top、left、鼠标样式
... ...
front-end/h5/src/store/index.js
... ... @@ -4,6 +4,7 @@ import editor from &#39;./modules/editor&#39;
4 4 import user from './modules/user'
5 5 import visible from './modules/visible'
6 6 import loading from './modules/loading'
  7 +import element from './modules/element'
7 8  
8 9 Vue.use(Vuex)
9 10  
... ... @@ -21,6 +22,7 @@ export default new Vuex.Store({
21 22 editor,
22 23 user,
23 24 visible,
24   - loading
  25 + loading,
  26 + element
25 27 }
26 28 })
... ...
front-end/h5/src/store/modules/element.js 0 → 100644
  1 +// initial state
  2 +const state = {
  3 + editingElement: null
  4 +}
  5 +
  6 +// getters
  7 +const getters = {
  8 +
  9 +}
  10 +
  11 +// actions
  12 +const actions = {
  13 + setEditingElement ({ commit }, payload) {
  14 + commit('setEditingElement', payload)
  15 + }
  16 +}
  17 +
  18 +// mutations
  19 +const mutations = {
  20 + setEditingElement (state, payload) {
  21 + state.editingElement = payload
  22 + }
  23 +}
  24 +
  25 +export default {
  26 + namespaced: true,
  27 + state,
  28 + getters,
  29 + actions,
  30 + mutations
  31 +}
... ...