Commit 4e2a11fb authored by rockyl's avatar rockyl

增加节点选择器

增加参数连接器
parent fe14e83c
......@@ -14,7 +14,7 @@
"id": "entry",
"name": "Entry",
"group": "base",
"options": {},
"props": {},
"output": [
"success"
]
......@@ -23,7 +23,7 @@
"id": "wait",
"name": "Wait",
"group": "base",
"options": {
"props": {
"duration": {
"type": "number",
"default": 1000
......@@ -52,7 +52,7 @@
"uuid": "2",
"meta": "wait",
"alias": "等待",
"options": {
"props": {
"duration": 500
}
}
......@@ -63,7 +63,7 @@
{
"id": "wave",
"name": "Wave",
"options": {
"props": {
"duration": {
"type": "number",
"default": 1000
......
......@@ -11,17 +11,19 @@ const data = {
{
id: 'entry',
name: 'Entry',
desc: '入口',
group: 'base',
type: 'builtin',
options: {},
props: {},
output: ['success'],
},
{
id: 'wait',
name: 'Wait',
desc: '等待执行',
group: 'base',
type: 'builtin',
options: {
props: {
duration: {type: 'number', default: 1000},
},
output: ['complete'],
......@@ -29,6 +31,7 @@ const data = {
{
id: 'prefab1',
name: 'Prefab1',
desc: '转盘预设',
type: 'builtin',
isPrefab: true,
subEntry: '1',
......@@ -45,7 +48,7 @@ const data = {
uuid: '2',
meta: 'wait',
alias: '等待',
options: {
props: {
duration: 500,
},
},
......@@ -56,7 +59,7 @@ const data = {
{
id: 'wave',
name: 'Wave',
options: {
props: {
duration: {type: 'number', default: 1000},
name: {type: 'string', default: 'hello'},
color: {type: 'color', default: '#123456'},
......
......@@ -7,6 +7,6 @@
"operator": "卞龙亭",
"create_time": "2019-09-19T06:56:01.000Z",
"update_time": "2019-09-19T06:56:01.000Z",
"data": "{\"views\":[{\"name\":\"view1\",\"type\":\"node\",\"properties\":{},\"uuid\":\"3a357816-5581-41ba-aa6f-4a6cc40ae396\",\"children\":[{\"name\":\"rect\",\"type\":\"rect\",\"properties\":{\"width\":50,\"height\":50,\"fillColor\":\"rgba(105, 182, 255, 1)\",\"strokeWidth\":0,\"x\":10,\"y\":10},\"events\":{\"click\":{\"once\":false,\"behaviors\":[{\"uuid\":\"aa1\",\"alias\":\"主过程\",\"meta\":\"main\"}]}},\"scripts\":[{\"script\":\"wave\",\"options\":{\"duration\":500,\"autoPlay\":true,\"type\":\"cubicIn\"}}],\"uuid\":\"f97bbf76-6923-4669-b5a3-e6382753e49a\"}]}],\"assets\":[],\"dataMapping\":[],\"processMap\":{\"main\":{\"id\":\"main\",\"name\":\"Main\",\"options\":{},\"subEntry\":\"1\",\"sub\":{\"a1\":{\"uuid\":\"a1\",\"alias\":\"入口\",\"meta\":\"entry\",\"output\":{\"success\":[\"a2\"]},\"design\":{\"x\":10,\"y\":10}},\"a2\":{\"uuid\":\"a2\",\"alias\":\"test\",\"meta\":\"test\",\"options\":{\"text\":\"hello\"},\"output\":{\"success\":[],\"failed\":[]},\"design\":{\"x\":20,\"y\":100}}}},\"test\":{\"id\":\"test\",\"name\":\"Test\",\"options\":{\"text\":{\"type\":\"string\",\"default\":\"\"}},\"output\":[\"success\",\"failed\"]}}}"
"data": "{\"views\":[{\"name\":\"view1\",\"type\":\"node\",\"properties\":{},\"uuid\":\"3a357816-5581-41ba-aa6f-4a6cc40ae396\",\"children\":[{\"name\":\"rect\",\"type\":\"rect\",\"properties\":{\"width\":50,\"height\":50,\"fillColor\":\"rgba(105, 182, 255, 1)\",\"strokeWidth\":0,\"x\":10,\"y\":10},\"events\":{\"click\":{\"once\":false,\"behaviors\":[{\"uuid\":\"aa1\",\"alias\":\"主过程\",\"meta\":\"main\"}]}},\"scripts\":[{\"script\":\"wave\",\"props\":{\"duration\":500,\"autoPlay\":true,\"type\":\"cubicIn\"}}],\"uuid\":\"f97bbf76-6923-4669-b5a3-e6382753e49a\"}]}],\"assets\":[],\"dataMapping\":[],\"processMap\":{\"main\":{\"id\":\"main\",\"name\":\"Main\",\"props\":{},\"subEntry\":\"1\",\"sub\":{\"a1\":{\"uuid\":\"a1\",\"alias\":\"入口\",\"meta\":\"entry\",\"output\":{\"success\":[\"a2\"]},\"design\":{\"x\":10,\"y\":10}},\"a2\":{\"uuid\":\"a2\",\"alias\":\"test\",\"meta\":\"test\",\"props\":{\"text\":\"hello\"},\"output\":{\"success\":[],\"failed\":[]},\"design\":{\"x\":20,\"y\":100}}}},\"test\":{\"id\":\"test\",\"name\":\"Test\",\"props\":{\"text\":{\"type\":\"string\",\"default\":\"\"}},\"output\":[\"success\",\"failed\"]}}}"
}
}
\ No newline at end of file
......@@ -8,7 +8,8 @@ const data = {
"type": "node",
"properties": {},
"uuid": "3a357816-5581-41ba-aa6f-4a6cc40ae396",
"children": [{
"children": [
{
"name": "rect",
"type": "rect",
"properties": {
......@@ -35,14 +36,15 @@ const data = {
scripts: [
{
script: 'wave',
options: {
props: {
duration: 500,
autoPlay: true,
type: 'cubicIn',
}
}
],
}]
},
]
}],
"assets": [
{
......@@ -206,7 +208,7 @@ const data = {
{
id: 'main',
name: 'Main',
options: {},
props: {},
subEntry: 'a1',
sub: {
a1: {
......@@ -225,8 +227,8 @@ const data = {
uuid: 'a2',
alias: 'test',
meta: 'test',
options: {
text: 'hello',
props: {
text: '$_linked_$',
},
output: {
success: [],
......@@ -242,13 +244,15 @@ const data = {
{
id: 'test',
name: 'Test',
options: {
desc: '测试过程',
props: {
text: {alias: '文本', type: 'string', default: '你好'},
num: {alias: '数字', type: 'number', default: 1},
type: {alias: '类型', type: 'enum', enum: ['rotate', 'jump', 'breath'], default: 'rotate'},
autoPlay: {alias: '自动播放', type: 'boolean', default: false},
color: {alias: '颜色', type: 'color', default: '#123456'},
asset: {alias: '素材', type: 'asset',},
node: {alias: '节点', type: 'node',},
},
output: ['success', 'failed'],
script: "console.log('test');",
......
<template>
<el-select v-model="selected" :filterable="filterable" allow-create placeholder="请选择">
<el-option
v-for="item in options"
v-for="item in props"
:style="`${optionStyle}${item.value}`"
:key="item.value"
:label="item.label"
......@@ -36,7 +36,7 @@ export default {
};
},
computed: {
options() {
props() {
return this.optionList || this.optionMapper[this.optionType];
},
optionStyle() {
......
......@@ -13,6 +13,10 @@
"Upload": "Upload",
"Edit": "Edit",
"Name": "Name",
"Desc": "Desc",
"Event": "Event",
"No desc": "No desc",
"Description": "Description",
"Failed to fetch": "Network error!",
"In processing": "In processing",
"Projects": "Projects",
......@@ -30,8 +34,11 @@
"Saving": "Saving…",
"Create project": "Create project",
"Rename project": "Rename project",
"Options Editor": "Options Editor",
"Props Editor": "Props Editor",
"Edit Behavior": "Edit Behavior",
"Trigger once": "Trigger once",
"Meta Editor": "Meta Editor",
"Link to parent": "Link to parent",
"Input project name": "Input project name",
"Invalid project name": "Invalid project name",
"Creating project": "Creating project…",
......@@ -68,6 +75,8 @@
"Meta is in use, can not delete": "Meta is in use, can not delete!",
"Are you sure to delete this meta": "Are you sure to delete this meta?",
"This Meta ID is in use, can not save": "This Meta ID is in use, can not save!",
"Custom node desc": "Custom node",
"Divider node desc": "Divider node, exit will be executed in sequence",
"menu": {
"save": "Save",
"details": "Details",
......
......@@ -3,6 +3,7 @@ import App from './App.vue'
import router from './router'
import store from './store/index'
import i18n from './i18n'
import './vue-resize'
Vue.config.productionTip = false;
......@@ -16,5 +17,3 @@ new Vue({
i18n,
render: h => h(App)
}).$mount('#app');
//todo 节点选择器
\ No newline at end of file
......@@ -14,19 +14,21 @@ export const behaviorStore = {
currentBehavior: null,
processContext: [],
originData: null,
originBehaviors: null,
behaviors: null,
},
mutations: {
behavior_startEdit(state, {originData, behaviors, event}) {
state.originData = originData;
state.behaviors = behaviors;
state.originBehaviors = behaviors;
state.behaviors = JSON.parse(JSON.stringify(behaviors));
state.data = JSON.parse(JSON.stringify(originData));
if (behaviors.length > 0) {
state.currentBehavior = behaviors[0];
if (state.behaviors.length > 0) {
state.currentBehavior = state.behaviors[0];
} else {
let metaUUID = generateUUID();
state.currentBehavior = behaviors[0] = {
state.currentBehavior = state.behaviors[0] = {
uuid: generateUUID(),
alias: event,
meta: metaUUID,
......@@ -34,16 +36,20 @@ export const behaviorStore = {
let subEntryUUID = generateUUID();
state.data.processes.push({
id: metaUUID,
name: 'event',
options: {},
name: event,
props: {},
subEntry: subEntryUUID,
sub: {
[subEntryUUID]: {
uuid: subEntryUUID,
alias: 'Entry',
meta: 'entry',
},
design: {
x: 10,
y: 10,
}
},
},
});
}
},
......@@ -53,7 +59,10 @@ export const behaviorStore = {
},
behavior_save(state) {
state.originData.processes = state.data.processes;
state.behaviors[0] = state.currentBehavior;
state.originBehaviors[0] = state.currentBehavior;
},
behavior_cancel(state) {
},
updateProcesses(state, {targetMetaID, replaceMetaID}) {
for (let process of state.data.processes) {
......@@ -102,6 +111,9 @@ export const behaviorStore = {
behavior_getAssetByUUID: state => uuid => {
return state.data.assets.find(item => item.uuid === uuid);
},
behavior_views: state=>{
return state.data.views;
}
},
actions: {
addCustomProcessMeta({commit, state}) {
......@@ -109,7 +121,7 @@ export const behaviorStore = {
id: generateUUID(),
name: 'Custom',
script: '',
options: {},
props: {},
output: ['success', 'failed'],
};
commit('addProcessMeta', meta);
......@@ -118,8 +130,8 @@ export const behaviorStore = {
addDividerProcessMeta({commit, state}) {
let meta = {
id: generateUUID(),
isDivider: true,
name: 'Divider',
isDivider: true,
output: ['p0'],
};
commit('addProcessMeta', meta);
......
......@@ -5,6 +5,7 @@
*/
import {envApi} from "../../api";
import i18n from "../../i18n";
export const envStore = {
state: {
......@@ -34,11 +35,13 @@ export const envStore = {
const dividerProcess = {
id: 'divider',
name: 'Divider',
desc: i18n.t('Divider node desc'),//'分流节点,出口会按顺序一次执行',
};
tree.unshift(dividerProcess);
const customProcess = {
id: 'custom',
name: 'Custom',
desc: i18n.t('Custom node desc'),//'自定义节点',
};
tree.unshift(customProcess);
......
......@@ -2,8 +2,8 @@
* Created by rockyl on 2019-09-25.
*/
export default function (options = {}) {
const {mutationTypes = []} = options;
export default function (props = {}) {
const {mutationTypes = []} = props;
return (store) => {
store.subscribe((mutation, state) => {
if (mutationTypes.includes(mutation.type)) {
......
......@@ -161,6 +161,11 @@ $dock-pin-width: 9px;
float: right;
border: 1px solid $--border-color-base;
}
.linked-icon {
float: right;
color: $--color-success;
}
}
}
......@@ -224,6 +229,18 @@ $dock-pin-width: 9px;
display: flex;
flex-direction: column;
.linked{
display: block;
height: 26px;
overflow: hidden;
border: 1px solid $--border-color-base;
border-radius: 4px;
background-color: $--background-color-base;
flex: 1;
padding: 0 5px;
color: $--color-text-secondary;
}
.el-input-group__prepend {
padding: 0 5px;
}
......@@ -243,17 +260,36 @@ $dock-pin-width: 9px;
display: flex;
flex-direction: column;
.name-input{
}
.scrollbar {
margin-right: -5px;
padding-top: 5px;
flex: 1;
.scrollbar-view{
padding-right: 10px;
}
.el-form-item__content {
display: flex;
justify-content: flex-end;
.el-input-number--mini {
flex: 1;
}
}
.node-select-container{
flex: 1;
}
.string-editor-container{
flex: 1;
height: 28px;
}
}
}
}
......@@ -275,7 +311,7 @@ $dock-pin-width: 9px;
}
}
.options-editor-dialog {
.props-editor-dialog {
.scrollbar {
width: 100%;
height: 40vh;
......@@ -309,3 +345,18 @@ $dock-pin-width: 9px;
width: 400px;
}
}
.node-select-popover{
.tree-scrollbar {
width: 100%;
height: 200px;
}
.bottom-bar{
margin-top: 5px;
display: flex;
align-items: center;
justify-content: space-between;
}
}
......@@ -20,6 +20,8 @@ export const RENDERER_TYPES= {
CANVAS: 'canvas',
};
export const linkedFlag = '$_linked_$';
export function messageError(e) {
Message({
dangerouslyUseHTMLString: true,
......
......@@ -111,7 +111,7 @@ export default {
textAlign: {
title: '文本对齐',
type: 'select',
options: [
props: [
{ label: '靠左', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '靠右', value: 'right' }
......@@ -121,7 +121,7 @@ export default {
/*verticalAlign: {
title: '纵向对齐',
type: 'select',
options: [
props: [
{ label: '靠上', value: 'up' },
{ label: '居中', value: 'middle' },
{ label: '靠下', value: 'down' }
......
<template>
<div class="zero-inspector-behavior-form" v-if="activeComponent.uuid">
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden">
<el-form ref="form" size="mini" label-width="80px">
<el-form ref="form" size="mini" label-width="60px">
<div v-for="(evn, key) in eventsObj" :key="key">
<el-form-item label="触发事件:">
<el-form-item :label="$t('Event') + ':'">
<div>{{key}}</div>
<div >
<el-tooltip content="是否单次触发" placement="top">
<el-tooltip :content="$t('Trigger once')" placement="top">
<el-switch v-model="evn.once" @change="v => handleOnceChange(key, v)"></el-switch>
</el-tooltip>
<el-tooltip content="行为编辑" placement="top" style="margin-left: 20px;">
<el-button size="mini" @click="showBehaviorEditor(evn, key)">
行为编辑
<el-button size="mini" @click="showBehaviorEditor(evn, key)" style="margin-left: 20px;">
{{$t('Edit Behavior')}}
<i v-if="evn.behaviors && evn.behaviors.length" class="el-icon-check el-icon--right"></i>
</el-button>
</el-tooltip>
</div>
</el-form-item>
</div>
......
<template>
<div class="behavior">
<split-panes>
<split-panes splitpanes-min="20" :splitpanes-size="20" horizontal>
<split-panes splitpanes-min="10" :splitpanes-size="10" horizontal>
<process-list @edit-meta="onEditMeta" @delete-meta="onDeleteMeta" :data="prefabProcessTree"
class="background full-size" splitpanes-min="20"
:splitpanes-size="30"/>
......@@ -9,7 +9,7 @@
class="background full-size" splitpanes-min="20"
:splitpanes-size="70"/>
</split-panes>
<div class="center full-size background" splitpanes-min="20" :splitpanes-size="60">
<div class="center full-size background" splitpanes-min="20" :splitpanes-size="70">
<edit-path :processStack="processStack" @pop="onPop"/>
<board ref="board" @select-process-node="onSelectProcessNode" @edit-process="editProcess"/>
</div>
......
<template>
<div class="board" @dragover="onDragOver" @drop="onDrop">
<div class="board" @dragover="onDragOver" @drop="onDrop" v-resize="onResize">
<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">
......@@ -32,7 +32,7 @@
import {state} from "./Board/state";
import events from "../../../global-events";
import generateUUID from "uuid/v4";
//todo 缩放功能
export default {
name: "Board",
components: {ToolTip, LinkLine, ProcessNode,},
......@@ -139,10 +139,13 @@
events.$emit('update-dock-pin-pos');
});
},
measure() {
onResize(){
const {x, y} = this.$el.getBoundingClientRect();
state.boardOffset.x = x;
state.boardOffset.y = y;
},
measure() {
this.onResize();
this.$nextTick(() => {
events.$emit('update-dock-pin-pos');
......
......@@ -12,12 +12,17 @@
<el-button size="mini" icon="el-icon-plus" circle @click="outputPointModify('add')"></el-button>
<el-button size="mini" icon="el-icon-minus" circle @click="outputPointModify('remove')"></el-button>
</div>
<div v-else class="field-item" v-for="(param, key, index) in meta.options" :key="index">
<div v-else class="field-item" v-for="(param, key, index) in meta.props" :key="index">
<span class="key">{{param.alias || key}}</span>:
<span class="" v-if="data.props[key] === linkedFlag">
<i class="el-icon-link linked-icon"></i>
</span>
<template v-else>
<div v-if="param.type === 'color'">
<div class="color-value" :style="{'background-color': valueToString(param, data, key)}"></div>
</div>
<span v-else class="string-value">{{valueToString(param, data, key)}}</span>
</template>
</div>
</div>
<div ref="inputDock" class="dock input">
......@@ -38,6 +43,7 @@
import DockPin from "./DockPin";
import {state} from "./state";
import events from "../../../../global-events";
import {linkedFlag} from "../../../../utils";
export default {
name: "ProcessNode",
......@@ -51,6 +57,7 @@
height: 100,
inputMeta,
active: false,
linkedFlag,
}
},
mounted() {
......@@ -79,18 +86,18 @@
methods: {
valueToString(param, data, key) {
if (param.type === 'asset') {
if (data.options.hasOwnProperty(key)) {
let asset = this.$store.getters.behavior_getAssetByUUID(data.options[key]);
if (data.props.hasOwnProperty(key)) {
let asset = this.$store.getters.behavior_getAssetByUUID(data.props[key]);
return asset.name;
}
} else {
return data.options[key] || param.default;
return data.props[key] || param.default;
}
return '';
},
prepare() {
let {design, options, output} = this.process.data;
let {design, props, output} = this.process.data;
if (!design) {
this.$set(this.process.data, 'design', {});
design = this.process.data.design;
......@@ -101,8 +108,8 @@
if (!design.y) {
this.$set(design, 'y', 0);
}
if (!options) {
this.$set(this.process.data, 'options', {});
if (!props) {
this.$set(this.process.data, 'props', {});
}
if (!output) {
this.$set(this.process.data, 'output', {});
......
......@@ -3,7 +3,7 @@
:close-on-click-modal="false"
:append-to-body="true">
<div class="meta-editor-wrapper">
<el-form :inline="true" class="info-editor" size="mini">
<el-form class="info-editor" size="mini" label-position="right" label-width="70px">
<template v-if="meta">
<el-form-item label="ID">
<el-input v-model="meta.id" placeholder="ID" :readonly="!editable"/>
......@@ -12,13 +12,15 @@
<el-input v-model="meta.name" placeholder="Name" :readonly="!editable"/>
</el-form-item>
</template>
</el-form>
<el-form class="info-editor" size="mini">
<template v-if="meta">
<el-form-item label="Options">
<el-link :underline="false" @click="onClickEditOptions" :disabled="!editable">
<template v-if="Object.keys(meta.options).length">
<el-tag type="success" size="mini" v-for="(option, key) in meta.options" :key="key">{{key}}</el-tag>
<el-form-item label="Desc">
<el-input v-model="meta.desc" placeholder="Description" :readonly="!editable"/>
</el-form-item>
<el-form-item label="Props">
<el-link :underline="false" @click="onClickEditProps" :disabled="!editable">
<template v-if="Object.keys(meta.props).length">
<el-tag type="success" size="mini" v-for="(option, key) in meta.props" :key="key">{{key}}</el-tag>
</template>
<template v-else>Nothing</template>
</el-link>
......@@ -40,30 +42,30 @@
<el-button size="mini" plain @click="save">Save</el-button>
</div>
</div>
<options-editor-dialog ref="optionsEditorDialog"/>
<props-editor-dialog ref="propsEditorDialog"/>
</el-dialog>
</template>
<script>
import ElFormItem from "./editors/form-item";
import OptionsEditorDialog from "./OptionsEditorDialog";
import PropsEditorDialog from "./PropsEditorDialog";
export default {
name: "MetaEditorDialog",
components: {OptionsEditorDialog, ElFormItem},
components: {PropsEditorDialog, ElFormItem},
data() {
return {
visible: false,
meta: null,
optionsEditorVisible: false,
propsEditorVisible: false,
}
},
computed: {
editable() {
return this.meta && this.meta.type !== 'builtin';
},
options() {
return Object.keys(this.meta.options).join(',')
props() {
return Object.keys(this.meta.props).join(',')
},
},
methods: {
......@@ -72,8 +74,8 @@
this.meta = JSON.parse(JSON.stringify(meta));
this.oldMetaID = this.meta.id;
},
onClickEditOptions() {
this.$refs.optionsEditorDialog.edit(this.meta.options);
onClickEditProps() {
this.$refs.propsEditorDialog.edit(this.meta.props);
},
save() {
if (this.oldMetaID !== this.meta.id && this.$store.getters.metaIDExists(this.meta.id)) {
......
......@@ -14,12 +14,13 @@
<span :draggable="draggable(data)" @dragstart.stop="dragProcessStart(data, $event)">{{data.name}}</span>
</div>
<el-dropdown v-if="metaEditable(data)" class="more-button" size="mini" trigger="click"
<el-dropdown v-if="nodeMenu(data)" class="more-button" size="mini" trigger="click"
@command="(command)=>{onMoreMenu(command, data, node)}">
<el-link icon="el-icon-more" :underline="false" @click.stop/>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="edit">{{$t('Edit')}}</el-dropdown-item>
<el-dropdown-item command="delete">{{$t('Delete')}}</el-dropdown-item>
<el-dropdown-item command="desc" >{{$t('Desc')}}</el-dropdown-item>
<el-dropdown-item command="edit" v-if="metaEditable(data)" divided>{{$t('Edit')}}</el-dropdown-item>
<el-dropdown-item command="delete" v-if="metaEditable(data)">{{$t('Delete')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
......@@ -70,11 +71,22 @@
dragProcessStart(data, event) {
event.dataTransfer.setData('process', data.id);
},
nodeMenu(data){
return !data.hasOwnProperty('children')
},
metaEditable(data){
return data.type !== 'builtin' && !data.hasOwnProperty('children') && !editableIds.includes(data.id) && data.isDivider !== true
},
onMoreMenu(command, data, node) {
switch (command) {
case 'desc':
this.$alert(data.desc || this.$t('No desc'), data.name + ' ' + this.$t('Description'), {
confirmButtonText: this.$t('Confirm'),
callback: action => {
}
});
break;
case 'edit':
this.$emit('edit-meta', data);
break;
......
<template>
<div class="wrapper" v-if="process">
<el-input v-model="process.data.alias" clearable :placeholder="process.meta.name" size="mini">
<el-input class="name-input" v-model="process.data.alias" clearable :placeholder="process.meta.name" size="mini">
<template slot="prepend">{{$t('Name')}}</template>
</el-input>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view">
<el-form v-model="process" size="mini" label-width="80px" label-position="left" @submit.prevent>
<component v-for="(property, key) in process.meta.options"
<component v-for="(property, key) in process.meta.props"
:is="getEditor(property)"
v-model="process.data.options[key]"
v-model="process.data.props[key]"
:propertyName="key"
:property="property"
:container="process"
......@@ -26,6 +26,7 @@
import BooleanEditor from "./editors/BooleanEditor";
import ColorEditor from "./editors/ColorEditor";
import AssetEditor from "./editors/AssetEditor";
import NodeSelectEditor from "./editors/NodeSelectEditor";
const editorMapping = {
number: 'NumberEditor',
......@@ -34,11 +35,12 @@
boolean: 'BooleanEditor',
color: 'ColorEditor',
asset: 'AssetEditor',
node: 'NodeSelectEditor',
};
export default {
name: "PropertiesEditor",
components: {AssetEditor, ColorEditor, BooleanEditor, EnumEditor, NumberEditor, StringEditor},
components: {NodeSelectEditor, AssetEditor, ColorEditor, BooleanEditor, EnumEditor, NumberEditor, StringEditor},
data() {
return {
process: null,
......
<template>
<el-dialog :title="$t('Options Editor')" width="80%" :visible.sync="visible"
<el-dialog :title="$t('Props Editor')" width="80%" :visible.sync="visible"
:close-on-click-modal="false"
:append-to-body="true">
<div class="options-editor-dialog">
<div class="props-editor-dialog">
<el-button class="add-button" size="mini" circle icon="el-icon-plus" plain @click="addItem"/>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" v-if="options"
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" v-if="props"
view-class="scrollbar-view">
<div class="wrapper">
<el-table
:data="options"
:data="props"
style="width: 100%">
<el-table-column
label="Type"
......@@ -81,13 +81,13 @@
<script>
export default {
name: "OptionsEditorDialog",
name: "PropsEditorDialog",
data() {
return {
visible: false,
originOptions: null,
copiedOptions: null,
options: [],
originProps: null,
copiedProps: null,
props: [],
types: [
'boolean',
'string',
......@@ -95,32 +95,33 @@
'enum',
'color',
'asset',
'node',
],
}
},
methods: {
edit(options) {
edit(props) {
this.visible = true;
this.originOptions = options;
this.copiedOptions = JSON.parse(JSON.stringify(options));
this.options.splice(0);
for (let key in this.copiedOptions) {
let option = this.copiedOptions[key];
this.options.push({
this.originProps = props;
this.copiedProps = JSON.parse(JSON.stringify(props));
this.props.splice(0);
for (let key in this.copiedProps) {
let option = this.copiedProps[key];
this.props.push({
key,
option,
})
}
},
save(){
let keys = Object.keys(this.originOptions);
let keys = Object.keys(this.originProps);
for (let key of keys) {
this.$delete(this.originOptions, key);
this.$delete(this.originProps, key);
}
for(let item of this.options){
for(let item of this.props){
if(item.key){
this.$set(this.originOptions, item.key, item.option);
this.$set(this.originProps, item.key, item.option);
}
}
......@@ -133,14 +134,14 @@
},
addItem(){
this.options.push({
this.props.push({
option: {
type: 'string',
}
})
},
deleteItem(index){
this.options.splice(index, 1);
this.props.splice(index, 1);
},
},
}
......
<template>
<editor-wrapper :property="property" :propertyName="propertyName">
<editor-wrapper :value="value" :container="container" :property="property" :propertyName="propertyName">
<el-select :value="editValue" @input="onInput" :placeholder="property.default" class="el-select">
<el-option
v-for="(item, key) in assets"
......
<template>
<editor-wrapper :property="property" :propertyName="propertyName">
<editor-wrapper :value="value" :container="container" :property="property" :propertyName="propertyName">
<el-switch :value="editValue" @input="onInput"
class="picker"></el-switch>
</editor-wrapper>
......
<template>
<editor-wrapper :property="property" :propertyName="propertyName" class="color-editor-container">
<editor-wrapper :value="value" :container="container" :property="property" :propertyName="propertyName" class="color-editor-container">
<el-color-picker
class="picker"
:value="editValue"
......
<template>
<el-form-item class="editor-wrapper" :label="property.alias || propertyName" content-float="right" :content-width="contentWidth" :labelOffsetTop="labelOffsetTop">
<el-form-item class="editor-wrapper" :label="property.alias || propertyName" content-float="right"
:content-width="contentWidth" :labelOffsetTop="labelOffsetTop">
<template v-if="linked">
<span class="linked">Linked to parent</span>
</template>
<template v-else>
<slot></slot>
</template>
<el-popover
trigger="click"
>
<div>
<span>{{$t('Link to parent')}}: </span>
<el-switch :value="linked" @input="onChange"/>
</div>
<el-link style="padding: 3px;" slot="reference" icon="el-icon-link" :underline="false"
:type="linked ? 'success' : 'default'"/>
</el-popover>
</el-form-item>
</template>
<script>
import camelcase from 'camelcase'
import ElFormItem from "./form-item";
import {linkedFlag} from "../../../../utils";
export default {
name: "EditorWrapper",
components: {ElFormItem},
props: {
property: Object,
value: {},
container: {},
propertyName: String,
contentWidth: {
type: String,
......@@ -24,8 +43,19 @@
},
},
computed: {
linked() {
return this.value === linkedFlag
}
},
methods: {
onChange(v) {
if (v) {
this.$set(this.container.data.props, this.propertyName, linkedFlag);
} else {
this.$delete(this.container.data.props, this.propertyName);
}
}
}
}
</script>
......
<template>
<editor-wrapper :property="property" :propertyName="propertyName">
<editor-wrapper :value="value" :container="container" :property="property" :propertyName="propertyName">
<el-select :value="editValue" @input="onInput" :placeholder="property.default" class="el-select">
<el-option
v-for="(item, key) in property.enum"
......
<template>
<editor-wrapper :value="value" :container="container" :property="property" :propertyName="propertyName">
<div style="display: flex;flex: 1;">
<el-popover
placement="top"
popper-class="node-select-popover"
class="node-select-container"
trigger="manual"
width="400"
v-model="popoverVisible"
>
<div>
<el-scrollbar class="tree-scrollbar" wrap-class="wrap-x-hidden">
<el-tree
:data="behavior_views"
:props="defaultProps"
:expand-on-click-node="false"
draggable
highlight-current
:default-expand-all="true"
@node-click="handleNodeClick"
empty-text=""
>
<div slot-scope="{ node, data }" class="tree-node">
{{data.name}}
</div>
</el-tree>
</el-scrollbar>
<div class="bottom-bar">
<div></div>
<el-button-group>
<el-button @click="onCancel" plain>Cancel</el-button>
<el-button @click="onConfirm" type="primary" plain>Confirm</el-button>
</el-button-group>
</div>
</div>
<el-input clearable slot="reference" v-model="editValue" @change="onInput" placeholder="unset">
<template slot="prepend">node://</template>
</el-input>
</el-popover>
<el-button-group>
<el-button :icon="editButtonIcon" @click="onClickEdit"></el-button>
<el-button icon="el-icon-delete" @click="onClickClean"></el-button>
</el-button-group>
</div>
</editor-wrapper>
</template>
<script>
import {mapGetters} from "vuex";
import EditorWrapper from "./EditorWrapper";
const nodeScheme = 'node://';
export default {
name: "NodeSelectEditor",
components: {EditorWrapper,},
props: ['value', 'container', 'property', 'propertyName'],
data() {
return {
editValueOrigin: this.value,
popoverVisible: false,
defaultProps: {
children: 'children',
label: 'name'
},
}
},
computed: {
...mapGetters([
'behavior_views'
]),
editValue: {
get(){
return this.editValueOrigin ? this.editValueOrigin.replace(nodeScheme) : '';
},
set(v){
this.editValueOrigin = v;
},
},
editButtonIcon() {
return this.popoverVisible ? 'el-icon-check' : 'el-icon-edit';
},
},
methods: {
onInput(v, oldValue) {
if (v !== this.value) {
this.$emit('input', v ? nodeScheme + v : undefined, this.container, this.propertyName, oldValue);
}
},
onChange() {
this.$emit('input', nodeScheme + this.selectedNodeUUID, this.container, this.propertyName, this.value);
},
onClickEdit() {
this.popoverVisible = !this.popoverVisible;
},
onClickClean() {
this.$emit('input', undefined, this.container, this.propertyName, this.value);
},
onConfirm() {
this.editValue = this.selectedNodeUUID;
this.onChange();
this.popoverVisible = false;
},
onCancel() {
this.$emit('cancel');
this.popoverVisible = false;
},
handleNodeClick(data, node) {
this.selectedNodeUUID = data.uuid;
},
},
}
</script>
<style scoped>
</style>
<template>
<editor-wrapper :property="property" :propertyName="propertyName">
<editor-wrapper :value="value" :container="container" :property="property" :propertyName="propertyName">
<el-input-number :value="editValue" @input="onInput" controls-position="right"
:placeholder="defaultValue"></el-input-number>
</editor-wrapper>
......
<template>
<editor-wrapper :property="property">
<div class="texture-box">
<el-popover
placement="top"
popper-class="raw-editor-popover"
class="raw-name-container"
trigger="manual"
width="400"
v-model="popoverVisible">
<json-editor ref="jsonEditor" :value="editValue" @input="onInput" @cancel="onCancel"></json-editor>
<div slot="reference" class="editor-texture-name">{{rawString}}</div>
</el-popover>
<el-button-group>
<el-button :icon="editButtonIcon" @click="onClickEdit"></el-button>
<el-button icon="el-icon-delete" @click="onClickClean"></el-button>
</el-button-group>
</div>
</editor-wrapper>
</template>
<script>
import JsonEditor from "./JsonEditor";
import EditorWrapper from "./EditorWrapper";
export default {
name: "RawEditor",
components: {JsonEditor, EditorWrapper},
props: ['component', 'value', 'property'],
data() {
return {
popoverVisible: false,
}
},
computed: {
editButtonIcon() {
return this.popoverVisible ? 'el-icon-check' : 'el-icon-edit';
},
rawString() {
return this.editValue ? JSON.stringify(this.editValue) : '';
},
editValue(){
return this.value ? this.value.data : null;
}
},
watch: {
popoverVisible(v){
if(v){
this.$refs.jsonEditor.initValue();
}
}
},
methods: {
onClickEdit() {
this.popoverVisible = !this.popoverVisible;
},
onClickClean() {
this.$emit('input', undefined, this.component, this.property.name, this.value);
},
onInput(v) {
if(v !== this.value){
this.$emit('input', {
_type_: 'raw',
data: v,
}, this.component, this.property.name, this.value);
}
this.popoverVisible = false;
},
onCancel(){
this.popoverVisible = false;
}
},
}
</script>
<style scoped>
.texture-box {
width: 100%;
height: 28px;
display: flex;
font-size: 12px;
}
.editor-texture-name {
line-height: 28px;
height: 28px;
border-radius: 4px 0 0 4px;
color: gray;
padding: 0 5px;
user-select: none;
}
.raw-name-container {
flex: 1;
overflow: hidden;
}
.invalid-link {
color: red;
}
.inspector-append {
width: 45px;
}
</style>
<template>
<editor-wrapper :property="property" :propertyName="propertyName">
<editor-wrapper :value="value" :container="container" :property="property" :propertyName="propertyName">
<div style="display: flex;flex: 1;">
<el-popover
placement="top"
popper-class="input-area-popover"
class="string-name-container"
class="string-editor-container"
trigger="manual"
width="400"
v-model="popoverVisible"
......@@ -14,7 +14,8 @@
type="textarea"
v-model="popoverEditValue"
:placeholder="defaultValue"
:rows="6">
:rows="6"
>
</el-input>
<div class="bottom-bar">
<el-button @click="onClean" type="danger" plain>Clean</el-button>
......@@ -24,11 +25,12 @@
</el-button-group>
</div>
</div>
<el-input clearable slot="reference" v-model="editValue" @change="onChange" :placeholder="defaultValue"/>
<el-input clearable slot="reference" :value="editValue" @input="onInput" @change="onChange"
:placeholder="defaultValue"/>
</el-popover>
<el-button-group>
<el-button :icon="editButtonIcon" @click="onClickEdit"></el-button>
<el-button icon="el-icon-delete" @click="onClickClean"></el-button>
<el-button :icon="editButtonIcon" @click="onClickEdit" ></el-button>
<el-button icon="el-icon-delete" @click="onClickClean" ></el-button>
</el-button-group>
</div>
</editor-wrapper>
......@@ -36,7 +38,7 @@
<script>
import EditorWrapper from "./EditorWrapper";
import {getEditorDefaultValue} from "../../../../utils";
import {getEditorDefaultValue, linkedFlag} from "../../../../utils";
export default {
name: "StringEditor",
......@@ -44,22 +46,25 @@
props: ['value', 'container', 'property', 'propertyName'],
data() {
return {
editValue: this.value,
editValueOrigin: this.value,
popoverEditValue: this.value,
popoverVisible: false,
}
},
computed: {
editValue() {
return this.editable ? this.editValueOrigin : '';
},
editButtonIcon() {
return this.popoverVisible ? 'el-icon-check' : 'el-icon-edit';
},
defaultValue() {
return getEditorDefaultValue(this.property);
}
},
},
watch: {
value(v) {
this.editValue = v;
this.editValueOrigin = v;
},
popoverVisible(v) {
if (v) {
......@@ -74,11 +79,14 @@
onClickClean() {
this.$emit('input', undefined, this.container, this.propertyName, this.value);
},
onInput(v) {
this.editValueOrigin = v;
},
onChange() {
this.$emit('input', this.editValue, this.container, this.propertyName, this.value);
},
onConfirm() {
this.editValue = this.popoverEditValue;
this.editValueOrigin = this.popoverEditValue;
this.onChange();
this.popoverVisible = false;
},
......@@ -100,8 +108,4 @@
align-items: center;
justify-content: space-between;
}
.string-name-container {
flex: 1;
}
</style>
<template>
<el-dialog :title="$t('Behavior Editor')" :visible.sync="visible" @opened="onOpened" :fullscreen="true"
<el-dialog :title="$t('Behavior Editor')" :visible.sync="visible" @before-close="beforeClose" @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>
<behavior-editor v-if="editorReady" 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>
......@@ -18,11 +19,12 @@
data() {
return {
visible: false,
editorReady: false,
}
},
computed: {
...mapState({
data: state=>state.project.data,
data: state => state.project.data,
}),
},
methods: {
......@@ -32,6 +34,7 @@
behaviors,
event,
});
this.editorReady = false;
this.visible = true;
},
onSave() {
......@@ -39,12 +42,22 @@
this.visible = false;
this.$emit('change');
},
beforeClose() {
this.behavior_cancel();
this.visible = false;
this.$emit('cancel');
},
onOpened() {
this.editorReady = true;
this.$nextTick(() => {
this.$refs.behaviorEditor.edit();
});
},
...mapMutations([
'behavior_startEdit',
'behavior_save',
'behavior_cancel',
]),
}
}
......
/**
* Created by rockyl on 2019-11-11.
*/
import Vue from 'vue'
Vue.directive('resize', {
bind(el, binding) {
let width = '', height = '';
function get() {
const style = document.defaultView.getComputedStyle(el);
if (width !== style.width || height !== style.height) {
binding.value({width, height});
}
width = style.width;
height = style.height;
}
el.__vueReize__ = setInterval(get, 500);
},
unbind(el) {
clearInterval(el.__vueReize__);
},
});
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