Commit bb3df274 authored by 张晨辰's avatar 张晨辰

feat: 脚本

parent a12fb83a
...@@ -34,8 +34,7 @@ export default new Vuex.Store({ ...@@ -34,8 +34,7 @@ export default new Vuex.Store({
'addDataMapping', 'addDataMapping',
'deleteDataMapping', 'deleteDataMapping',
'modifyDataMapping', 'modifyDataMapping',
'modifyComponent', 'modifyActiveView'
'modifyProperties'
] ]
}) })
] ]
......
...@@ -20,6 +20,7 @@ export const projectStore = { ...@@ -20,6 +20,7 @@ export const projectStore = {
dataMapping: [], dataMapping: [],
}, },
activeComponent: {}, activeComponent: {},
activeComponentCopy: {}, // 当前选中节点的镜像,用来处理拖拽时数据变化频繁的问题
activeIdList: [], activeIdList: [],
activeViews: '', activeViews: '',
dragUUID: '', dragUUID: '',
...@@ -58,67 +59,79 @@ export const projectStore = { ...@@ -58,67 +59,79 @@ export const projectStore = {
if (item !== state.activeComponent) { if (item !== state.activeComponent) {
state.activeComponent = item || state.activeComponent; state.activeComponent = item || state.activeComponent;
state.activeComponent.properties = state.activeComponent.properties || {}; state.activeComponent.properties = state.activeComponent.properties || {};
state.activeComponentCopy = _.cloneDeep(state.activeComponent);
} }
}, },
/** /**
* 修改当前组件的属性 * 修改当前选中的节点
* @param {*} state * @param {*} state
* @param {*} data * @param {*} view
*/ */
modifyProperties(state, props) { modifyActiveView(state, view) {
if (!props) { if (!view) {
return; return;
} }
let _prop = _.cloneDeep(state.activeComponent.properties || {}); let _view = _.cloneDeep(state.activeComponent || {});
if (state.operateStack.length) { if (state.operateStack.length) {
if (state.stackIndex !== 0) { if (state.stackIndex !== 0) {
// 如果操作栈index有值,说明之前做过撤销/重做 // 如果操作栈index有值,说明之前做过撤销/重做
// 则把操作index之后的数据都丢弃 // 则把操作index之后的数据都丢弃
state.operateStack = state.operateStack.slice(state.stackIndex); state.operateStack = state.operateStack.slice(state.stackIndex);
} }
let _last_operate = state.operateStack[0];
// 上一次的操作和最新一次操作uuid不同,表示发生了不同视图的操作
// 需要记录当前视图的原始数据
if (_last_operate.uuid !== _view.uuid) {
state.operateStack.unshift(_.cloneDeep(_view));
}
} else { } else {
// 如果操作栈中是空的,说明从来没有操作 // 如果操作栈中是空的,说明从来没有操作
// 则在开始插入数据的时候,额外插入一条最原始的数据 // 则在开始插入数据的时候,额外插入一条最原始的数据
state.operateStack.unshift({ state.operateStack.unshift(_.cloneDeep(_view));
uuid: state.activeComponent.uuid,
properties: _.cloneDeep(_prop)
});
} }
state.stackIndex = 0; // 开始编辑的时候,重置操作栈的下标 state.stackIndex = 0; // 开始编辑的时候,重置操作栈的下标
_prop = Object.assign({}, _prop, props); _view = _.merge(_view, view);
// 在操作栈中插入最新值 // 在操作栈中插入最新值
state.operateStack.unshift({ state.operateStack.unshift(_.cloneDeep(_.assign({ uuid: _view.uuid }, view)));
uuid: state.activeComponent.uuid,
properties: _.cloneDeep(_prop)
});
// 操作栈最大200 // 操作栈最大200
if (state.operateStack.length > 200) { if (state.operateStack.length > 200) {
state.operateStack.pop(); state.operateStack.pop();
} }
Vue.set(state.activeComponent, 'properties', _prop);
_.forIn(view, (value, key) => {
Vue.set(state.activeComponent, key, _view[key]);
})
if (view.type) {
// view.type有值说明修改了节点的类型
// 如果修改了节点类型,需要删除无用的属性
let defaultProps = Object.keys(getCmpProps(view.type));
let _properties = _.cloneDeep(state.activeComponent.properties);
_.forIn(_properties, (value, key, prop) => {
if (defaultProps.indexOf(key) === -1) {
delete prop[key];
}
});
Vue.set(state.activeComponent, 'properties', _properties);
}
state.activeComponentCopy = _.cloneDeep(state.activeComponent);
}, },
/** /**
* 修改当前组件 * 修改当前组件镜像的属性
* @param {*} state * @param {*} state
* @param {*} data * @param {*} props
*/ */
modifyComponent(state, data) { modifyCopyProperties(state, props) {
if (!data || !data.label) { if (!props) {
return; return;
} }
Vue.set(state.activeComponent, data.label, data.value);
if (data.label === 'type') { let _prop = _.cloneDeep(state.activeComponentCopy.properties || {});
// 如果修改了组件类型,需要删除无用的属性 _prop = Object.assign({}, _prop, props);
let defaultProps = Object.keys(getCmpProps(data.value)); Vue.set(state.activeComponentCopy, 'properties', _prop);
let cmpProps = _.cloneDeep(state.activeComponent.properties);
_.forIn(cmpProps, (value, key, prop) => {
if (defaultProps.indexOf(key) === -1) {
delete prop[key];
}
});
Vue.set(state.activeComponent, 'properties', cmpProps);
}
}, },
/** /**
* assets拖拽 * assets拖拽
...@@ -144,17 +157,12 @@ export const projectStore = { ...@@ -144,17 +157,12 @@ export const projectStore = {
if (_operate) { if (_operate) {
let _cmp = getCmpByUUID(_operate.uuid, state.data.views); let _cmp = getCmpByUUID(_operate.uuid, state.data.views);
if (_cmp) { if (_cmp) {
_cmp.properties = _operate.properties; _cmp = _.merge(_cmp, _operate);
if (_operate.uuid === state.activeComponentCopy.uuid) {
state.activeComponentCopy = _.cloneDeep(_cmp)
}
} }
} }
},
/**
* 修改节点的events
* @param {*} state
* @param {*} event
*/
updateNodeEvent(state, event) {
state.activeComponent.events = _.assign(state.activeComponent.events, event)
}, },
modifyProject(state) { modifyProject(state) {
...@@ -252,6 +260,9 @@ export const projectStore = { ...@@ -252,6 +260,9 @@ export const projectStore = {
activeComponent: state => { activeComponent: state => {
return state.activeComponent; return state.activeComponent;
}, },
activeComponentCopy: state => {
return state.activeComponentCopy;
},
/** /**
* 当前激活的组件ID * 当前激活的组件ID
*/ */
...@@ -334,8 +345,6 @@ export const projectStore = { ...@@ -334,8 +345,6 @@ export const projectStore = {
context.state.activeViews = _view.data.uuid; context.state.activeViews = _view.data.uuid;
} }
// let _cmp = context.getters.componentList.find(c => c.uuid === data.data.uuid)
context.commit('activeComponent', data.data); context.commit('activeComponent', data.data);
}, },
/** /**
...@@ -357,28 +366,49 @@ export const projectStore = { ...@@ -357,28 +366,49 @@ export const projectStore = {
let _img = new Image(); let _img = new Image();
_img.src = _url; _img.src = _url;
_img.onload = function () { _img.onload = function () {
commit('modifyProperties', { commit('modifyActiveView', {
properties: {
width: _img.width, width: _img.width,
height: _img.height height: _img.height
}
}); });
} }
} }
} }
commit('modifyProperties', props) commit('modifyActiveView', {
properties: props
})
},
/**
* 修改镜像的属性
* @param {*} param0
* @param {*} props
*/
modifyCopyProperties({ commit }, props) {
commit('modifyCopyProperties', props)
}, },
/** /**
* 修改组件 * 修改当前选中的节点
*/ */
modifyComponent({ commit }, data) { modifyActiveView({ commit }, view) {
commit('modifyComponent', data) commit('modifyActiveView', view)
}, },
/** /**
* 编辑节点事件 * 新增节点脚本
* @param {*} param0
* @param {*} data
*/ */
updateNodeEvent({ commit }, data) { addNodeScript({ commit, state }, script) {
commit('updateNodeEvent', data) let _scripts = _.cloneDeep(state.activeComponent.scripts || []);
_scripts.push({
script: script,
options: {}
});
commit('modifyActiveView', {
scripts: _scripts
})
}, },
exportView({ state }, view) { exportView({ state }, view) {
......
export default [{
script: 'wave',
options: {
duration: {
title: 'duration',
type: 'inputNumber',
value: 500
},
autoPlay: {
title: 'autoPlay',
type: 'switch',
value: true
},
type: {
title: 'type',
type: 'swSelect',
value: 'cubicIn'
}
}
}, {
script: 'wave2',
options: {
duration: {
title: 'duration',
type: 'inputNumber',
value: 500
},
autoPlay: {
title: 'autoPlay',
type: 'switch',
value: true
},
type: {
title: 'type',
type: 'swSelect',
value: 'cubicIn'
}
}
}]
\ No newline at end of file
...@@ -81,7 +81,9 @@ export default { ...@@ -81,7 +81,9 @@ export default {
once: v, once: v,
behaviors: _event.behaviors behaviors: _event.behaviors
}; };
this.$store.dispatch('updateNodeEvent', event); this.$store.dispatch('modifyActiveView', {
events: event
});
} }
}, },
/** /**
...@@ -92,7 +94,9 @@ export default { ...@@ -92,7 +94,9 @@ export default {
let event = {}; let event = {};
let currentEvent = this.eventsObj[this.currentEvent]; let currentEvent = this.eventsObj[this.currentEvent];
event[this.currentEvent] = Object.assign(currentEvent, { behaviors: v }); event[this.currentEvent] = Object.assign(currentEvent, { behaviors: v });
this.$store.dispatch('updateNodeEvent', event); this.$store.dispatch('modifyActiveView', {
events: event
});
this.updateEventsObj(); this.updateEventsObj();
} }
} }
......
<template> <template>
<div class="zero-inspector-props-form" v-if="activeComponent.uuid"> <div class="zero-inspector-props-form" v-if="activeComponent.uuid">
<el-form ref="form" size="mini" :model="form" label-width="80px">
<el-collapse v-model="configColl"> <el-collapse v-model="configColl">
<el-collapse-item title="配置" name="properties"> <el-collapse-item title="配置" name="properties">
<el-form ref="form" size="mini" :model="form" label-width="80px">
<el-form-item label="名称"> <el-form-item label="名称">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input> <el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
</el-form-item> </el-form-item>
...@@ -14,13 +14,23 @@ ...@@ -14,13 +14,23 @@
<template v-for="(p, key) in cmpProps"> <template v-for="(p, key) in cmpProps">
<el-form-item v-if="key !== 'groupName'" :id="activeComponent.uuid + '-' + key" :key="activeComponent.uuid + key" :label="p.title"> <el-form-item v-if="key !== 'groupName'" :id="activeComponent.uuid + '-' + key" :key="activeComponent.uuid + key" :label="p.title">
<!-- {{key}} --> <!-- {{key}} -->
<dynamic-component :label="key" :component-value="getDynamicValue(p, key)" :component-props="getDynamicProps(p)" :component-type="getDynamicCmpType(p)" @onChange="v => handlePropertiesChange(key, v)"></dynamic-component> <dynamic-component :component-value="getPropValue(p, key)" :component-props="getPropProps(p)" :component-type="getPropCmpType(p)" @onChange="v => handlePropertiesChange(key, v)"></dynamic-component>
</el-form-item> </el-form-item>
</template> </template>
</el-form>
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="脚本" name="scripts"> <el-collapse-item title="脚本" name="scripts">
<div style="text-align: center;"> <el-collapse accordion v-if="activeComponent.scripts && activeComponent.scripts.length">
<template v-for="(script, index) in activeComponent.scripts">
<el-collapse-item :title="getScriptName(script.script)" :key="script + index">
<template v-for="(p, key) in getScriptOptions(script.script)">
<el-form-item :key="activeComponent.uuid + index + key" :label="key">
<dynamic-component :component-value="getScriptValue(p, key, index)" :component-props="getScriptProps(p, index)" :component-type="getScriptType(p, index)" @onChange="v => handleScriptChange(index, key, v)"></dynamic-component>
</el-form-item>
</template>
</el-collapse-item>
</template>
</el-collapse>
<div style="padding-top: 15px;text-align: center;">
<el-popover <el-popover
placement="top" placement="top"
width="300" width="300"
...@@ -34,6 +44,7 @@ ...@@ -34,6 +44,7 @@
</div> </div>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
</el-form>
<!-- <div class="script-config-dialog" v-show="scriptDialog"> <!-- <div class="script-config-dialog" v-show="scriptDialog">
<el-tree :data="scripts" :props="defaultProps" @node-click="handleNodeClick"></el-tree> <el-tree :data="scripts" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
</div> --> </div> -->
...@@ -42,8 +53,8 @@ ...@@ -42,8 +53,8 @@
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import _ from 'lodash';
import { componentsMap, getCmpProps } from '../../../utils/common'; import { componentsMap, getCmpProps } from '../../../utils/common';
import scripts from '../../../utils/scripts';
import dynamicComponent from '../components/dynamicComponent'; import dynamicComponent from '../components/dynamicComponent';
const componentMapper = { const componentMapper = {
...@@ -87,38 +98,66 @@ const componentMapper = { ...@@ -87,38 +98,66 @@ const componentMapper = {
} }
}; };
const scriptTypeMap = { const scriptTypeMap = {
number: {
} component: 'el-input-number',
props: {
size: 'mini'
}
},
string: {
component: 'el-input'
},
boolean: {
component: 'el-switch',
props: {
activeColor: '#13ce66',
inactiveColor: '#eee',
width: 40
}
},
color: {
component: 'el-color-picker',
props: {
'show-alpha': true
}
},
select: {
component: 'el-select',
props: {
slotComponent: 'el-option'
}
}
};
export default { export default {
name: 'PropsTab', name: 'PropsTab',
components: { 'dynamic-component': dynamicComponent }, components: { 'dynamic-component': dynamicComponent },
data() { data() {
console.log('scripts', scripts);
return { return {
scripts,
componentsMap, componentsMap,
scriptDialog: false, scriptDialog: false,
form: { form: {
name: '', name: '',
type: '', type: '',
properties: {} properties: {},
scripts: []
}, },
defaultProps: { defaultProps: {
children: 'children', children: 'children',
label: 'script' label: 'name'
}, },
configColl: ['properties'] configColl: ['properties']
}; };
}, },
computed: { computed: {
...mapGetters(['activeComponent', 'componentList']), ...mapGetters(['activeComponent', 'activeComponentCopy', 'componentList']),
cmpProps: function() { cmpProps: function() {
// 获取properties.js中的默认配置 // 获取properties.js中的默认配置
return getCmpProps(this.activeComponent.type); return getCmpProps(this.activeComponent.type);
},
scripts: function() {
return this.$store.state.env.scripts;
} }
}, },
watch: { watch: {
...@@ -133,19 +172,20 @@ export default { ...@@ -133,19 +172,20 @@ export default {
}, },
methods: { methods: {
handleChange(label, v) { handleChange(label, v) {
this.$store.dispatch('modifyComponent', { let _view = {};
label: label, _view[label] = v;
value: v this.$store.dispatch('modifyActiveView', _view);
});
}, },
/** /**
* 脚本预设对象选中 * 脚本预设对象选中
*/ */
handleNodeClick(data, node, cmp) { handleNodeClick(script) {
console.log('handleNodeClick', data); console.log('handleNodeClick', script);
this.$store.dispatch('addNodeScript', script.id);
this.scriptDialog = false;
}, },
/** /**
* 属性栏发生改变 * 基础属性发生改变
*/ */
handlePropertiesChange(key, v) { handlePropertiesChange(key, v) {
console.log('handlePropertiesChange', key, v); console.log('handlePropertiesChange', key, v);
...@@ -154,31 +194,89 @@ export default { ...@@ -154,31 +194,89 @@ export default {
_prop[key] = v; _prop[key] = v;
this.$store.dispatch('modifyProperties', _prop); this.$store.dispatch('modifyProperties', _prop);
}, },
/**
* 事件属性发生改变
*/
handleScriptChange(index, key, v) {
let _options = {};
_options[key] = v;
let _scripts = _.cloneDeep(this.activeComponent.scripts);
let _script = _scripts[index];
_script.options = _.assign(_script.options, _options);
_scripts[index] = _script;
this.$store.dispatch('modifyActiveView', {
scripts: _scripts
});
},
/** /**
* 获取动态组件的类型 * 获取动态组件的类型
*/ */
getDynamicCmpType(item) { getPropCmpType(item) {
return componentMapper[item.type].component; return componentMapper[item.type].component;
}, },
/** /**
* 获取动态组件的属性v-bind * 获取动态组件的属性v-bind
*/ */
getDynamicProps(item) { getPropProps(item) {
let _cmp = componentMapper[item.type]; let _cmp = componentMapper[item.type];
return { return {
size: 'mini', size: 'mini',
...(_cmp.props || {}), ...(_cmp.props || {}),
...item.props, ...item.props,
options: item.options || {} options: item.options || {}
} };
}, },
/** /**
* 获取当前选中节点对应的属性的值 * 获取当前选中节点对应的属性的值
*/ */
getDynamicValue(item, key) { getPropValue(item, key) {
// debugger; let _properties = this.activeComponentCopy.properties;
let _properties = this.activeComponent.properties;
return _properties[key] || item.value; return _properties[key] || item.value;
},
getScriptValue(item, key, index) {
let _script = this.activeComponent.scripts[index];
// let result =
return _script.options[key] || item.default;
},
getScriptProps(item, index) {
let _type = item.type;
let _options = {};
if (_.isArray(_type)) {
// 如果脚本选项对应的类型是数组,说明是枚举类型
_options = item.type.map(i => {
return {
label: i,
value: i
};
});
_options.unshift({
label: '请选择',
value: ''
});
_type = 'select';
}
let _cmp = scriptTypeMap[_type];
return {
size: 'mini',
...(_cmp.props || {}),
options: _options
};
},
getScriptType(item, index) {
// 如果脚本选项对应的类型是数组,说明是枚举类型
let _type = _.isArray(item.type) ? 'select' : item.type;
return scriptTypeMap[_type].component;
},
getScriptName(id) {
console.log('getScriptName', id);
let _script = this.scripts.find(script => script.id === id);
return _script ? _script.name : '';
},
getScriptOptions(id) {
let _script = this.scripts.find(script => script.id === id);
return _script ? _script.options : {};
} }
} }
}; };
...@@ -195,7 +293,7 @@ export default { ...@@ -195,7 +293,7 @@ export default {
background-color: #e9e9e9; background-color: #e9e9e9;
} }
} }
.script-config-dialog{ .script-config-dialog {
height: 350px; height: 350px;
} }
</style> </style>
\ No newline at end of file
...@@ -19,8 +19,9 @@ ...@@ -19,8 +19,9 @@
:style="styleObject" :style="styleObject"
v-bind="position" v-bind="position"
@dragging="handleDragging" @dragging="handleDragging"
@dragstop="handleDragStop"
@resizing="handleResize" @resizing="handleResize"
@resizestop="resizeStop" @resizestop="handleResizeStop"
@deactivated="handleDeactivated" @deactivated="handleDeactivated"
> >
</vue-draggable-resizable> </vue-draggable-resizable>
...@@ -46,10 +47,10 @@ export default { ...@@ -46,10 +47,10 @@ export default {
if (!this.active || !this.activeComponentId) { if (!this.active || !this.activeComponentId) {
return false; return false;
} }
let _prop = this.activeComponent.properties; let _prop = this.activeComponentCopy.properties;
if (_prop.x !== x || _prop.y !== y || _prop.width !== w || _prop.height !== h) { if (_prop.x !== x || _prop.y !== y || _prop.width !== w || _prop.height !== h) {
this.$store.dispatch('modifyProperties', { this.$store.dispatch('modifyCopyProperties', {
x: x, x: x,
y: y, y: y,
width: w, width: w,
...@@ -63,32 +64,57 @@ export default { ...@@ -63,32 +64,57 @@ export default {
if (!this.active || !this.activeComponentId) { if (!this.active || !this.activeComponentId) {
return false; return false;
} }
let _prop = this.activeComponent.properties; let _prop = this.activeComponentCopy.properties;
if (_prop.x !== x || _prop.y !== y) { if (_prop.x !== x || _prop.y !== y) {
this.$store.dispatch('modifyProperties', { this.$store.dispatch('modifyCopyProperties', {
x: x, x: x,
y: y y: y
}); });
// console.log('handleDragging', x, y); // console.log('handleDragging', x, y);
} }
}, },
resizeStop(x, y, w, h) { handleResizeStop(x, y, w, h) {
// console.log('resizeStop', x, y, w, h); if (!this.active || !this.activeComponentId) {
return false;
}
let _prop = this.activeComponent.properties;
if (_prop.x !== x || _prop.y !== y || _prop.width !== w || _prop.height !== h) {
this.$store.dispatch('modifyProperties', {
x: x,
y: y,
width: w,
height: h
});
}
},
handleDragStop(x, y) {
if (!this.active || !this.activeComponentId) {
return false;
}
let _prop = this.activeComponent.properties;
if (_prop.x !== x || _prop.y !== y) {
this.$store.dispatch('modifyProperties', {
x: x,
y: y
});
}
} }
}, },
computed: { computed: {
...mapState(['project']), ...mapState(['project']),
...mapGetters(['activeComponent', 'componentList', 'activeComponentId']), ...mapGetters(['activeComponent', 'activeComponentCopy', 'componentList', 'activeComponentId']),
active() { active() {
return !!this.activeComponentId; return !!this.activeComponentId;
// return this.activeComponentId === (this.activeComponent || {}).uuid; // return this.activeComponentId === (this.activeComponent || {}).uuid;
}, },
styleObject() { styleObject() {
return styles.getComponentStyle(this.activeComponent, this.project, this.componentList, true); return styles.getComponentStyle(this.activeComponentCopy, this.project, this.componentList, true);
}, },
position() { position() {
let _props = this.activeComponent.properties || {}; let _props = this.activeComponentCopy.properties || {};
const _node = properties.node; const _node = properties.node;
// console.log('********', _props); // console.log('********', _props);
let result = { let result = {
...@@ -120,7 +146,7 @@ export default { ...@@ -120,7 +146,7 @@ export default {
width: 10px; width: 10px;
height: 10px; height: 10px;
font-size: 1px; font-size: 1px;
background: #EEE; background: #eee;
border: 1px solid #333; border: 1px solid #333;
} }
.handle-tl { .handle-tl {
......
<template> <template>
<!-- <vue-draggable-resizable
:minw="1"
:minh="1"
:z="2"
:style="styleObject"
:class="[active ? 'choosed-cmp' : 'unchoosed-cmp', isTyping && 'isTyping']"
v-bind="position"
@dragging="handleDragging"
@resizing="handleResize"
@deactivated="handleDeactivated"
>
<div
class="sword-compomnent-content-wrapper"
:contenteditable="false"
@dblclick="handleEnableInput"
@input="handleInput"
@keyup.delete.prevent="changeEditRange"
>
</div>
</vue-draggable-resizable> -->
<custom-node :custom-style="styleObject" :properties="componentData.properties"></custom-node> <custom-node :custom-style="styleObject" :properties="componentData.properties"></custom-node>
</template> </template>
<script> <script>
...@@ -43,14 +22,19 @@ export default { ...@@ -43,14 +22,19 @@ export default {
}, },
computed: { computed: {
...mapState(['project']), ...mapState(['project']),
...mapGetters(['componentList']), ...mapGetters(['activeComponentCopy', 'componentList']),
styleObject() { styleObject() {
return styles.getComponentStyle(this.componentData, this.project, this.componentList); let result = '';
if (this.componentData.uuid === this.activeComponentCopy.uuid) {
result = styles.getComponentStyle(this.activeComponentCopy, this.project, this.componentList);
} else {
result = styles.getComponentStyle(this.componentData, this.project, this.componentList);
}
return result;
} }
} }
}; };
</script> </script>
<style> <style>
</style> </style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment