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

Merge branch 'dev' into feature/20190923-feature

parents 98853578 fb91d1d2
{
"success": true,
"data": {
"name": "Zeroing Editor",
"version": "1.0.0",
"templates": {
"builtin": ["blank"],
"custom": []
},
"components": [
],
"processes": [
]
}
}
\ No newline at end of file
const data = {
success: true,
data: {
name: "Zeroing Editor",
version: "1.0.0",
templates: {
builtin: ["blank"],
custom: []
},
processes: [
{
id: 'entry',
name: 'Entry',
group: 'base',
options: {},
output: ['success'],
},
{
id: 'wait',
name: 'Wait',
group: 'base',
options: {
duration: {type: 'number', default: 1000},
},
output: ['complete'],
},
{
id: 'test',
name: 'Test',
group: 'test',
options: {
duration: {type: 'number', default: 1000},
},
output: ['complete'],
},
{
id: 'prefab1',
name: 'Prefab1',
isPrefab: true,
sub: {
1: {
uuid: '1',
alias: '入口',
meta: 'entry',
output: {
success: ['2'],
},
},
2: {
uuid: '2',
meta: 'wait',
alias: '等待',
options: {
duration: 500,
},
},
}
}
],
scripts: [
{
id: 'wave',
name: 'Wave',
options: {
duration: {type: 'number', default: 1000},
type: {type: 'string', default: 'yoyo'},
ease: {type: 'string', default: 'linear'},
autoPlay: {type: 'boolean', default: false},
}
}
]
},
};
module.exports = function () {
return data;
};
......@@ -4,59 +4,7 @@
const data = {
"views": [
{
"name": "view1",
"children": [
{
name: 'node1',
type: 'node',
uuid: 'aaa',
properties: {
x: 0,
y: 0,
width: 0,
height: 0,
rotation: 0,
scaleX: 1,
scaleY: 1,
alpha: 1,
visible: true,
}
},
{
name: 'node2',
type: 'label',
uuid: 'bbb',
properties: {
text: '',
color: 'black',
size: 12,
align: 'left',
}
},
{
"name": "node3",
type: 'image',
uuid: 'ccc',
properties: {
source: '',
}
},
{
"name": "node4",
type: 'rect',
uuid: 'ddd',
properties: {
fillColor: 'white',
strokeColor: 'black',
strokeWidth: 1,
}
},
]
},
{
"name": "view2"
}
],
"assets": [
{
......
......@@ -21,6 +21,7 @@
"Project does not exist": "Project does not exist",
"Project name": "Project name",
"Data mapping": "Data mapping",
"Behavior Editor": "Behavior Editor",
"Template": "Template",
"Preparing": "Preparing…",
"Deleting": "Deleting…",
......@@ -36,6 +37,7 @@
"Duplicate project success": "Duplicate project success",
"Project name cannot be empty": "Project name cannot be empty",
"This action will permanently delete project": "This action will permanently delete project[{projectName}], continue?",
"Fetching projects": "Fetching projects…",
"Failed to fetch env": "Failed to fetch env",
"Failed to fetch projects": "Failed to fetch projects",
"Failed to fetch project": "Failed to fetch project",
......
......@@ -8,24 +8,52 @@ import {envApi} from "../../api";
export const envStore = {
state: {
initialized: false,
name: 'Zeroing Editor',
version: '1.0.0',
templates: {
builtin: ['blank'],
custom: [],
},
components: [],
processes: [],
scripts: [],
},
mutations: {
updateEnv(state, env) {
Object.assign(state, env);
state.initialized = true;
},
},
getters: {
prefabProcessTree: state => {
return groupProcesses(state.processes, process => process.isPrefab);
},
normalProcessTree: state => {
return groupProcesses(state.processes, process => !process.isPrefab);
}
},
actions: {
async updateEnv({commit}) {
const env = await envApi.fetchEnv();
commit('updateEnv', env);
async updateEnv({state, commit}) {
if(!state.initialized){
const env = await envApi.fetchEnv();
commit('updateEnv', env);
}
}
}
};
function groupProcesses(processes, filterFunc) {
const result = [];
processes
.filter(filterFunc)
.forEach(process => {
const groupName = process.group || 'others';
let group = result.find(group => group.name === groupName);
if (!group) {
group = {name: groupName, children: []};
result.push(group);
}
group.children.push(process);
});
return result;
}
......@@ -22,3 +22,8 @@ html, body, #app {
background-color: rgba(0,0,0,0.7);
z-index: 2000;
}
.full-size{
width: 100%;
height: 100%;
}
......@@ -2,136 +2,152 @@
$dock-point-width: 9px;
.behavior {
width: 100%;
height: 100%;
.svg-board {
width: 100%;
height: 100%;
.behavior-editor-dialog {
display: flex;
flex-direction: column;
.line {
stroke: #979797;
.el-dialog__body {
flex: 1;
padding: 5px;
}
&.hover, &:hover {
stroke: $--color-primary;
stroke-dasharray: 5, 1;
}
.process-pane {
}
}
.behavior {
border: 1px solid $--border-color-base;
.node {
display: flex;
flex-direction: column;
min-width: 100px;
.background{
background-color: $--background-color-base;
border: 1px solid $block-border-blur-background-color;
position: relative;
border-radius: 5px;
outline: none;
user-select: none;
margin: 0 $dock-point-width;
&:hover {
border-color: $block-border-hover-background-color;
& > .header {
background-color: $block-border-hover-background-color;
}
}
&:focus {
border-color: $block-border-focus-background-color;
}
& > .header {
background-color: $block-border-focus-background-color;
}
}
.board {
.svg-board {
.line {
stroke: #979797;
.header {
min-height: 12px;
background-color: $block-border-blur-background-color;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
padding: 3px;
font-size: 12px;
color: white;
}
&.hover, &:hover {
stroke: $--color-primary;
stroke-dasharray: 5, 1;
}
.body {
display: flex;
flex-direction: column;
padding: 3px;
font-size: 12px;
color: $--color-text-primary;
.field-item {
}
.node {
display: flex;
flex-direction: column;
min-width: 100px;
background-color: $--background-color-base;
border: 1px solid $block-border-blur-background-color;
position: relative;
border-radius: 5px;
outline: none;
user-select: none;
margin: 0 $dock-point-width;
.key {
flex: 1;
width: 0;
overflow: hidden;
}
&:hover {
border-color: $block-border-hover-background-color;
.value {
flex: 1;
text-align: right;
& > .header {
background-color: $block-border-hover-background-color;
}
}
}
}
&:focus {
border-color: $block-border-focus-background-color;
.dock {
position: absolute;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
& > .header {
background-color: $block-border-focus-background-color;
}
}
.point {
border: 1px solid $block-border-blur-background-color;
padding: 1px;
margin-bottom: 5px;
background-color: white;
div {
width: 3px;
height: 3px;
border: 1px solid $block-border-blur-background-color;
border-radius: 3px;
.header {
min-height: 12px;
background-color: $block-border-blur-background-color;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
padding: 3px;
font-size: 12px;
color: white;
}
&:hover {
border-color: $--color-primary;
.body {
display: flex;
flex-direction: column;
padding: 3px;
font-size: 12px;
color: $--color-text-primary;
.field-item {
display: flex;
.key {
flex: 1;
width: 0;
overflow: hidden;
}
.value {
flex: 1;
text-align: right;
}
}
}
& > div {
border-color: $--color-primary;
.dock {
position: absolute;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
.point {
border: 1px solid $block-border-blur-background-color;
padding: 1px;
margin-bottom: 5px;
background-color: white;
div {
width: 3px;
height: 3px;
border: 1px solid $block-border-blur-background-color;
border-radius: 3px;
}
&:hover {
border-color: $--color-primary;
& > div {
border-color: $--color-primary;
}
}
&:last-child {
margin-bottom: 0;
}
}
}
&:last-child {
margin-bottom: 0;
.input {
@extend .dock;
left: -$dock-point-width;
}
.output {
@extend .dock;
right: -$dock-point-width;
}
}
}
}
.input {
@extend .dock;
left: -$dock-point-width;
}
.properties {
.output {
@extend .dock;
right: -$dock-point-width;
}
}
}
.properties {
width: 100%;
height: 100%;
background-color: #5396da;
}
}
}
\ No newline at end of file
.zero-inspector-form{
.el-input-number.el-input-number--mini,.el-select.el-select--mini{
width: 100%;
}
.el-slider.zero-slider {
width: 180px;
}
.zero-slider .el-slider__runway.show-input {
width: 100px;
}
.zero-slider > .el-slider__input {
width: 60px;
}
}
\ No newline at end of file
.inspector-tabs {
flex: 1;
border: 0 !important;
.el-tabs__item {
height: 25px;
line-height: 25px;
}
.zero-inspector-form {
.el-input-number.el-input-number--mini, .el-select.el-select--mini {
width: 100%;
}
.el-slider.zero-slider {
width: 180px;
}
.zero-slider .el-slider__runway.show-input {
width: 100px;
}
.zero-slider > .el-slider__input {
width: 60px;
}
}
}
......@@ -60,10 +60,31 @@
computed: {
...mapGetters([]),
},
mounted() {
async mounted() {
document.addEventListener('keydown', this.onKeyPress);
await playWaiting(this.prepare(), this.$t('Preparing')).catch(e => {});
this.loadProject();
},
destroyed(){
document.removeEventListener('keydown', this.onKeyPress)
},
methods: {
prepare(){
return Promise.all([
this.updateEnv(),
])
},
onKeyPress(e){
if(e.key === 's' && (e.ctrlKey||e.metaKey)){
e.preventDefault();
this.saveProject();
return false;
}
},
async loadProject(){
const {projectID} = this.$route.params;
if (await this.localVersionExist(projectID)) {
......@@ -170,6 +191,7 @@
'loadFromRemote',
"saveToLocal",
"saveToRemote",
'updateEnv',
])
}
}
......
<template>
<div
v-show="visible"
v-if="visible"
class="modal-layer">
<transition
name="el-fade-in"
......@@ -12,12 +12,13 @@
<div class="wrapper">
<el-button class="close-button" size="mini" circle icon="el-icon-close" @click="hide"></el-button>
<div class="name-bar">
<el-input v-for="(field, index) in showFields" :key="index" size="mini" class="item" v-model="file[field]" readonly>
<el-input v-for="(field, index) in showFields" :key="index" size="mini" class="item" v-model="file[field]"
readonly>
<template slot="prepend"><span class="field">{{field}}</span></template>
<el-button slot="append" icon="el-icon-document-copy" @click="copyValue(field)"></el-button>
</el-input>
</div>
<el-image class="big-image" :src="imageUrl" fit="contain" @load="onImageLoaded"/>
<el-image class="big-image" :src="imageUrl" fit="contain"/>
<div class="image-info">
<span class="size">{{size}}</span>
</div>
......@@ -45,7 +46,7 @@
}
},
watch: {
file(){
file() {
this.size = '';
}
},
......@@ -59,6 +60,13 @@
this.visible = true;
this.file = file;
document.body.appendChild(this.$el);
let img = new Image();
img.onload = () => {
const {width, height} = img;
this.size = width + ' x ' + height;
};
img.src = this.imageUrl;
},
hide() {
if (this.$el && this.$el.parentNode) {
......@@ -83,10 +91,6 @@
duration: 700,
});
},
onImageLoaded(e) {
const {width, height} = e.target;
this.size = width + ' x ' + height;
}
}
}
</script>
......
<template>
<pane icon="el-icon-s-operation" :title="$t('panes.Inspector')">
<div class="zero-inspector-form">
<el-form ref="form" size="mini" v-if="activeComponent.uuid" :model="form" label-width="80px">
<el-divider content-position="left">配置</el-divider>
<el-form-item label="名称">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="form.type" @change="v => handleChange('type', v)" placeholder="请选择类型">
<el-option v-for="cmp in componentsMap" :key="cmp.value" :label="cmp.label" :value="cmp.value"></el-option>
</el-select>
</el-form-item>
<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">
<!-- {{key}} -->
<dynamic-component :label="key" :item="p" :properties="activeComponent.properties"></dynamic-component>
</el-form-item>
</template>
</el-form>
</div>
<el-tabs v-model="tab" type="border-card" class="inspector-tabs">
<el-tab-pane label="Props" name="properties">
<div class="zero-inspector-form">
<el-form ref="form" size="mini" v-if="activeComponent.uuid" :model="form" label-width="80px">
<el-divider content-position="left">配置</el-divider>
<el-form-item label="名称">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="form.type" @change="v => handleChange('type', v)" placeholder="请选择类型">
<el-option v-for="cmp in componentsMap" :key="cmp.value" :label="cmp.label" :value="cmp.value"></el-option>
</el-select>
</el-form-item>
<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">
<!-- {{key}} -->
<dynamic-component :label="key" :item="p" :properties="activeComponent.properties"></dynamic-component>
</el-form-item>
</template>
</el-form>
</div>
</el-tab-pane>
<el-tab-pane label="Behavior" name="behavior">
<behavior-tab/>
</el-tab-pane>
</el-tabs>
</pane>
</template>
......@@ -28,12 +35,14 @@ import Pane from '../../components/Pane';
import _ from 'lodash';
import { componentsMap, getCmpProps } from '../../utils/common';
import dynamicComponent from './components/dynamicComponent';
import BehaviorTab from "./Inspector/BehaviorTab";
export default {
name: 'Inspector',
components: { Pane, 'dynamic-component': dynamicComponent },
components: {BehaviorTab, Pane, 'dynamic-component': dynamicComponent },
data() {
return {
tab: 'properties',
componentsMap,
form: {
name: '',
......
<template>
<div>
<el-button @click="showBehaviorEditor">行为编辑器</el-button>
<behavior-editor-dialog ref="behaviorEditorDialog"></behavior-editor-dialog>
</div>
</template>
<script>
import BehaviorEditorDialog from "../dialogs/BehaviorEditorDialog";
export default {
name: "BehaviorTab",
components: {BehaviorEditorDialog},
methods: {
showBehaviorEditor() {
this.$refs.behaviorEditorDialog.show();
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
......@@ -121,7 +121,7 @@ export default {
this.addNode({
node: data,
type,
name: 'node'
name: type,
});
}
break;
......
<template>
<div class="behavior">
<split-panes class="pane-container">
<board :builtins="builtins" :mainProcess="mainProcess" splitpanes-min="20" :splitpanes-size="80"/>
<div class="properties" splitpanes-min="20" :splitpanes-size="20">
</div>
<split-panes>
<split-panes splitpanes-min="20" :splitpanes-size="20" horizontal>
<process-list :data="prefabProcessTree" class="background full-size" splitpanes-min="20" :splitpanes-size="50"/>
<process-list :data="normalProcessTree" class="background full-size" splitpanes-min="20" :splitpanes-size="50"/>
</split-panes>
<board ref="board" class="background full-size" :builtins="builtins" :mainProcess="mainProcess"
splitpanes-min="20" :splitpanes-size="60"/>
<properties class="background full-size" splitpanes-min="20" :splitpanes-size="20"/>
</split-panes>
</div>
</template>
<script>
import {mapState, mapMutations, mapGetters} from 'vuex'
import Board from "./Board";
import SplitPanes from 'splitpanes'
import ProcessList from "./ProcessList";
import Properties from "./Properties";
const builtins = {
entry: {
......@@ -165,17 +171,29 @@ resolve({type: result ? 'equal' : 'unequal'});
export default {
name: "BehaviorEditor",
components: {Board,SplitPanes,},
components: {Properties, ProcessList, Board, SplitPanes,},
data() {
return {
builtins,
mainProcess,
}
},
computed: {
...mapState({
processes: state => state.env.processes,
}),
...mapGetters([
'prefabProcessTree',
'normalProcessTree'
])
},
mounted() {
},
methods: {
measure() {
this.$refs.board.measure();
}
}
}
</script>
......
<template>
<div class="behavior">
<svg class="svg-board" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<div class="board">
<svg class="svg-board full-size" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="layer" stroke-width="2" fill="none" fill-rule="evenodd">
<link-line v-for="(line, key, index) in lines" :data="line" :key="index" @dblclick="onDeleteLine"></link-line>
<path v-show="lineDrawing.visible" class="line hover" :d="lineDrawing.path"></path>
......@@ -24,6 +24,7 @@
import ToolTip from "./Board/ToolTip";
import {DOCK_POINT_OFFSET} from "../../../config";
import {state} from "./Board/state";
import events from "../../../global-events";
export default {
name: "Board",
......@@ -47,9 +48,19 @@
}
},
mounted() {
this.updateLines();
},
methods: {
measure(){
const {x, y} = this.$el.getBoundingClientRect();
console.log(x, y);
state.boardOffset.x = x;
state.boardOffset.y = y;
events.$emit('update-dock-point-pos');
this.updateLines();
},
updateLines() {
this.lines = {};
for (let id in this.processMap) {
......@@ -64,7 +75,6 @@
}
}
}
console.log(this.lines);
},
addLine(process, outputID, outputType, outputIndex) {
const nextProcess = this.processMap[outputID];
......@@ -99,7 +109,8 @@
this.onMouseMove(e);
},
onMouseMove(e) {
const {x, y} = e;
let x = e.x - state.boardOffset.x;
let y = e.y - state.boardOffset.y;
this.lineDrawing.path = this.drawingLineStart + `${x},${y} ${x},${y}`;
},
onMouseUp(e) {
......
<template>
<foreignObject :x="data.design.x" :y="data.design.y" :width="width" :height="height">
<div ref="node" class="node" tabindex="0" @mousedown="onMouseDown" @mouseenter="onMouseEnter">
<div ref="node" class="node" tabindex="0" @mousedown="onMouseDown" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
<div class="header">
<span>{{data.alias || meta.name}}</span>
</div>
......@@ -11,7 +11,7 @@
</div>
</div>
<div ref="inputDock" class="dock input">
<dock-point v-if="meta.name !== 'Entry'" v-for="(point, key, index) in inputMeta" :key="index"></dock-point>
<dock-point v-if="meta.id !== 'entry'" v-for="(point, key, index) in inputMeta" :key="index"></dock-point>
</div>
<div ref="outputDock" class="dock output">
<dock-point v-for="(point, key, index) in meta.output" :key="index" :data="point"
......@@ -27,6 +27,7 @@
<script>
import DockPoint from "./DockPoint";
import {state} from "./state";
import events from "../../../../global-events";
export default {
name: "ProcessNode",
......@@ -45,11 +46,11 @@
},
mounted() {
this.updateDockPointPos();
let bounds = this.$refs.node.getBoundingClientRect();
this.width = bounds.width + 9;
this.height = bounds.height;
events.$on('update-dock-point-pos', this.updateDockPointPos);
},
computed: {
meta() {
......@@ -83,10 +84,15 @@
}
},
onMouseEnter(e) {
if(state.drawing){
if (state.drawing && this.meta.id !== 'entry') {
state.targetUUID = this.data.uuid;
}
},
onMouseLeave(e) {
if (state.drawing) {
state.targetUUID = null;
}
},
onMouseDown(e) {
const {x, y} = this.data.design;
this.mouseDownPos = {x: e.screenX, y: e.screenY, dx: x, dy: y};
......@@ -117,6 +123,7 @@
},
updateDockPointPos() {
const {x: dx, y: dy} = this.process.data.design;
const {x: offX, y: offY} = state.boardOffset;
for (let side of ['input', 'output']) {
let container = this.$refs[side + 'Dock'];
let sideMeta = side === 'input' ? this.inputMeta : this.meta[side];
......@@ -131,8 +138,8 @@
let dockPoint = container.children[i];
const {x, y} = dockPoint.getBoundingClientRect();
posArr.push({
x: x - dx,
y: y - dy,
x: x - dx - offX,
y: y - dy - offY,
});
}
}
......
......@@ -6,4 +6,5 @@ export const state = {
drawing: false,
targetUUID: '',
lineID: 0,
boardOffset: {x: 0, y: 0},
};
<template>
<div>
<el-tree
v-model="data"
>
</el-tree>
</div>
</template>
<script>
export default {
name: "ProcessList",
props: [
'data',
],
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<div class="properties">
<span>Properties</span>
</div>
</template>
<script>
export default {
name: "Properties"
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<el-dialog :title="$t('Behavior Editor')" :visible.sync="visible" @opened="onOpened" :fullscreen="true"
:append-to-body="true" :close-on-click-modal="false" custom-class="behavior-editor-dialog">
<behavior-editor ref="behaviorEditor" class="full-size"></behavior-editor>
<div slot="footer" class="dialog-footer">
<el-button size="mini" type="primary" @click="onSave">{{$t('Save')}}</el-button>
</div>
</el-dialog>
</template>
<script>
import {mapState, mapMutations} from 'vuex'
import BehaviorEditor from "../behavior-editor/BehaviorEditor";
export default {
name: "BehaviorEditorDialog",
components: {BehaviorEditor},
data() {
return {
visible: false,
}
},
computed: {
...mapState({
}),
},
methods: {
show() {
this.visible = true;
},
onSave() {
this.visible = false;
},
onOpened() {
this.$refs.behaviorEditor.measure();
},
...mapMutations([
]),
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
......@@ -78,8 +78,8 @@
methods: {
prepare(){
return Promise.all([
this.updateProjects(),
this.updateEnv(),
this.updateProjects(),
])
},
moment(time) {
......@@ -117,8 +117,8 @@
},
...mapActions([
'updateProjects',
'deleteProject',
'updateEnv',
'deleteProject'
]),
},
}
......
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