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

feat: undo redo

parent e7b8cd9d
<template>
<div class="menu">
<el-badge class="menu-item" v-for="(item, key) of data" :key="key" is-dot :hidden="!menuBadge(key)">
<el-link @click="clickItem(key)" :icon="item.icon">
<el-link @click="clickItem(key)" :icon="item.icon" :disabled="item.disabled">
{{item.text}}
</el-link>
</el-badge>
......
......@@ -36,9 +36,6 @@ export default {
swvalue: this.value || 0
};
},
mounted() {
console.log('slider created', this.value);
},
watch: {
swvalue(v) {
this.$emit('change', v);
......
<template>
<el-input v-model="swvalue" @drop.native="drop" @dragover.native="dragOver"></el-input>
<el-popover
placement="top"
trigger="hover"
width="auto"
:content="url"
>
<img v-if="url" :src="url" alt="" />
<el-input v-model="swvalue" slot="reference" @drop.native="drop" @dragover.native="dragOver"></el-input>
</el-popover>
</template>
<style>
</style>
......@@ -29,6 +37,21 @@ export default {
dragOver(e) {
e.preventDefault();
}
},
computed: {
url: function() {
if (this.swvalue) {
if (this.swvalue.indexOf('asset://') > -1) {
let uuid = this.swvalue.split('//')[1];
let _ass = this.$store.state.project.data.assets.find(a => a.uuid === uuid);
return _ass ? _ass.url : '';
} else {
return this.swvalue;
}
} else {
return '';
}
}
}
};
</script>
......@@ -66,7 +66,9 @@
"preview": "Preview",
"publish": "Publish",
"data-mapping": "DataMapping",
"exit": "Exit"
"exit": "Exit",
"undo": "Undo",
"redo": "Redo"
},
"view_node_menu": {
"node": "Node",
......
......@@ -8,7 +8,7 @@ import { projectApi } from "../../api";
// import { compoleteComponentData } from '../../utils/compoleteCmpData';
import path from "path";
import generateUUID from "uuid/v4";
import { getCmpProps } from '../../utils/common';
import { getCmpProps, flattenViews, getCmpByUUID } from '../../utils/common';
import { saveAs } from "../../utils";
export const projectStore = {
......@@ -26,6 +26,8 @@ export const projectStore = {
activeViews: '',
dragUUID: '',
dirty: false,
operateStack: [],
stackIndex: 0
},
mutations: {
setDirty(state, dirty = true) {
......@@ -62,23 +64,39 @@ export const projectStore = {
state.activeComponent.properties = state.activeComponent.properties || {};
console.log('mutations activeComponent', state);
}
// state.activeComponent = item;
},
/**
* 修改当前组件的属性
* @param {*} state
* @param {*} data
*/
modifyProperties(state, data) {
if (!data || !data.label) {
modifyProperties(state, props) {
if (!props) {
return;
}
let _prop = _.cloneDeep(state.activeComponent.properties || {});
_prop[data.label] = data.value;
// state.activeComponent.properties[data.label] = data.value;
if (state.operateStack.length) {
// state.operateStack.shift();
if (state.stackIndex !== 0) {
state.operateStack = state.operateStack.slice(state.stackIndex);
}
} else {
state.operateStack.unshift({
uuid: state.activeComponent.uuid,
properties: _.cloneDeep(_prop)
});
}
state.stackIndex = 0; // 开始编辑的时候,重置操作栈的下标
_prop = Object.assign({}, _prop, props);
state.operateStack.unshift({
uuid: state.activeComponent.uuid,
properties: _.cloneDeep(_prop)
});
Vue.set(state.activeComponent, 'properties', _prop);
console.log('modifyProperties', state.activeComponent);
console.log('operateStack', state.operateStack);
// console.log('modifyProperties', state.activeComponent);
},
/**
* 修改当前组件
......@@ -112,6 +130,28 @@ export const projectStore = {
console.log('assetDragStart', data);
state.dragUUID = data.uuid;
},
/**
* 重做/撤销
* @param {*} state
* @param {*} step
*/
undoRedo(state, step) {
let _beforeStack = state.operateStack[state.stackIndex];
state.stackIndex += step;
let _afterStack = state.operateStack[state.stackIndex];
if (_beforeStack.uuid !== _afterStack.uuid) {
// 如果uuid不一样,说明是不同节点的操作,需要把下标多移动一位
state.stackIndex += step;
}
console.log('undoRedo', state.stackIndex);
let _operate = state.operateStack[state.stackIndex];
if (_operate) {
let _cmp = getCmpByUUID(_operate.uuid, state.data.views);
if (_cmp) {
_cmp.properties = _operate.properties;
}
}
},
modifyProject(state, ) {
},
......@@ -205,13 +245,6 @@ export const projectStore = {
* 当前激活的组件
*/
activeComponent: state => {
// if (state.activeComponent) {
// let _cmp = state.activeComponent || {}
// console.log('activeComponent', _cmp);
// return { ..._cmp, uuid: _cmp.uuid || generateUUID() }
// } else {
// return {};
// }
return state.activeComponent;
},
/**
......@@ -224,21 +257,6 @@ export const projectStore = {
* 扁平化所有节点
*/
componentList: state => {
const flatten = arr => {
return arr.reduce((flat, toFlat) => {
if (toFlat.children) {
let _children = toFlat.children.map(c => {
c.parent = toFlat.uuid;
return c;
});
return flat.concat([toFlat].concat(flatten(_children)));
} else {
return flat.concat([toFlat]);
}
// return flat.concat(toFlat.children ? flatten(toFlat.children).concat([toFlat]) : [toFlat]);
}, []);
};
// 如果有选中的节点,则展示对应的视图组
// 否则展示第一个视图组
let _view = state.data.views.length ? [].concat(state.data.views[0]) : [];
......@@ -246,7 +264,7 @@ export const projectStore = {
_view = state.data.views.filter(v => v.uuid === state.activeViews)
}
let result = flatten(_.cloneDeep(_view));
let result = flattenViews(_.cloneDeep(_view));
console.log('componentList', result);
return result;
}
......@@ -296,7 +314,8 @@ export const projectStore = {
* @param {*} param
* @param {*} data
*/
activeComponent({ commit, state }, data) {
activeComponent(context, data) {
// debugger;
let getTopView = node => {
if (node.parent && !node.parent.parent) {
return node;
......@@ -307,20 +326,24 @@ export const projectStore = {
let _view = getTopView(data.node);
if (_view && _view.data) {
state.activeViews = _view.data.uuid;
context.state.activeViews = _view.data.uuid;
}
commit('activeComponent', data.data);
// let _cmp = context.getters.componentList.find(c => c.uuid === data.data.uuid)
context.commit('activeComponent', data.data);
},
/**
* 修改属性
*/
modifyProperties({ commit, state }, data) {
modifyProperties({ commit, state }, props) {
// debugger;
// 如果当前修改的是“来源”属性,节点又没有高度宽度,则取图片的高度宽度
if (data.label === 'source') {
let _source = Object.keys(props).indexOf('source') > -1;
if (_source) {
let _props = state.activeComponent.properties;
if (!_props.width || !_props.height) {
let _url = data.value;
let _url = props['source']; //_source.value;
if (_url.indexOf('asset://') === 0) {
let uuid = _url.split('//')[1];
let asset = state.data.assets.find(a => a.uuid === uuid);
......@@ -329,12 +352,15 @@ export const projectStore = {
let _img = new Image();
_img.src = _url;
_img.onload = function () {
commit('modifyProperties', { label: 'width', value: _img.width });
commit('modifyProperties', { label: 'height', value: _img.height });
commit('modifyProperties', {
width: _img.width,
height: _img.height
});
}
}
}
commit('modifyProperties', data)
commit('modifyProperties', props)
},
/**
* 修改组件
......@@ -349,6 +375,6 @@ export const projectStore = {
zip.generateAsync({ type: "blob" }).then(function (content) {
saveAs(content, `view-${view.name}.zrv`);
});
},
}
},
};
......@@ -80,22 +80,38 @@ function getParentCmps(uuid, list) {
export { getParentCmps, completeSelfProps };
/**
* 移除组件从父级继承的属性
* @param {*} cmp
* @param {*} list
*/
export const removeParentProps = function(cmp, list) {
export const getCmpByUUID = function (uuid, views) {
if (!uuid) {
return null;
}
let _node = views.find(v => v.uuid === uuid);
if (_node) {
return _node
} else {
for (let index = 0; index < views.length; index++) {
if (views[index].children && views[index].children.length) {
return getCmpByUUID(uuid, views[index].children)
}
}
}
}
/**
* 根据
* @param {*} cmp
* @param {*} list
* 扁平化视图数组
* @param {*} views
*/
export const buildPropsByParent = function(cmp, list) {
export const flattenViews = function (views) {
return views.reduce((flat, toFlat) => {
if (toFlat.children) {
let _children = toFlat.children.map(c => {
c.parent = toFlat.uuid;
return c;
});
return flat.concat([toFlat].concat(flattenViews(_children)));
} else {
return flat.concat([toFlat]);
}
}, []);
}
/**
......@@ -217,7 +233,7 @@ export const styles = {
});
result += `background-position: center;background-size: 100% 100%;`
// console.log('getComponentStyle', result);
console.log('getComponentStyle', result);
return result;
}
}
......
......@@ -157,6 +157,12 @@
case 'details':
this.$refs.projectDialogsDialog.show();
break;
case 'undo':
this.$store.commit('undoRedo', 1);
break;
case 'redo':
this.$store.commit('undoRedo', -1);
break;
case 'data-mapping':
this.$refs.dataMappingDialog.show();
break;
......
......@@ -18,19 +18,26 @@
name: "ToolBar",
components: {SampleMenu},
data() {
const menuConfig = this.$t('menu');
const menu = {};
for (let item of Object.keys(menuConfig)) {
menu[item] = {text: menuConfig[item]}
}
return {
menu,
}
return {}
},
computed: {
...mapState([
'project'
])
]),
menu: function() {
const menuConfig = this.$t('menu');
let menu = {};
for (let item of Object.keys(menuConfig)) {
menu[item] = {text: menuConfig[item], disabled: false}
}
if (menu['undo']) {
menu['undo']['disabled'] = !this.project.operateStack.length || this.project.operateStack.length === this.project.stackIndex + 1
}
if (menu['redo']) {
menu['redo']['disabled'] = this.project.stackIndex === 0;
}
return menu;
}
},
methods: {
clickMenu(menuItem) {
......
......@@ -17,7 +17,7 @@
</style>
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { mapGetters } from 'vuex';
import wrapper from './wrapper';
export default {
......
......@@ -137,10 +137,9 @@ export default {
methods: {
handleChange(v) {
console.log('handleChange', v);
this.$store.dispatch('modifyProperties', {
label: this.label,
value: v
});
let _prop = {};
_prop[this.label] = v;
this.$store.dispatch('modifyProperties', _prop);
},
handleInput(v) {
if (this.item.type === 'input') {
......
......@@ -28,7 +28,7 @@
import { mapState, mapGetters } from 'vuex';
import { styles, getParentCmps } from '../../../utils/common';
import properties from '../../../utils/properties';
import customNode from '../../../components/customElement/node/index.vue'
import customNode from '../../../components/customElement/node/index.vue';
export default {
props: {
......@@ -44,7 +44,7 @@ export default {
return {};
},
components: {
"custom-node": customNode
'custom-node': customNode
},
methods: {
handleEnableInput() {
......@@ -72,75 +72,38 @@ export default {
// id: this.cmpId
// });
},
handleResize(left, top, w, h) {
const id = this.cmpId;
handleResize(x, y, w, h) {
if (!this.active) {
return false;
}
let _prop = this.componentData.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
});
console.log('handleResize', x, y, w, h);
}
// this.$store.dispatch('batchModifyComponent', [
// {
// key: ['bindings', 'outline', 'position', 'top', 'v'],
// value: top,
// id
// },
// {
// key: ['bindings', 'outline', 'position', 'left', 'v'],
// value: left,
// id
// },
// {
// key: ['bindings', 'outline', 'position', 'width', 'v'],
// value: w,
// id
// },
// {
// key: ['bindings', 'outline', 'position', 'height', 'v'],
// value: h,
// id
// }
// ]);
},
handleDragging(left, top) {
handleDragging(x, y) {
if (!this.active) {
return false;
}
console.log('handleDragging', left, top);
// console.log('getParentCmps', getParentCmps);
let _prop = this.componentData.properties;
if (_prop.x !== x || _prop.y !== y) {
this.$store.dispatch('modifyProperties', {
x: x,
y: y
});
console.log('handleDragging', x, y);
}
// 文本编辑状态 与 位置锁定状态无法拖动
// if (
// this.isTyping ||
// path(
// ['bindings', 'outline', 'position', 'fixed', 'v'],
// this.componentData
// )
// ) {
// return -1;
// }
// const leftKeys = ['bindings', 'outline', 'position', 'left', 'v'];
// const topKeys = ['bindings', 'outline', 'position', 'top', 'v'];
// if (this.$store.state.activeIdList.length > 1) {
// // 多组件移动
// this.$store.dispatch('modifyComponentListPosition', {
// leftDistance: left - path(leftKeys, this.componentData),
// topDistance: top - path(topKeys, this.componentData)
// });
// } else {
// // console.log('active component change---------');
// // 单组件移动
// const id = this.cmpId;
// this.$store.dispatch('activeComponent', id);
// this.$store.dispatch('modifyComponent', {
// key: leftKeys,
// value: left,
// id
// });
// this.$store.dispatch('modifyComponent', {
// key: topKeys,
// value: top,
// id
// });
// }
// return 0;
}
},
computed: {
......@@ -162,26 +125,15 @@ export default {
position() {
let _props = this.componentData.properties || {};
const _node = properties.node;
console.log('********', _props);
// console.log('********', _props);
let result = {
x: _props.x || _node.x.value,
y: _props.y || _node.y.value,
w: _props.width || _node.width.value,
h: _props.height || _node.height.value
}
};
console.log('####position', result);
// if (this.active) {
// return result;
// } else {
// return {
// x: 30,
// y: 30,
// w: 40,
// h: 40
// }
// }
return result;
}
},
......@@ -204,6 +156,9 @@ export default {
.vdr.active {
border-color: transparent;
}
.vdr.active.unchoosed-cmp {
border: none;
}
.vdr.choosed-cmp {
border: 1px solid rgba(157, 172, 255, 0.9);
cursor: move;
......
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