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 import Shape from '../../support/shape' 2 import Shape from '../../support/shape'
  3 +
2 export default { 4 export default {
3 - props: ['elements', 'editingElement', 'handleClickElementProp', 'handleClickCanvasProp'], 5 + props: ['elements', 'handleClickElementProp', 'handleClickCanvasProp'],
4 data: () => ({ 6 data: () => ({
5 vLines: [], 7 vLines: [],
6 hLines: [], 8 hLines: [],
7 contextmenuPos: [] 9 contextmenuPos: []
8 }), 10 }),
  11 + computed: {
  12 + ...mapState('element', {
  13 + editingElement: state => state.editingElement
  14 + })
  15 + },
9 methods: { 16 methods: {
  17 + ...mapActions('element', [
  18 + 'setEditingElement' // -> this.foo()
  19 + ]),
10 // TODO #!zh: 优化代码 20 // TODO #!zh: 优化代码
11 // generate vertical line 21 // generate vertical line
12 drawVLine (newLeft) { 22 drawVLine (newLeft) {
@@ -86,6 +96,11 @@ export default { @@ -86,6 +96,11 @@ export default {
86 hideContextMenu () { 96 hideContextMenu () {
87 this.contextmenuPos = [] 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 * #!zh: renderCanvas 渲染中间画布 105 * #!zh: renderCanvas 渲染中间画布
91 * elements 106 * elements
@@ -100,7 +115,7 @@ export default { @@ -100,7 +115,7 @@ export default {
100 class="canvas-editor-wrapper" 115 class="canvas-editor-wrapper"
101 onClick={(e) => { 116 onClick={(e) => {
102 this.hideContextMenu() 117 this.hideContextMenu()
103 - this.handleClickCanvasProp(e) 118 + this.handleClickCanvas(e)
104 }} 119 }}
105 onContextmenu={e => { 120 onContextmenu={e => {
106 this.bindContextMenu(e) 121 this.bindContextMenu(e)
@@ -117,7 +132,7 @@ export default { @@ -117,7 +132,7 @@ export default {
117 props: element.pluginProps, // #6 #3 132 props: element.pluginProps, // #6 #3
118 on: { 133 on: {
119 // 高亮当前点击的元素 134 // 高亮当前点击的元素
120 - // click: () => this.handleClickElementProp(element) 135 + // click: () => this.setEditingElement(element)
121 input: ({ value, pluginName }) => { 136 input: ({ value, pluginName }) => {
122 if (pluginName === 'lbp-text') { 137 if (pluginName === 'lbp-text') {
123 element.pluginProps.text = value 138 element.pluginProps.text = value
@@ -128,13 +143,12 @@ export default { @@ -128,13 +143,12 @@ export default {
128 return ( 143 return (
129 <Shape 144 <Shape
130 element={element} 145 element={element}
131 - editingElement={this.editingElement}  
132 active={this.editingElement === element} 146 active={this.editingElement === element}
133 handleMousedownProp={() => { 147 handleMousedownProp={() => {
134 // 在 shape 上面添加 mousedown,而非 plugin 本身添加 onClick 的原因: 148 // 在 shape 上面添加 mousedown,而非 plugin 本身添加 onClick 的原因:
135 // 在 mousedown 的时候,即可激活 editingElement(当前选中元素) 149 // 在 mousedown 的时候,即可激活 editingElement(当前选中元素)
136 // 这样,就不用等到鼠标抬起的时候,也就是 plugin 的 onClick 生效的时候,才给选中的元素添加边框等选中效果 150 // 这样,就不用等到鼠标抬起的时候,也就是 plugin 的 onClick 生效的时候,才给选中的元素添加边框等选中效果
137 - this.handleClickElementProp(element) 151 + this.setEditingElement(element)
138 }} 152 }}
139 handleElementMoveProp={this.handleElementMove} 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 export default { 3 export default {
2 - props: ['editingElement'], 4 + computed: {
  5 + ...mapState('element', {
  6 + editingElement: state => state.editingElement
  7 + })
  8 + },
3 methods: { 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,7 +31,6 @@ export default {
31 activeMenuKey: 'pluginList', 31 activeMenuKey: 'pluginList',
32 pages: [], 32 pages: [],
33 elements: [], 33 elements: [],
34 - editingElement: null,  
35 isPreviewMode: false 34 isPreviewMode: false
36 }), 35 }),
37 methods: { 36 methods: {
@@ -51,14 +50,6 @@ export default { @@ -51,14 +50,6 @@ export default {
51 // const defaultPropsValue = this.getPropsDefaultValue(name) 50 // const defaultPropsValue = this.getPropsDefaultValue(name)
52 const editorConfig = this.getEditorConfig(name) 51 const editorConfig = this.getEditorConfig(name)
53 this.elements.push(new Element({ name, zindex, editorConfig })) 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 render (h) { 55 render (h) {
@@ -113,9 +104,6 @@ export default { @@ -113,9 +104,6 @@ export default {
113 ? <RenderPreviewCanvas elements={this.elements}/> 104 ? <RenderPreviewCanvas elements={this.elements}/>
114 : <RenderEditCanvas 105 : <RenderEditCanvas
115 elements={this.elements} 106 elements={this.elements}
116 - handleClickElementProp={this.setCurrentEditingElement}  
117 - handleClickCanvasProp={this.handleClickCanvas}  
118 - editingElement={this.editingElement}  
119 /> 107 />
120 } 108 }
121 </div> 109 </div>
@@ -135,7 +123,7 @@ export default { @@ -135,7 +123,7 @@ export default {
135 属性 123 属性
136 </span> 124 </span>
137 {/* { this.renderPropsEditorPanel(h) } */} 125 {/* { this.renderPropsEditorPanel(h) } */}
138 - <RenderPropsEditor editingElement={this.editingElement} /> 126 + <RenderPropsEditor/>
139 </a-tab-pane> 127 </a-tab-pane>
140 <a-tab-pane label="动画" key='动画' tab='动画'>动画</a-tab-pane> 128 <a-tab-pane label="动画" key='动画' tab='动画'>动画</a-tab-pane>
141 <a-tab-pane label="动作" key='动作' tab='动作'>动作</a-tab-pane> 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,7 +13,7 @@ const directionKey = {
13 const points = ['lt', 'rt', 'lb', 'rb', 'l', 'r', 't', 'b'] 13 const points = ['lt', 'rt', 'lb', 'rb', 'l', 'r', 't', 'b']
14 14
15 export default { 15 export default {
16 - props: ['element', 'active', 'editingElement', 'handleMousedownProp', 'handleElementMoveProp'], 16 + props: ['element', 'active', 'handleMousedownProp', 'handleElementMoveProp'],
17 methods: { 17 methods: {
18 /** 18 /**
19 * 通过方位计算样式,主要是 top、left、鼠标样式 19 * 通过方位计算样式,主要是 top、left、鼠标样式
front-end/h5/src/store/index.js
@@ -4,6 +4,7 @@ import editor from &#39;./modules/editor&#39; @@ -4,6 +4,7 @@ import editor from &#39;./modules/editor&#39;
4 import user from './modules/user' 4 import user from './modules/user'
5 import visible from './modules/visible' 5 import visible from './modules/visible'
6 import loading from './modules/loading' 6 import loading from './modules/loading'
  7 +import element from './modules/element'
7 8
8 Vue.use(Vuex) 9 Vue.use(Vuex)
9 10
@@ -21,6 +22,7 @@ export default new Vuex.Store({ @@ -21,6 +22,7 @@ export default new Vuex.Store({
21 editor, 22 editor,
22 user, 23 user,
23 visible, 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 +}