Commit 420f7c0b authored by rockyl's avatar rockyl

完善各种体验

parent 3cc19fd2
<template>
<el-popover
placement="top"
trigger="hover"
width="auto"
:disabled="!legalUrl"
:content="url"
>
<img style="max-width: 200px;" v-if="url" :src="url" alt="" />
<el-input v-model="swvalue" slot="reference" @drop.native="drop" @dragover.native="dragOver"></el-input>
</el-popover>
<el-popover
placement="top"
trigger="hover"
width="auto"
:disabled="!legalUrl"
:content="url"
>
<img style="max-width: 200px;" v-if="url" :src="url" alt=""/>
<el-input v-model="swvalue" slot="reference" @drop.native="drop" @dragover.native="dragOver">
<el-button slot="append" icon="el-icon-aim" @click="locateAsset"></el-button>
</el-input>
</el-popover>
</template>
<style>
</style>
<script>
export default {
props: {
value: [String, Number, Boolean]
},
data() {
return {
swvalue: this.value || ''
};
},
watch: {
swvalue(v) {
this.$emit('change', v);
}
},
methods: {
drop(e) {
if (this.$store.state.project.dragUUID) {
console.log('native drop', this.$store.state);
this.swvalue = `asset://${this.$store.state.project.dragUUID}`
}
},
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 '';
}
},
legalUrl: function() {
return (this.swvalue + '').indexOf('//') > -1;
}
}
};
import events from "@/global-events.js"
export default {
props: {
value: [String, Number, Boolean]
},
data() {
return {
swvalue: this.value || ''
};
},
watch: {
swvalue(v) {
this.$emit('change', v);
}
},
methods: {
drop(e) {
if (this.$store.state.project.dragUUID) {
console.log('native drop', this.$store.state);
this.swvalue = `asset://${this.$store.state.project.dragUUID}`
}
},
dragOver(e) {
e.preventDefault();
},
locateAsset() {
let uuid = this.swvalue ? this.swvalue.split('//')[1] : null;
if (uuid) {
let asset = this.$store.state.project.data.assets.find(a => a.uuid === uuid);
if (asset) {
events.$emit('select-asset-item', asset);
}
}
}
},
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 '';
}
},
legalUrl: function () {
return (this.swvalue + '').indexOf('//') > -1;
}
}
};
</script>
......@@ -85,7 +85,7 @@
"Type": "类型",
"Group": "分组",
"Select from history": "打开历史版本",
"Meta Search": "过程元查找",
"Process Search": "过程搜索定位",
"Access denied": "无权限",
"Invalid router": "无效的页面",
"Jump after": "{cd}秒后跳转",
......@@ -155,6 +155,7 @@
"Remote Version": "远程版本",
"Confirm to exit the editor": "确定退出编辑器吗?",
"Confirm to publish": "确定发布吗?",
"Are you sure to delete this process?": "确定删除这对应的过程吗",
"Are you sure to delete it's process": "确定删除这对应的过程吗",
"Are you sure to delete this asset": "确定删除这个素材吗",
"Are you sure to delete all assets": "确定删除全部素材吗",
......@@ -186,7 +187,7 @@
"Publish": "发布",
"Publish config": "发布配置",
"Publish to projectx": "发布到星速台",
"Are sure to delete this item?": "确定要删除这条记录吗?",
"Are you sure to delete this item?": "确定要删除这条记录吗?",
"Projectx skin editor": "星速台皮肤编辑器",
"Skin name required": "皮肤名必填",
"Skin html required": "皮肤代码必填",
......@@ -201,6 +202,9 @@
"Index page exists": "已存在首页",
"Please config projectID": "请先配置{env}环境下的projectId",
"Online ticket is invalid": "线上tickct失效,请联系管理员修改",
"Are you sure to translate to normal process?": "你确定将此转化为通用过程吗?",
"Input custom process name": "请输入自定义过程名称",
"Invalid name": "无效名称",
"eventGroup": {
"in": "接收",
"out": "派发"
......@@ -215,6 +219,9 @@
"preview-fast": {
"label": "预览"
},
"search": {
"label": "搜索"
},
"pack": {
"label": "打包",
"sub": {
......
......@@ -29,7 +29,7 @@ export default new Vuex.Store({
plugins: [
SaveToLocalPlugin({
mutationTypes: [
'makeDirty',
'makeProjectDirty',
'modifyProject',
'addNode',
'deleteNode',
......
......@@ -7,7 +7,7 @@
import Vue from "vue";
import i18n from "../../i18n";
import generateUUID from "uuid/v4";
import {clonePureObj, getViewNodePath, metaInUse, searchViewNode, updateProcesses} from "../../utils";
import {clonePureObj, getViewNodePath, findMetaDepById, searchViewNode, updateProcesses} from "../../utils";
import {arrayFind} from "element-ui/src/utils/util";
export const behaviorStore = {
......@@ -18,6 +18,7 @@ export const behaviorStore = {
currentBehavior: null,
processContext: [],
currentProcess: null,
lastProcess: null,
processStack: [],
editable: false,
......@@ -31,7 +32,7 @@ export const behaviorStore = {
dirty: false,
},
mutations: {
makeBehaviorDirty(state){
makeBehaviorDirty(state) {
state.dirty = true;
},
behavior_startEdit(state, {originData, behavior}) {
......@@ -78,7 +79,7 @@ export const behaviorStore = {
state.currentProcess = process;
},
popProcessStack(state, index) {
state.processStack.splice(index);
state.lastProcess = state.processStack.splice(index)[0];
updatePropsEditable(state);
state.currentProcess = state.processStack[state.processStack.length - 1];
},
......@@ -90,7 +91,15 @@ export const behaviorStore = {
scale += value;
}
state.drawState.boardScale = Math.max(Math.min(4, scale), 0.1);
}
},
translateToNormal(state, {meta, parentMeta}) {
meta.isInline = false;
let index = parentMeta.metas.indexOf(meta);
parentMeta.metas.splice(index, 1);
state.processes.push(meta);
this.commit('makeBehaviorDirty');
},
},
getters: {
customProcessMap: state => {
......@@ -101,16 +110,7 @@ export const behaviorStore = {
return map;
},
metaInUse: state => targetMetaID => {
let allMetasInUse = [];
for (let process of state.processes) {
let usedMetas = metaInUse(process, targetMetaID);
allMetasInUse.push(...usedMetas);
/*if (usedMetas.length > 0) {
result = true;
break;
}*/
}
return allMetasInUse;
return findMetaDepById(state.processes, targetMetaID);
},
metaIDExists: state => id => {
let result = false;
......@@ -129,7 +129,7 @@ export const behaviorStore = {
return state.projectData.views;
},
behavior_searchViewNode: state => uuid => {
if(uuid){
if (uuid) {
return searchViewNode(state.projectData.views, uuid);
}
}
......@@ -139,7 +139,7 @@ export const behaviorStore = {
commit('behavior_save');
return state.currentBehavior;
},
addCustomProcessMeta({commit, state}, {masterProcess, isInline, processId}) {
addCustomProcessMeta({commit, state}, {masterProcess, isInline, processId, name}) {
let meta = {
id: generateUUID(),
script: '',
......@@ -149,11 +149,11 @@ export const behaviorStore = {
};
switch (processId) {
case 'custom':
meta.name = i18n.t('Custom');
meta.name = name || i18n.t('Custom');
meta.output = ['success', 'failed'];
break;
case 'divider':
meta.name = i18n.t('Divider');
meta.name = name || i18n.t('Divider');
meta.output = ['p0'];
meta.isDivider = true;
break;
......@@ -172,9 +172,6 @@ export const behaviorStore = {
addProcessMeta(commit, isInline, masterProcess, meta);
return meta;
},
searchMeta({state}, {keyword}) {
searchMeta(state.processes, keyword);
},
}
};
......@@ -193,14 +190,6 @@ function addProcessMeta(commit, isInline, masterProcess, meta) {
}
}
function searchMeta(processes, keyword, path) {
for (let process of processes) {
/*if(process.id === keyword || process.name === keyword){
}*/
}
}
export function addBehavior({alias, from}, processes) {
let metaUUID = generateUUID();
let behavior = {
......@@ -225,7 +214,7 @@ export function addBehavior({alias, from}, processes) {
},
},
};
if(from){
if (from) {
p.from = from;
}
processes.push(p);
......@@ -242,7 +231,3 @@ export function deleteProcessMeta(metaId, processes) {
}
}
}
export function findProcess(metaId, processes) {
return arrayFind(processes, process => process.id === metaId);
}
......@@ -7,17 +7,18 @@ import {editorApi, projectApi} from "../../api"
import path from "path"
import generateUUID from "uuid/v4"
import {flattenViews, getCmpByUUID, getCmpProps} from '../../utils/common'
import {clonePureObj, getMockServeEnabled, saveAs} from "../../utils"
import {clonePureObj, findProcess, getMockServeEnabled, saveAs, traverseViewNode} from "../../utils"
import {template} from "../../template"
import events from "@/global-events"
import {packImages} from "../../utils/sheet-pack"
import {addBehavior, deleteProcessMeta, findProcess} from "./behavior"
import {addBehavior, deleteProcessMeta,} from "./behavior"
import db from "../../utils/db-storage"
import {preprocess} from "../../views/Preview/preview-preprocess"
import {packageStore} from "./package"
import {fetchApi} from "../../api/common"
import {toZeroing} from "psd-parse-web"
import {arrayFind} from "element-ui/src/utils/util";
const storeName = 'project';
const psStoreName = 'pack-history';
......@@ -52,16 +53,16 @@ function getDefaultOptions() {
function setUUIDForAllChildren(node) {
if (node.children && node.children.length > 0) {
for (let i = 0; i < node.children.length; i++) {
node.children[i] = copyBaseRoot(node.children[i])
node.children[i] = copyBaseRoot(node.children[i]);
setUUIDForAllChildren(node.children[i])
}
}
}
let copyNodeCatch = null
let copyNodeCatch = null;
function copyBaseRoot(node) {
let _node = JSON.parse(JSON.stringify(node))
let _node = JSON.parse(JSON.stringify(node));
let data;
......@@ -118,7 +119,7 @@ export const projectStore = {
setDirty(state, dirty = true) {
state.dirty = dirty;
},
makeDirty() {
makeProjectDirty() {
},
updateProject(state, project) {
......@@ -378,7 +379,7 @@ export const projectStore = {
copyNode(state, {node, parentNode}) {
let _node1 = node
let _node1 = node;
copyNodeCatch = _node1;
//localStorage.copyNodeCatch=_node1;
// let _node=copyBaseRoot(_node1);
......@@ -394,10 +395,10 @@ export const projectStore = {
},
pasteNode(state, {node, parentNode, pasteState}) {
let _node1 = copyNodeCatch;
console.log(copyNodeCatch)
console.log(copyNodeCatch);
if (_node1) {
let _node = copyBaseRoot(_node1);
setUUIDForAllChildren(_node)
setUUIDForAllChildren(_node);
if (pasteState == 1) {
parentNode = parentNode;
} else {
......@@ -482,6 +483,9 @@ export const projectStore = {
options(state) {
return state.data.options;
},
processes(state) {
return state.data.processes;
},
envKeys(state) {
return state.data.options.env.map(item => item.name);
},
......@@ -534,11 +538,48 @@ export const projectStore = {
return state.data.assets;
},
getProcess: state => behavior => {
return findProcess(behavior.meta, state.data.processes);
return arrayFind(state.data.processes, process => process.id === behavior.meta);
},
dependencies(state) {
return state.data.dependencies;
},
searchProcess: state => (keyword, searchField) => {
let result = [];
let paths = findProcess(state.data.processes, state.package.packages.process, function (process, meta) {
return meta[searchField] && meta[searchField].includes(keyword)
|| process.meta.includes(keyword)
|| (process.alias && process.alias.includes(keyword));
});
let entries = paths.map(path => path[0].id);
traverseViewNode(state.data.views, function (node, parent) {
for (let eventName in node.events) {
let event = node.events[eventName];
for (let behavior of event.behaviors) {
let index = entries.indexOf(behavior.meta);
if (index >= 0) {
let path = paths.splice(index, 1, null)[0];
path[0] = {
process: behavior,
metaName: path[0].name,
};
result.push({
node,
path,
});
}
}
}
});
/*for (let path of paths) {
if (path) {
result.push({
path,
})
}
}*/
return result;
},
},
actions: {
async saveToLocal({getters, commit}) {
......@@ -785,7 +826,7 @@ export const projectStore = {
},
addBehaviorDirect({state}, {behaviors, alias}) {
let behavior = addBehavior(alias, state.data.processes);
let behavior = addBehavior({alias, from: 'trigger'}, state.data.processes);
behaviors.push(behavior);
},
......@@ -828,15 +869,15 @@ async function getSheetUrlByUUID(uuid) {
const response = await fetchApi('/api/uuid2url', {
params: {uuid},
errMessage: 'Failed to get url',
})
});
return response.url
}
async function packAssets(assets) {
let failedList = []
let newAssets = assets.concat()
await packImages(newAssets, {getSheetUrlByUUID})
let failedList = [];
let newAssets = assets.concat();
await packImages(newAssets, {getSheetUrlByUUID});
for (let asset of newAssets) {
if (asset.file) {
......@@ -847,18 +888,18 @@ async function packAssets(assets) {
file: url,
type: 'sheet',
frames: asset.frames,
}
let sheetConfigFile = new File([JSON.stringify(sheetConfig)], 'sheet.json', {type: 'plain/text'})
};
let sheetConfigFile = new File([JSON.stringify(sheetConfig)], 'sheet.json', {type: 'plain/text'});
const {url: sheetConfigUrl} = await editorApi.uploadFile(sheetConfigFile, false, asset.sheetUUID).catch(e => {
failedList.push(asset)
})
asset.url = sheetConfigUrl
asset.uuid = generateUUID()
});
asset.url = sheetConfigUrl;
asset.uuid = generateUUID();
delete asset.file
delete asset.frames
delete asset.sheetUUID
delete asset.file;
delete asset.frames;
delete asset.sheetUUID;
}
}
......@@ -871,18 +912,3 @@ export async function updateMock(mocks) {
db.set('mock', mock);
}
}
function parseItem(list) {
let hasError, result = [];
for (let i = 0, li = list.length; i < li; i++) {
try {
result.push(JSON.parse(list[i].data));
} catch (e) {
hasError = true;
}
}
if (hasError) {
console.warn('parse env error');
}
return result;
}
......@@ -80,7 +80,9 @@
& > .icon > .operate-bar {
visibility: visible;
}
}
&.select{
& > .name > .full-name {
visibility: visible;
}
......
......@@ -263,7 +263,7 @@
.wrapper {
height: 50vh;
.form{
.form {
height: 100%;
display: flex;
flex-direction: column;
......@@ -272,7 +272,7 @@
margin-bottom: 20px;
}
.editor-form-item{
.editor-form-item {
flex: 1;
.el-form-item__content {
......@@ -288,6 +288,30 @@
}
}
.meta-search-dialog {
.wrapper{
padding: 10px;
height: 50vh;
display: flex;
flex-direction: column;
.inputs{
display: flex;
align-items: center;
margin-bottom: 5px;
.input-keyword{
flex: 1;
margin-right: 5px;
}
}
.scrollbar{
flex: 1;
}
}
}
.dialog-footer {
display: flex;
justify-content: space-between;
......
/**
* Created by rockyl on 2019-04-28.
*/
import Vue from 'vue';
export function addEntity(parent, target, pos) {
}
export function removeEntity(parent, target, pos) {
}
export function prepare(history){
history.regActions('add-entity', addEntity, removeEntity);
history.regActions('remove-entity', removeEntity, addEntity);
}
/**
* Created by rockyl on 2019-01-31.
*/
import Vue from 'vue'
const showLog = true;
export default class History extends Vue{
constructor(max = 200) {
super();
this.actions = {};
this.history = [];
this.position = -1;
this.max = max;
}
regActions(name, forward, backward) {
this.actions[name] = {
forward,
backward
};
}
reset() {
this.history.splice(0);
this._setPosition(- 1, true);
}
_setPosition(v, reset=false){
this.position = v;
this.$emit('change', {canUndo: this.canUndo, canRedo: this.canRedo, position: v, reset})
}
done(action, ...input) {
this.history.splice(this.position + 1);
this.history.push({
action,
input,
});
if (this.history.length > this.max) {
this.history.shift();
} else {
this._setPosition(this.history.length - 1);
}
if(showLog) console.log('done>', action, input);
}
undo() {
if (this.canUndo) {
const {action, input} = this.history[this.position];
let {backward} = this.actions[action];
backward(...input);
this._setPosition(this.position - 1);
if(showLog) console.log('undo>', action, input);
}
return this.canUndo;
}
redo() {
if (this.canRedo) {
this._setPosition(this.position + 1);
let {action, input} = this.history[this.position];
let {forward} = this.actions[action];
forward(...input);
if(showLog) console.log('redo>', action, input);
}
return this.canRedo;
}
get canUndo(){
return this.position >= 0
}
get canRedo(){
return this.position < this.history.length - 1
}
}
......@@ -77,6 +77,20 @@ export function guid() {
return `xy-${generateUUID()}`;
}
/**
* 遍历视图节点
* @param nodes
* @param callback
* @param parent
*/
export function traverseViewNode(nodes, callback, parent) {
for (let node of nodes) {
callback(node, parent);
if (node.children && node.children.length > 0) {
traverseViewNode(node.children, callback, node);
}
}
}
export function strEllipsis(str, maxLength = 0, rightOffset = 0) {
let result = str;
......@@ -110,20 +124,70 @@ export function updateProcesses(processes, targetMetaID, replaceMetaID) {
/**
* 过程元依赖过程
* @param process
* @param targetMetaID
* @param processMetas
* @param metaId
* @return {Array}
*/
export function metaInUse(process, targetMetaID) {
export function findMetaDepById(processMetas, metaId) {
let result = [];
for (let key in process.sub) {
let subProcess = process.sub[key];
if (subProcess.meta === targetMetaID) {
result.push(process);
//break;
findMetaDepOnce(processMetas,
function findProcessFilterById(process) {
return process.meta === metaId;
}, result);
return result;
}
function findMetaDepOnce(processMetas, filter, result, parentPath = []) {
for (let meta of processMetas) {
let path = parentPath.concat();
path.push(meta);
for (let key in meta.sub) {
let subProcess = meta.sub[key];
if (filter(subProcess)) {
path.push(subProcess);
result.push(path);
}
}
if (meta.metas && meta.metas.length > 0) {
findMetaDepOnce(meta.metas, filter, result, path);
}
}
}
export function findProcess(rootMetas, builtinMetas, filter) {
let result = [];
findOnce(rootMetas);
return result;
function findOnce(metas, parentPath) {
for (let meta of metas) {
let path = parentPath ? parentPath : [];
if (!parentPath) {
path.push(meta);
}
for (let key in meta.sub) {
let subProcess = meta.sub[key];
let subMeta = meta.metas ? meta.metas.find(item => item.id === subProcess.meta) : null;
if (!subMeta) {
subMeta = rootMetas.find(item => item.id === subProcess.meta);
}
if (!subMeta) {
subMeta = builtinMetas[subProcess.meta]
}
if (subMeta) {
let newPath = path.concat();
newPath.push({
process: subProcess,
metaName: subMeta.name,
});
if (filter(subProcess, subMeta)) {
result.push(newPath);
}
findOnce([subMeta], newPath);
}
}
}
}
}
/**
......@@ -324,6 +388,6 @@ export function getViewNodePath(node, sep = '/') {
return segs.join(sep)
}
export function timeFormat(time, format = 'YYYY-MM-DD HH:mm:ss'){
export function timeFormat(time, format = 'YYYY-MM-DD HH:mm:ss') {
return moment(time).format(format)
}
......@@ -18,11 +18,13 @@
<code-sync-serve-dialog ref="codeSyncServeDialog"/>
<missing-packages-dialog ref="missingPackagesDialog"/>
<px-skin-editor-dialog ref="pxSkinEditorDialog"/>
<process-search-dialog ref="processSearchDialog"/>
<behavior-editor-dialog @change="handleBehaviorsChange" ref="behaviorEditorDialog"></behavior-editor-dialog>
</div>
</template>
<script>
import {mapState, mapActions} from 'vuex'
import {mapState, mapActions, mapMutations} from 'vuex'
import SplitPanes from 'splitpanes'
import ToolBar from "./Editor/ToolBar";
import Inspector from "./Editor/Inspector";
......@@ -41,10 +43,14 @@
import MissingPackagesDialog from "./Editor/dialogs/MissingPackagesDialog";
import PackManagerDialog from "./Editor/dialogs/PackManagerDialog";
import PxSkinEditorDialog from "./Editor/dialogs/PxSkinEditorDialog";
import BehaviorEditorDialog from "./Editor/dialogs/BehaviorEditorDialog";
import ProcessSearchDialog from "./Editor/dialogs/ProcessSearchDialog";
export default {
name: 'Editor',
components: {
ProcessSearchDialog,
BehaviorEditorDialog,
PxSkinEditorDialog,
PackManagerDialog,
MissingPackagesDialog,
......@@ -88,6 +94,7 @@
this.clickMenu("preview");
});
events.$on('show-missing-packages', this.showMissingPackages);
events.$on('edit-behavior', this.editBehavior);
events.$on('show-code-sync-serve-dialog', () => {
this.$refs.codeSyncServeDialog.show();
......@@ -109,6 +116,7 @@
destroyed() {
document.removeEventListener('keydown', this.onKeyPress);
events.$off('show-missing-packages', this.showMissingPackages);
events.$off('edit-behavior', this.editBehavior);
},
methods: {
......@@ -229,6 +237,9 @@
case 'pack-manager':
this.$refs.packManagerDialog.show();
break;
case 'search':
await this.$refs.processSearchDialog.show();
break;
case 'mock':
this.$refs.mockEditorDialog.show();
break;
......@@ -312,6 +323,13 @@
showMissingPackages(missingPackages) {
this.$refs.missingPackagesDialog.show(missingPackages);
},
editBehavior(path, index) {
this.$refs.behaviorEditorDialog.show(path, index);
},
handleBehaviorsChange() {
this.makeProjectDirty();
},
...mapMutations(['makeProjectDirty']),
...mapActions([
'loadPackages',
'localVersionExist',
......
......@@ -22,7 +22,7 @@
</div>
<el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view">
<asset-list editable @show-file-details="showFileDetails" @click-item="onItemClick">
<asset-list ref="assetList" editable @show-file-details="showFileDetails" @click-item="onItemClick">
<div slot="first" class="file-uploader" @click="toUploadFile" @dragover.prevent @drop="onDropFile">
<i class="el-icon-plus file-uploader-icon"></i>
</div>
......@@ -42,6 +42,7 @@
import SplitPanes from 'splitpanes'
import {scanFiles, selectFile} from "../../utils";
import AssetList from "./Assets/AssetList";
import events from "@/global-events.js"
export default {
name: "Assets",
......@@ -60,6 +61,10 @@
},
mounted() {
this.currentItem = null;
events.$on('select-asset-item', this.selectAssetItem);
},
destroyed(){
events.$off('select-asset-item', this.selectAssetItem);
},
methods: {
toUploadFile() {
......@@ -100,6 +105,10 @@
}).catch((e) => {
});
},
selectAssetItem(asset){
this.currentItem = asset;
this.$refs.assetList.selectItem(asset);
},
...mapMutations([
'deleteAllAssets',
]),
......
<template>
<div class="file-list">
<slot name="first"></slot>
<file-item v-for="(asset, index) in assets" :editable="editable" :data="asset" :key="index" @show-file-details="showFileDetails(asset)"
@click="onClickItem(asset)"/>
<file-item v-for="(asset, index) in assets" :class="{select: isSelected(index)}" :editable="editable" :data="asset"
:key="index" @show-file-details="showFileDetails(asset)"
@click="onClickItem(asset)"
@delete="onDeleteItem(asset)"
/>
</div>
</template>
......@@ -14,9 +17,14 @@
name: "AssetList",
components: {FileItem},
props: {
editable:{
editable: {
type: Boolean,
default: false,
},
},
data() {
return {
selectedIndices: []
}
},
computed: {
......@@ -30,7 +38,23 @@
},
onClickItem(asset) {
this.$emit('click-item', asset);
this.selectItem(asset);
},
onDeleteItem(asset) {
if (this.isSelected(this.assets.indexOf(asset))) {
this.selectItem();
this.$emit('click-item', null);
}
},
selectItem(asset) {
this.selectedIndices.splice(0);
if (asset) {
this.selectedIndices.push(this.assets.indexOf(asset));
}
},
isSelected(index) {
return this.selectedIndices.includes(index);
}
}
}
</script>
......
......@@ -64,6 +64,7 @@
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this.$emit('delete', this.data);
this.deleteAsset(this.data.uuid);
}).catch((e) => {
});
......
......@@ -44,19 +44,17 @@
</el-collapse-transition>
</div>
</el-scrollbar>
<behavior-editor-dialog @change="handleBehaviorsChange" ref="behaviorEditorDialog"></behavior-editor-dialog>
</div>
</template>
<script>
import {mapGetters, mapActions, mapMutations} from 'vuex';
import BehaviorEditorDialog from "../dialogs/BehaviorEditorDialog";
import events from "@/global-events.js"
import EnabledSetter from "../components/EnabledSetter";
export default {
name: "BehaviorTab",
components: {EnabledSetter, BehaviorEditorDialog},
components: {EnabledSetter},
data() {
const builtinEvents = this.$t('events');
return {
......@@ -116,7 +114,7 @@
alias, behaviors,
});
this.makeDirty();
this.makeProjectDirty();
},
editTriggerName(name) {
this.$prompt(this.$t('Input event name'), this.$t('Rename event'), {
......@@ -137,7 +135,7 @@
this.$set(events, value, events[name]);
this.$delete(events, name);
this.makeDirty();
this.makeProjectDirty();
}
}
}).catch(() => {
......@@ -146,20 +144,13 @@
deleteTrigger(name) {
this.$delete(this.activeComponent.events, name);
this.makeDirty();
this.makeProjectDirty();
},
editBehavior(behavior, behaviors) {
this.$refs.behaviorEditorDialog.show(behavior, behaviors);
},
handleBehaviorsChange(isPreview) {
if (isPreview) {
events.$emit('save-and-preview')
}
this.makeDirty();
events.$emit('edit-behavior', [{process: behavior}]);
},
onBehaviorNameChange(){
this.makeDirty();
this.makeProjectDirty();
},
async deleteBehavior(index, behaviors) {
let deleteMeta = false;
......@@ -172,7 +163,7 @@
}).then(() => {
deleteMeta = true;
this.makeDirty();
this.makeProjectDirty();
}).catch(action => {
if(action === 'close'){
close = true;
......@@ -194,7 +185,7 @@
'deleteBehaviorDirect',
]),
...mapMutations([
'makeDirty',
'makeProjectDirty',
]),
}
}
......
......@@ -76,8 +76,6 @@
import {selectFile} from "../../utils";
import events from "../../global-events";
let PSD = window['require']('psd');
export default {
name: 'Views',
components: {Pane},
......@@ -95,6 +93,12 @@
nodeFilterPresets: this.$t('nodeFilterPresets'),
};
},
mounted() {
events.$on('locate-view-node', this.locateViewNode);
},
destroyed() {
events.$off('locate-view-node', this.locateViewNode);
},
computed: {
...mapGetters(['activeComponentId']),
...mapState({
......@@ -112,6 +116,10 @@
},
},
methods: {
locateViewNode(node) {
this.$refs.tree.setCurrentKey(node.uuid);
this.expandedKeys = [node.uuid];
},
updateFilter() {
if (this.$refs.tree) {
this.$refs.tree.filter(this.filterText);
......@@ -191,11 +199,15 @@
this.filterText = filterText;
},
allowDrag(draggingNode) {
return draggingNode.parent.parent;
//return draggingNode.parent.parent;
return true;
},
allowDrop(draggingNode, dropNode, type) {
//console.log(dropNode.parent.parent, type);
return dropNode.parent.parent || type === 'inner';
if(!draggingNode.parent.parent){ //根视图拖动
return !dropNode.parent.parent && type !== 'inner';
}else{
return dropNode.parent.parent || type === 'inner';
}
},
/**
* 点击左侧视图列表
......
......@@ -12,7 +12,7 @@
<div class="center full-size background" splitpanes-min="20" :splitpanes-size="70">
<div class="top-bar">
<edit-path :processStack="processStack" @pop="onPop"/>
<el-button icon="el-icon-search" circle plain size="mini" @click="onClickSearch"></el-button>
<!--<el-button icon="el-icon-search" circle plain size="mini" @click="onClickSearch"></el-button>-->
</div>
<!--<div class="operate-bar">
<el-button-group>
......@@ -21,14 +21,17 @@
<el-button size="mini" icon="el-icon-zoom-in" @click="setScale(0.1)"/>
</el-button-group>
</div>-->
<board ref="board" @select-process-node="onSelectProcessNode" @edit-process="editProcess" @edit-meta="onEditMeta"/>
<board ref="board"
@select-process-node="onSelectProcessNode"
@edit-process="editProcess"
@translate-to-normal="onTranslateToNormal"
@edit-meta="onEditMeta"/>
</div>
<div class="properties background full-size" splitpanes-min="20" :splitpanes-size="20">
<properties-editor ref="properties"/>
</div>
</split-panes>
<meta-editor-dialog ref="metaEditorDialog" @input="onSaveMeta"/>
<meta-search-dialog ref="metaSearchDialog"/>
</div>
</template>
......@@ -41,12 +44,10 @@
import EditPath from "./Board/EditPath";
import Process from "./Board/Process";
import MetaEditorDialog from "./MetaEditorDialog";
import events from "@/global-events.js"
import MetaSearchDialog from "./MetaSearchDialog";
export default {
name: "BehaviorEditor",
components: {MetaSearchDialog, MetaEditorDialog, PropertiesEditor, EditPath, ProcessList, Board, SplitPanes,},
components: {MetaEditorDialog, PropertiesEditor, EditPath, ProcessList, Board, SplitPanes,},
props: [],
data() {
return {
......@@ -62,8 +63,10 @@
]
},
...mapState({
data: state => state.project.data,
behavior: state => state.behavior.currentBehavior,
processStack: state=>state.behavior.processStack,
processStack: state => state.behavior.processStack,
lastProcess: state => state.behavior.lastProcess,
}),
...mapGetters([
'processTree',
......@@ -77,38 +80,50 @@
}
}
},
edit() {
edit(path, index = 0) {
this.behavior_startEdit({
originData: this.data,
behavior: path[0].process,
});
this.clearProcessStack();
let process = new Process(null, this.behavior, this.resolveProcess);
this.editProcess(process);
let parent;
for (let i = 0; i <= index; i++) {
const item = path[i];
let subProcess = new Process(parent, item.process, this.resolveProcess);
this.pushProcessStack(subProcess);
parent = subProcess;
}
this.editProcess(parent, false);
},
onSelectProcessNode(process) {
this.$refs.properties.edit(process);
},
editProcess(process) {
this.pushProcessStack(process);
editProcess(process, editStack = true) {
if (editStack) {
this.pushProcessStack(process);
}
this.$refs.board.edit(process, this.resolveProcess);
this.$refs.properties.edit();
},
onPop(index) {
this.popProcessStack(index + 1);
let process = this.processStack[this.processStack.length - 1];
this.$refs.board.edit(process, this.resolveProcess);
this.$refs.board.edit(process, this.resolveProcess, this.lastProcess);
this.$refs.properties.edit();
},
onClickSearch(){
this.$refs.metaSearchDialog.show();
},
onEditMeta(meta) {
onEditMeta(meta, parentMeta) {
this.metaInEditing = meta;
this.$refs.metaEditorDialog.edit(meta);
this.$refs.metaEditorDialog.edit(meta, parentMeta);
},
onDeleteMeta(meta) {
const metaInUse = this.$store.getters.metaInUse(meta.id);
if (metaInUse.length > 0) {
console.log(metaInUse);
let metas = metaInUse.map(meta=>`<b>→ ${meta.name}</b>`).join('<br>');
this.$alert(metas, this.$t('Meta is in use, can not delete'),{
const requirePaths = this.$store.getters.metaInUse(meta.id);
if (requirePaths.length > 0) {
let metas = requirePaths.map(path => {
return path.map(meta => `<b>${(meta.name || meta.meta) + (meta.alias ? '[' + meta.alias + ']' : '')}</b>`).join(' > ')
}).join('<br>');
this.$alert(metas, this.$t('Meta is in use, can not delete'), {
dangerouslyUseHTMLString: true,
})
.catch((e) => {
......@@ -124,7 +139,7 @@
});
}
},
onSaveMeta(meta,isPreview) {
onSaveMeta(meta) {
let oldMetaID = this.metaInEditing.id;
for (let key in meta) {
this.metaInEditing[key] = meta[key];
......@@ -137,12 +152,16 @@
});
}
this.$refs.board.updateProcessNode(meta.id);
//是否预览
if(isPreview){
events.$emit('behaviorSave',isPreview)
}
},
onTranslateToNormal(meta, parentMeta) {
this.$confirm(this.$t('Are you sure to translate to normal process?'), this.$t('Alert'))
.then(() => {
this.translateToNormal({meta, parentMeta});
}).catch(e => {
})
},
...mapMutations([
'behavior_startEdit',
'updateProcesses',
'deleteProcessMeta',
'updatePropsEditable',
......@@ -150,6 +169,7 @@
'pushProcessStack',
'popProcessStack',
'setScale',
'translateToNormal',
]),
...mapGetters([
'metaInUse',
......
......@@ -12,7 +12,7 @@
<g id="nodes">
<process-node v-for="(process, key, index) of subProcessMap" :ref="'pn_' + key" :process="process"
:key="index"
@click="onClickProcessNode(process, key)"
@click="selectProcessNode(process, key)"
@hover-pin="onPinHover"
@leave-pin="onPinLeave"
@down-pin="onPinDown"
......@@ -22,6 +22,7 @@
@edit-meta="onEditMeta"
@dblclick="editSubProcess(process)"
@meta-modified="onProcessMetaModified"
@translate-to-normal="onTranslateToNormal"
/>
</g>
</g>
......@@ -92,7 +93,9 @@
'setScale',
'makeBehaviorDirty',
]),
async edit(process, resolveProcess) {
async edit(process, resolveProcess, lastProcess) {
//this.lastProcess = this.process;
this.boardOffset.x = 0;
this.boardOffset.y = 0;
this.setScale(0);
......@@ -107,6 +110,10 @@
this.updateSubProcess();
this.measure();
this.$nextTick(function(){
this.selectProcessNode(lastProcess);
});
},
updateSubProcess() {
this.subProcessMap = {};
......@@ -124,20 +131,40 @@
}
},
showInlineChoose() {
return this.$confirm(this.$t('As inline'));
return this.$confirm(this.$t('As inline'), {
distinguishCancelAndClose: true,
});
},
async addSubProcessData(processId, pos) {
let meta, isInline;
if (customs.includes(processId)) {
let name;
if (processId === 'custom') {//自定义过程必须输入名称
let result = await this.$prompt(this.$t('Input custom process name'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
inputPattern: /^.{1,64}$/,
inputErrorMessage: this.$t('Invalid name'),
}).catch(e => {
});
if (result) {
name = result.value;
}
if (!name) {
return;
}
}
try {
await this.showInlineChoose();
isInline = true;
} catch (e) {
} catch (action) {
if (action === 'close') {
return;
}
}
meta = await this.addCustomProcessMeta({masterProcess: this.process, isInline, processId});
meta = await this.addCustomProcessMeta({masterProcess: this.process, isInline, processId, name});
} else {
meta = this.process.resolveMeta(processId);
}
......@@ -146,8 +173,10 @@
try {
await this.showInlineChoose();
isInline = true;
} catch (e) {
} catch (action) {
if (action === 'close') {
return;
}
}
meta = await this.addProcessFromPrefab({masterProcess: this.process, isInline, meta})
}
......@@ -164,7 +193,6 @@
this.$set(this.process.meta, 'sub', {});
}
this.$set(this.process.meta.sub, data.uuid, data);
this.makeBehaviorDirty();
return data;
},
onDragOver(e) {
......@@ -174,8 +202,11 @@
}
},
async onDrop(e) {
const processId = e.dataTransfer.getData('process');
if(!this.editable){
return;
}
const processId = e.dataTransfer.getData('process');
if (processId === 'entry') {
return;
}
......@@ -188,6 +219,7 @@
return;
}
this.addSubProcess(data.uuid, data);
this.makeBehaviorDirty();
this.$nextTick(() => {
events.$emit('update-dock-pin-pos');
......@@ -263,7 +295,7 @@
for (let key in process.data.output) {
let item = process.data.output[key];
let index = item.indexOf(outputID);
if(index >= 0){
if (index >= 0) {
item.splice(index, 1);
}
}
......@@ -321,10 +353,10 @@
this.$delete(this.process.meta.sub, process.uuid);
let remain = 0;
for(let key in this.process.meta.sub){
for (let key in this.process.meta.sub) {
let p = this.process.meta.sub[key];
if (p.meta === meta.id) {
remain ++;
remain++;
}
}
......@@ -338,7 +370,7 @@
}
if (meta.isDivider || meta.isInline) { //如果是分流节点或者内联节点还要删除对应的meta
if(remain === 0){
if (remain === 0) {
this.deleteProcessMeta({
meta,
process: this.process,
......@@ -361,11 +393,14 @@
});
}
},
onProcessNodeMove(data){
onProcessNodeMove(data) {
this.makeBehaviorDirty();
},
onEditMeta(data, meta) {
this.$emit('edit-meta', meta);
onEditMeta(meta, parentMeta) {
this.$emit('edit-meta', meta, parentMeta);
},
onTranslateToNormal(meta) {
this.$emit('translate-to-normal', meta, this.process.meta);
},
editSubProcess(process) {
if (!process.meta.isDivider && this.editable && (process.meta.type !== 'builtin' || process.meta.sub && Object.keys(process.meta.sub).length > 0)) {
......@@ -387,13 +422,16 @@
}
this.makeBehaviorDirty();
},
onClickProcessNode(process, uuid) {
selectProcessNode(process) {
if(!process){
return;
}
for (let key in this.$refs) {
if (key.startsWith('pn_')) {
const processNode = this.$refs[key][0];
if (processNode) {
let hint = 'pn_' + uuid === key;
let hint = 'pn_' + process.uuid === key;
processNode.setActive(hint);
if (hint && this.selectedProcessNode !== processNode) {
this.selectedProcessNode = processNode;
......
......@@ -10,16 +10,24 @@ export default class Process {
this.meta = typeof data.meta === 'string' ? this.resolveMeta(data.meta) : data.meta;
}
get data(){
get data() {
return this._data;
}
get parent() {
return this._parent;
}
get uuid() {
return this._data.uuid;
}
resolveMeta(id) {
let meta = this.meta && this.meta.metas ? this.meta.metas.find(meta=>meta.id === id) : null;
let meta = this.meta && this.meta.metas ? this.meta.metas.find(meta => meta.id === id) : null;
if (!meta && this._parent) {
meta = this._parent.resolveMeta(id);
}
if(!meta){
if (!meta) {
meta = this._resolveProcess(id);
}
......
......@@ -4,6 +4,7 @@
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave" @click="onClick" @dblclick="onDblclick">
<div class="top-bar" v-if="meta.id !== 'entry' && editable">
<el-link icon="el-icon-magic-stick" :underline="false" v-if="meta.isInline" @mousedown.stop.prevent @click.stop="onClickTranslate"/>
<el-link icon="el-icon-delete" :underline="false" @mousedown.stop.prevent @click.stop="onClickDelete"/>
<el-link icon="el-icon-edit" :underline="false" v-if="meta.type !== 'builtin' && !meta.isDivider"
@mousedown.stop.prevent @click.stop="onClickEdit"/>
......@@ -248,11 +249,17 @@
this.$emit('down-pin', e, this.data, pin);
}
},
onClickTranslate() {
this.$emit('translate-to-normal', this.meta);
},
onClickDelete() {
this.$emit('delete', this.data, this.meta);
this.$confirm(this.$t('Are you sure to delete this process?'))
.then(()=>{
this.$emit('delete', this.data, this.meta);
}).catch(e=>{});
},
onClickEdit() {
this.$emit('edit-meta', this.data, this.meta);
this.$emit('edit-meta', this.meta);
},
onClickCopy() {
this.$emit('copy', this.data, this.meta);
......
<template>
<el-dialog :title="$t('Meta Search')" width="80%" :visible.sync="visible"
:append-to-body="true"
custom-class="behavior-editor-dialog"
>
<div style="padding: 10px;">
<el-input size="mini" v-model="keyword">
<el-button slot="append" icon="el-icon-search" @click="search"></el-button>
</el-input>
<div style="height: 50vh;">
</div>
</div>
<div slot="footer" class="dialog-footer">
<div></div>
<el-button size="mini" plain @click="close">{{$t('Close')}}</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: "MetaSearchDialog",
data() {
return {
visible: false,
keyword: '',
}
},
methods:{
show(){
this.visible = true;
},
close(){
this.visible = false;
},
search(){
},
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
......@@ -70,10 +70,10 @@
}),
},
methods: {
transName(meta, node){
if(node.parent.parent){
transName(meta, node) {
if (node.parent.parent) {
return meta.name
}else{
} else {
return this.$t('processGroups')[meta.name] || meta.name;
}
},
......
......@@ -32,7 +32,6 @@
},
computed: {
...mapState({
data: state => state.project.data,
showBadge: state => state.behavior.dirty,
}),
},
......@@ -42,28 +41,24 @@
});
},
methods: {
show(behavior, behaviors) {
this.behaviors = behaviors;
this.behavior_startEdit({
originData: this.data,
behavior,
});
show(path, index) {
this.path = path;
this.index = index;
//this.behaviors = behaviors;
this.editorReady = false;
this.visible = true;
},
async onSave(isPreview) {
async onSave() {
const behavior = await this.behavior_save();
for (let i = 0, li = this.behaviors.length; i < li; i++) {
/*for (let i = 0, li = this.behaviors.length; i < li; i++) {
const b = this.behaviors[i];
if (b.uuid === behavior.uuid) {
this.$set(this.behaviors, i, behavior);
break;
}
}
this.$emit('change', isPreview);
if (!isPreview) {
this.visible = false;
}
}*/
this.$emit('change');
this.visible = false;
},
beforeClose(done) {
if (this.$store.state.behavior.dirty) {
......@@ -93,12 +88,12 @@
onOpened() {
this.editorReady = true;
this.$nextTick(() => {
this.$refs.behaviorEditor.edit();
});
this.$nextTick(this.startEdit);
},
startEdit() {
this.$refs.behaviorEditor.edit(this.path, this.index);
},
...mapMutations([
'behavior_startEdit',
'behavior_cancel',
]),
...mapActions([
......
......@@ -49,7 +49,7 @@
type="success"
@click="copyTpl(scope.row.packResult.tpl)"
>{{$t('Copy skin')}}</el-button>
<el-popconfirm :title="$t('Are sure to delete this item?')"
<el-popconfirm :title="$t('Are you sure to delete this item?')"
@onConfirm="deleteItem(scope.row.id)"
placement="top">
<el-button
......
<template>
<el-dialog :title="$t('Process Search')" width="80%" :visible.sync="visible"
:append-to-body="true"
custom-class="meta-search-dialog"
>
<div class="wrapper">
<div class="inputs">
<el-input class="input-keyword" size="mini" v-model="keyword" clearable @change="search">
<span slot="prepend">关键词</span>
<el-button slot="append" icon="el-icon-search" @click="search"></el-button>
</el-input>
<el-radio-group v-model="searchType">
<el-radio :label="1">{{$t('Name')}}</el-radio>
<el-radio :label="2">ID</el-radio>
<el-radio :label="3">{{$t('Code')}}</el-radio>
</el-radio-group>
</div>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden">
<div v-for="(item, key) in paths" :key="key">
<template v-if="item.node">
<el-link type="success" icon="el-icon-aim" :underline="false" @click="clickMeta(item.node)">
{{item.node.name}}
</el-link>
<span> > </span>
</template>
<span v-for="(p, index) in item.path" :key="index">
<el-link type="primary" :underline="false" icon="el-icon-aim" @click="clickMeta(item.path, index)">
<span>{{p.metaName || p.process.meta}}</span>
<span v-if="p.process.alias">[{{p.process.alias}}]</span>
</el-link>
<!--<template v-if="meta.meta">
<span v-if="meta.meta">{{meta.meta}}
<span v-if="meta.alias">[{{meta.alias}}]</span>
</span>
</template>-->
<span v-if="index < item.path.length-1"> > </span>
</span>
</div>
</el-scrollbar>
</div>
<div slot="footer" class="dialog-footer">
<div></div>
<el-button size="mini" plain @click="close">{{$t('Close')}}</el-button>
</div>
</el-dialog>
</template>
<script>
import {mapGetters} from 'vuex'
import events from "@/global-events.js"
const searchFieldMap = [
'',
'name',
'id',
'script',
];
export default {
name: "ProcessSearchDialog",
data() {
return {
visible: false,
keyword: '',
searchType: 1,
paths: [],
}
},
computed: {
...mapGetters([
'searchProcess'
])
},
watch: {
searchType(){
this.search();
},
},
methods: {
show() {
this.visible = true;
},
close() {
this.visible = false;
},
search() {
if(this.keyword){
this.paths = this.searchProcess(this.keyword, searchFieldMap[this.searchType]);
}
},
clickMeta(path, index) {
if (index === undefined) { //view node
events.$emit('locate-view-node', path)
} else { //process
events.$emit('edit-behavior', path, index)
}
this.visible = false;
},
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
......@@ -41,7 +41,7 @@
@click="copyUrl(scope.row)"
>{{$t('Copy url')}}
</el-button>
<el-popconfirm :title="$t('Are sure to delete this item?')"
<el-popconfirm :title="$t('Are you sure to delete this item?')"
@onConfirm="deleteItem(scope.row)"
placement="top">
<el-button
......
......@@ -55,7 +55,7 @@
})
},
methods: {
...mapMutations(["modifyProjectx", 'makeDirty']),
...mapMutations(["modifyProjectx", 'makeProjectDirty']),
edit() {
this.editData = clonePureObj(this.pxEnv);
},
......@@ -63,7 +63,7 @@
this.modifyProjectx(this.editData);
this.$emit('save');
if (this.showBottomBar) {
this.makeDirty();
this.makeProjectDirty();
}
},
getTpl(tpl) {
......
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