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

feat: undo redo

parent e7b8cd9d
<template> <template>
<div class="menu"> <div class="menu">
<el-badge class="menu-item" v-for="(item, key) of data" :key="key" is-dot :hidden="!menuBadge(key)"> <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}} {{item.text}}
</el-link> </el-link>
</el-badge> </el-badge>
......
...@@ -36,9 +36,6 @@ export default { ...@@ -36,9 +36,6 @@ export default {
swvalue: this.value || 0 swvalue: this.value || 0
}; };
}, },
mounted() {
console.log('slider created', this.value);
},
watch: { watch: {
swvalue(v) { swvalue(v) {
this.$emit('change', v); this.$emit('change', v);
......
<template> <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> </template>
<style> <style>
</style> </style>
...@@ -29,6 +37,21 @@ export default { ...@@ -29,6 +37,21 @@ export default {
dragOver(e) { dragOver(e) {
e.preventDefault(); 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> </script>
...@@ -66,7 +66,9 @@ ...@@ -66,7 +66,9 @@
"preview": "Preview", "preview": "Preview",
"publish": "Publish", "publish": "Publish",
"data-mapping": "DataMapping", "data-mapping": "DataMapping",
"exit": "Exit" "exit": "Exit",
"undo": "Undo",
"redo": "Redo"
}, },
"view_node_menu": { "view_node_menu": {
"node": "Node", "node": "Node",
......
...@@ -8,7 +8,7 @@ import { projectApi } from "../../api"; ...@@ -8,7 +8,7 @@ import { projectApi } from "../../api";
// import { compoleteComponentData } from '../../utils/compoleteCmpData'; // import { compoleteComponentData } from '../../utils/compoleteCmpData';
import path from "path"; import path from "path";
import generateUUID from "uuid/v4"; import generateUUID from "uuid/v4";
import { getCmpProps } from '../../utils/common'; import { getCmpProps, flattenViews, getCmpByUUID } from '../../utils/common';
import { saveAs } from "../../utils"; import { saveAs } from "../../utils";
export const projectStore = { export const projectStore = {
...@@ -26,6 +26,8 @@ export const projectStore = { ...@@ -26,6 +26,8 @@ export const projectStore = {
activeViews: '', activeViews: '',
dragUUID: '', dragUUID: '',
dirty: false, dirty: false,
operateStack: [],
stackIndex: 0
}, },
mutations: { mutations: {
setDirty(state, dirty = true) { setDirty(state, dirty = true) {
...@@ -62,23 +64,39 @@ export const projectStore = { ...@@ -62,23 +64,39 @@ export const projectStore = {
state.activeComponent.properties = state.activeComponent.properties || {}; state.activeComponent.properties = state.activeComponent.properties || {};
console.log('mutations activeComponent', state); console.log('mutations activeComponent', state);
} }
// state.activeComponent = item;
}, },
/** /**
* 修改当前组件的属性 * 修改当前组件的属性
* @param {*} state * @param {*} state
* @param {*} data * @param {*} data
*/ */
modifyProperties(state, data) { modifyProperties(state, props) {
if (!data || !data.label) { if (!props) {
return; return;
} }
let _prop = _.cloneDeep(state.activeComponent.properties || {}); let _prop = _.cloneDeep(state.activeComponent.properties || {});
_prop[data.label] = data.value; if (state.operateStack.length) {
// state.activeComponent.properties[data.label] = data.value; // 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); 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 = { ...@@ -112,6 +130,28 @@ export const projectStore = {
console.log('assetDragStart', data); console.log('assetDragStart', data);
state.dragUUID = data.uuid; 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, ) { modifyProject(state, ) {
}, },
...@@ -205,13 +245,6 @@ export const projectStore = { ...@@ -205,13 +245,6 @@ export const projectStore = {
* 当前激活的组件 * 当前激活的组件
*/ */
activeComponent: state => { activeComponent: state => {
// if (state.activeComponent) {
// let _cmp = state.activeComponent || {}
// console.log('activeComponent', _cmp);
// return { ..._cmp, uuid: _cmp.uuid || generateUUID() }
// } else {
// return {};
// }
return state.activeComponent; return state.activeComponent;
}, },
/** /**
...@@ -224,21 +257,6 @@ export const projectStore = { ...@@ -224,21 +257,6 @@ export const projectStore = {
* 扁平化所有节点 * 扁平化所有节点
*/ */
componentList: state => { 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]) : []; let _view = state.data.views.length ? [].concat(state.data.views[0]) : [];
...@@ -246,7 +264,7 @@ export const projectStore = { ...@@ -246,7 +264,7 @@ export const projectStore = {
_view = state.data.views.filter(v => v.uuid === state.activeViews) _view = state.data.views.filter(v => v.uuid === state.activeViews)
} }
let result = flatten(_.cloneDeep(_view)); let result = flattenViews(_.cloneDeep(_view));
console.log('componentList', result); console.log('componentList', result);
return result; return result;
} }
...@@ -296,7 +314,8 @@ export const projectStore = { ...@@ -296,7 +314,8 @@ export const projectStore = {
* @param {*} param * @param {*} param
* @param {*} data * @param {*} data
*/ */
activeComponent({ commit, state }, data) { activeComponent(context, data) {
// debugger;
let getTopView = node => { let getTopView = node => {
if (node.parent && !node.parent.parent) { if (node.parent && !node.parent.parent) {
return node; return node;
...@@ -307,20 +326,24 @@ export const projectStore = { ...@@ -307,20 +326,24 @@ export const projectStore = {
let _view = getTopView(data.node); let _view = getTopView(data.node);
if (_view && _view.data) { 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; let _props = state.activeComponent.properties;
if (!_props.width || !_props.height) { if (!_props.width || !_props.height) {
let _url = data.value; let _url = props['source']; //_source.value;
if (_url.indexOf('asset://') === 0) { if (_url.indexOf('asset://') === 0) {
let uuid = _url.split('//')[1]; let uuid = _url.split('//')[1];
let asset = state.data.assets.find(a => a.uuid === uuid); let asset = state.data.assets.find(a => a.uuid === uuid);
...@@ -329,12 +352,15 @@ export const projectStore = { ...@@ -329,12 +352,15 @@ 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', { label: 'width', value: _img.width }); commit('modifyProperties', {
commit('modifyProperties', { label: 'height', value: _img.height }); width: _img.width,
height: _img.height
});
} }
} }
} }
commit('modifyProperties', data)
commit('modifyProperties', props)
}, },
/** /**
* 修改组件 * 修改组件
...@@ -349,6 +375,6 @@ export const projectStore = { ...@@ -349,6 +375,6 @@ export const projectStore = {
zip.generateAsync({ type: "blob" }).then(function (content) { zip.generateAsync({ type: "blob" }).then(function (content) {
saveAs(content, `view-${view.name}.zrv`); saveAs(content, `view-${view.name}.zrv`);
}); });
}, }
}, },
}; };
...@@ -80,22 +80,38 @@ function getParentCmps(uuid, list) { ...@@ -80,22 +80,38 @@ function getParentCmps(uuid, list) {
export { getParentCmps, completeSelfProps }; export { getParentCmps, completeSelfProps };
/** export const getCmpByUUID = function (uuid, views) {
* 移除组件从父级继承的属性 if (!uuid) {
* @param {*} cmp return null;
* @param {*} list }
*/ let _node = views.find(v => v.uuid === uuid);
export const removeParentProps = function(cmp, list) { 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 {*} views
* @param {*} list
*/ */
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 = { ...@@ -217,7 +233,7 @@ export const styles = {
}); });
result += `background-position: center;background-size: 100% 100%;` result += `background-position: center;background-size: 100% 100%;`
// console.log('getComponentStyle', result); console.log('getComponentStyle', result);
return result; return result;
} }
} }
......
...@@ -157,6 +157,12 @@ ...@@ -157,6 +157,12 @@
case 'details': case 'details':
this.$refs.projectDialogsDialog.show(); this.$refs.projectDialogsDialog.show();
break; break;
case 'undo':
this.$store.commit('undoRedo', 1);
break;
case 'redo':
this.$store.commit('undoRedo', -1);
break;
case 'data-mapping': case 'data-mapping':
this.$refs.dataMappingDialog.show(); this.$refs.dataMappingDialog.show();
break; break;
......
...@@ -18,19 +18,26 @@ ...@@ -18,19 +18,26 @@
name: "ToolBar", name: "ToolBar",
components: {SampleMenu}, components: {SampleMenu},
data() { data() {
const menuConfig = this.$t('menu'); return {}
const menu = {};
for (let item of Object.keys(menuConfig)) {
menu[item] = {text: menuConfig[item]}
}
return {
menu,
}
}, },
computed: { computed: {
...mapState([ ...mapState([
'project' '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: { methods: {
clickMenu(menuItem) { clickMenu(menuItem) {
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
</style> </style>
<script> <script>
import { mapState, mapActions, mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import wrapper from './wrapper'; import wrapper from './wrapper';
export default { export default {
......
...@@ -137,10 +137,9 @@ export default { ...@@ -137,10 +137,9 @@ export default {
methods: { methods: {
handleChange(v) { handleChange(v) {
console.log('handleChange', v); console.log('handleChange', v);
this.$store.dispatch('modifyProperties', { let _prop = {};
label: this.label, _prop[this.label] = v;
value: v this.$store.dispatch('modifyProperties', _prop);
});
}, },
handleInput(v) { handleInput(v) {
if (this.item.type === 'input') { if (this.item.type === 'input') {
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
import { mapState, mapGetters } from 'vuex'; import { mapState, mapGetters } from 'vuex';
import { styles, getParentCmps } from '../../../utils/common'; import { styles, getParentCmps } from '../../../utils/common';
import properties from '../../../utils/properties'; import properties from '../../../utils/properties';
import customNode from '../../../components/customElement/node/index.vue' import customNode from '../../../components/customElement/node/index.vue';
export default { export default {
props: { props: {
...@@ -44,7 +44,7 @@ export default { ...@@ -44,7 +44,7 @@ export default {
return {}; return {};
}, },
components: { components: {
"custom-node": customNode 'custom-node': customNode
}, },
methods: { methods: {
handleEnableInput() { handleEnableInput() {
...@@ -72,75 +72,38 @@ export default { ...@@ -72,75 +72,38 @@ export default {
// id: this.cmpId // id: this.cmpId
// }); // });
}, },
handleResize(left, top, w, h) { handleResize(x, y, w, h) {
const id = this.cmpId; 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) { if (!this.active) {
return false; return false;
} }
console.log('handleDragging', left, top); let _prop = this.componentData.properties;
// console.log('getParentCmps', getParentCmps);
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: { computed: {
...@@ -162,26 +125,15 @@ export default { ...@@ -162,26 +125,15 @@ export default {
position() { position() {
let _props = this.componentData.properties || {}; let _props = this.componentData.properties || {};
const _node = properties.node; const _node = properties.node;
console.log('********', _props); // console.log('********', _props);
let result = { let result = {
x: _props.x || _node.x.value, x: _props.x || _node.x.value,
y: _props.y || _node.y.value, y: _props.y || _node.y.value,
w: _props.width || _node.width.value, w: _props.width || _node.width.value,
h: _props.height || _node.height.value h: _props.height || _node.height.value
} };
console.log('####position', result); console.log('####position', result);
// if (this.active) {
// return result;
// } else {
// return {
// x: 30,
// y: 30,
// w: 40,
// h: 40
// }
// }
return result; return result;
} }
}, },
...@@ -204,6 +156,9 @@ export default { ...@@ -204,6 +156,9 @@ export default {
.vdr.active { .vdr.active {
border-color: transparent; border-color: transparent;
} }
.vdr.active.unchoosed-cmp {
border: none;
}
.vdr.choosed-cmp { .vdr.choosed-cmp {
border: 1px solid rgba(157, 172, 255, 0.9); border: 1px solid rgba(157, 172, 255, 0.9);
cursor: move; 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