Commit b6bd85db authored by 劳工's avatar 劳工

Merge branch 'feature/huhu' into 'master'

Feature/huhu

See merge request !2
parents 5b7783cb a945a697
...@@ -54,12 +54,19 @@ function execute(document) { ...@@ -54,12 +54,19 @@ function execute(document) {
var newName = layer.name.replace(mergeFlagReg, ''); var newName = layer.name.replace(mergeFlagReg, '');
layer.name = newName; layer.name = newName;
mergeLayers.push(newName); try {
layer.merge();
mergeLayers.push(newName);
}catch (e) {
layer.merge(); }
return 1; return 1;
} else if (layer.name.match(excludeFlagReg)) { } else if (layer.name.match(excludeFlagReg)) {
layer.remove(); try {
layer.remove();
}catch (e) {
alert('移除图层' + layer.name + '失败,可能含有锁的层');
}
return 2; return 2;
} }
return 0; return 0;
......
{ {
"name": "zeroing-editor", "name": "zeroing-editor",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"upload": "cp dist/index.html ../duiba-aurora-node/src/public/index.html&&ali-oss-publish -c oss.config.js -e dist", "upload": "node scripts/copy-index.js && ali-oss-publish -c oss.config.js -e dist",
"build:upload": "npm run build&&npm run upload", "build:upload": "npm run build&&npm run upload",
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'" "i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'"
}, },
"dependencies": { "dependencies": {
"cookie": "^0.4.0", "codemirror": "^5.50.0",
"copy-to-clipboard": "^3.2.0", "cookie": "^0.4.0",
"core-js": "^2.6.5", "copy-to-clipboard": "^3.2.0",
"element-ui": "^2.4.5", "core-js": "^2.6.5",
"jszip": "^3.2.2", "element-ui": "^2.4.5",
"moment": "^2.24.0", "indexdbwrapper": "^1.0.4",
"path": "^0.12.7", "jszip": "^3.2.2",
"querystringify": "^2.1.1", "moment": "^2.24.0",
"splitpanes": "^1.14.5", "monaco-editor": "^0.19.0",
"string-width": "^4.1.0", "monaco-editor-webpack-plugin": "^1.8.1",
"uuid": "^3.3.3", "path": "^0.12.7",
"vue": "^2.6.10", "querystringify": "^2.1.1",
"vue-i18n": "^8.0.0", "semver": "^7.1.1",
"vue-router": "^3.0.3", "socket.io-client": "^2.3.0",
"vuex": "^3.0.1" "splitpanes": "^1.14.5",
}, "string-width": "^4.1.0",
"devDependencies": { "uuid": "^3.3.3",
"@kazupon/vue-i18n-loader": "^0.3.0", "vue": "^2.6.10",
"@vue/cli-plugin-babel": "^3.11.0", "vue-codemirror": "^4.0.6",
"@vue/cli-service": "^3.11.0", "vue-i18n": "^8.0.0",
"ali-oss-publish": "^0.3.0", "vue-router": "^3.0.3",
"lodash": "^4.17.15", "vuex": "^3.0.1",
"node-sass": "^4.9.2", "zeroing-code-divider": "http://gitlab2.dui88.com/laoqifeng/zeroing-code-divider.git"
"sass": "^1.22.9", },
"sass-loader": "^7.2.0", "devDependencies": {
"vue-cli-plugin-element": "^1.0.1", "@kazupon/vue-i18n-loader": "^0.3.0",
"vue-cli-plugin-i18n": "^0.6.0", "@vue/cli-plugin-babel": "^3.11.0",
"vue-draggable-resizable": "^2.0.1", "@vue/cli-service": "^3.11.0",
"vue-template-compiler": "^2.6.10" "ali-oss-publish": "^0.3.0",
}, "lodash": "^4.17.15",
"appVersion": "0.1.0" "node-sass": "^4.9.2",
"sass": "^1.22.9",
"sass-loader": "^7.2.0",
"vue-cli-plugin-element": "^1.0.1",
"vue-cli-plugin-i18n": "^0.6.0",
"vue-draggable-resizable": "^2.0.1",
"vue-template-compiler": "^2.6.10"
},
"appVersion": "0.1.0"
} }
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>烽火台</title> <title>烽火台</title>
<script src="https://cdn.bootcss.com/jshint/2.10.2/jshint.min.js"></script>
<script src="https://cdn.bootcss.com/jsonlint/1.6.0/jsonlint.min.js"></script>
</head> </head>
<body> <body>
<noscript> <noscript>
...@@ -14,8 +17,9 @@ ...@@ -14,8 +17,9 @@
</noscript> </noscript>
<script> <script>
window.__data = { window.__data = {
token : "<%= process.env.NODE_ENV === 'development' ? 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MjA4NSwibmFtZSI6IuWKs-eQquWzsCIsImFjY291bnQiOiJsYW9xaWZlbmciLCJlbWFpbCI6Imxhb3FpZmVuZ0BkdWliYS5jb20uY24iLCJtb2JpbGUiOiIxMzQ1Njc3NDE1MyIsImRpbmdVc2VySWQiOiI1MDY5MDYyOTIxMjkxMDAxIiwibW9kZWxBdXRob3JpemUiOmZhbHNlLCJsb2dpblRpbWVvdXQiOjc5MTUwLCJnbXRDcmVhdGUiOjE1MzQ0MTIwMTUwMDAsImdtdE1vZGlmaWVkIjoxNTczODgyMDk2MDAwLCJzeXN0ZW1JZFNldCI6WzAsMSwzNCwyMjcsMzYsMzcsMjMxLDIwMywxOSwyMSwyNDYsMjMsMjUsMjQ5LDI3LDMwXSwiaWF0IjoxNTc0NzU4OTk5fQ.tlRIqNZ3t1-5ZjWBuB7sASQuDBQ03gjfv0aDC_Ba_3k' : '$TOKEN$' %>", token : "<%= process.env.NODE_ENV === 'development' ? 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MjU0NywibmFtZSI6IuWNnum-meS6rSIsImFjY291bnQiOiJiaWFubG9uZ3RpbmciLCJlbWFpbCI6ImJpYW5sb25ndGluZ0BkdWliYS5jb20uY24iLCJtb2JpbGUiOiIxNTg2OTE3NDMxMSIsImRpbmdVc2VySWQiOiIwMTE1MTgyMTEyMjE3OTYzNzAiLCJtb2RlbEF1dGhvcml6ZSI6ZmFsc2UsImxvZ2luVGltZW91dCI6NzczNjEsImdtdENyZWF0ZSI6MTU1OTUyNjcxNDAwMCwiZ210TW9kaWZpZWQiOjE1NzM3NDk1OTEwMDAsInN5c3RlbUlkU2V0IjpbMCwxLDM0LDM2LDM3LDIzMSwyMDMsNDQsMTksMjEsMjQ2LDIzLDI0OSwyNSwyNywzMF0sImlhdCI6MTU3Nzk1MjgxNH0.GNeP25ZZK5ca7rvwtj2BaQ3GZatGNSR3CthSN98PSD4' : '$TOKEN$' %>",
apiHost : "<%= process.env.NODE_ENV === 'development' ? 'http://beacon.duibadev.com.cn' : '$API_HOST$' %>" apiHost : "<%= process.env.NODE_ENV === 'development' ? 'http://beacon.duibadev.com.cn' : '$API_HOST$' %>",
types: `$TYPES$`
} }
</script> </script>
<div id="app"></div> <div id="app"></div>
......
/**
* Created by rockyl on 2019-12-26.
*/
const fs = require('fs');
fs.copyFileSync('dist/index.html', '../duiba-aurora-node/src/public/index.html');
const now = Date.now();
fs.copyFileSync('dist/index.html', `dist/index.${now}.html`);
console.log('node update-editor.js', now);
...@@ -21,29 +21,30 @@ class ApiError extends Error { ...@@ -21,29 +21,30 @@ class ApiError extends Error {
} }
} }
export async function fetchApi(uri, {params, method = 'get', contentType = 'json', errMessage} = { export async function fetchApi(uri, {params, method = 'get', auth = true, judgeSuccess = true, contentType = 'json', errMessage} = {
method: 'get', method: 'get',
contentType: 'json' contentType: 'json'
}) { }) {
let url = API_HOST + uri; let url = uri.startsWith('http') || uri.startsWith('//') ? uri : API_HOST + uri;
const options = { const options = {
method, method,
headers: { headers: {},
authorization: 'Bearer ' + window['zeroing_token'],
},
//credentials: 'include', //credentials: 'include',
}; };
if (auth) {
options.headers.authorization = 'Bearer ' + window['zeroing_token'];
}
if (params) { if (params) {
if (method.toLowerCase() === 'post') { if (method.toLowerCase() === 'post') {
switch(contentType){ switch (contentType) {
case 'form-data': case 'form-data':
let formData = new FormData(); let formData = new FormData();
for (let key in params) { for (let key in params) {
let value = params[key]; let value = params[key];
if(value instanceof File){ if (value instanceof File) {
formData.append(key, value, value.name); formData.append(key, value, value.name);
}else{ } else {
formData.append(key, value); formData.append(key, value);
} }
} }
...@@ -59,7 +60,8 @@ export async function fetchApi(uri, {params, method = 'get', contentType = 'json ...@@ -59,7 +60,8 @@ export async function fetchApi(uri, {params, method = 'get', contentType = 'json
break; break;
} }
} else { } else {
url += (url.indexOf('?') < 0 ? '?' : '') + (url.endsWith('&') ? '' : '&') + stringify(params); url += (url.indexOf('?') < 0 ? '?' : '');
url += (url.endsWith('?') ? '' : '&') + stringify(params);
} }
} }
...@@ -70,8 +72,12 @@ export async function fetchApi(uri, {params, method = 'get', contentType = 'json ...@@ -70,8 +72,12 @@ export async function fetchApi(uri, {params, method = 'get', contentType = 'json
const jsonObj = await response.json(); const jsonObj = await response.json();
//console.log(jsonObj); //console.log(jsonObj);
if (jsonObj.success) { if (judgeSuccess) {
return jsonObj.data; if (jsonObj.success) {
return jsonObj.data;
}
} else {
return jsonObj;
} }
throw new ApiError('call api failed', jsonObj.code, errMessage); throw new ApiError('call api failed', jsonObj.code, errMessage);
......
/**
* Created by rockyl on 2019-09-19.
*
* 包相关api
*/
import {fetchApi} from "./common";
export async function fetchPackages(ids) {
return await fetchApi('/api/package/query', {
params: {
ids,
},
errMessage: 'Failed to fetch packages',
})
}
...@@ -6,8 +6,9 @@ ...@@ -6,8 +6,9 @@
import {fetchApi} from "./common"; import {fetchApi} from "./common";
export async function fetchAll() { export async function fetchAll(currentPage, pageSize, onlyMine) {
return await fetchApi('/api/project/query', { return await fetchApi('/api/project/query', {
params: {currentPage, pageSize, isAll: onlyMine ? 0 : 1},
errMessage: 'Failed to fetch projects', errMessage: 'Failed to fetch projects',
}) })
} }
...@@ -44,7 +45,24 @@ export async function fetchOne(id) { ...@@ -44,7 +45,24 @@ export async function fetchOne(id) {
}) })
} }
export async function saveOne(project) { export async function fetchOneFromDataUrl(dataUrl) {
return await fetchApi(dataUrl, {
auth: false,
judgeSuccess: false,
errMessage: 'Failed to fetch project',
})
}
export async function fetchHistory(id, currentPage, pageSize) {
return await fetchApi('/api/project/history', {
params: {id, currentPage, pageSize},
method: 'get',
errMessage: 'Failed to history',
})
}
export async function saveOne(project, remark) {
project.remark = remark;
return await fetchApi('/api/project/update', { return await fetchApi('/api/project/update', {
params: project, params: project,
method: 'post', method: 'post',
...@@ -52,9 +70,9 @@ export async function saveOne(project) { ...@@ -52,9 +70,9 @@ export async function saveOne(project) {
}) })
} }
export async function pack(id) { export async function pack(id, debug, packedAssets) {
return await fetchApi('/api/project/pack', { return await fetchApi('/api/project/pack', {
params: {id}, params: {id, debug, packedAssets},
method: 'post', method: 'post',
errMessage: 'Failed to pack project', errMessage: 'Failed to pack project',
}) })
...@@ -73,11 +91,13 @@ export async function importView(file) { ...@@ -73,11 +91,13 @@ export async function importView(file) {
return response; return response;
} }
export async function uploadFile(file) { export async function uploadFile(file, compress = false) {
let params = {file};
if (compress) {
params.compress = true;
}
const response = await fetchApi('/api/uploadFile', { const response = await fetchApi('/api/uploadFile', {
params: { params,
file,
},
method: 'post', method: 'post',
contentType: 'form-data', contentType: 'form-data',
errMessage: 'Failed to upload file', errMessage: 'Failed to upload file',
......
/**
* Created by rockyl on 2019-12-25.
*/
import io from 'socket.io-client';
import events from './global-events';
let socket;
export let codeSyncServeEnabled = false;
export function startCodeSyncServe(config) {
if (socket) {
socket.close();
}
socket = io(`http://${config.ip}:${config.port}`);
socket.on('connect', onConnect);
socket.on('disconnect', onDisconnect);
socket.on('edit-save', onEditSave);
events.$on('edit-open', editCode);
}
export function stop() {
}
export function editCode(code) {
if(socket && socket.connected){
socket.emit('edit-open', code);
}
}
function onConnect(t) {
codeSyncServeEnabled = true;
events.$emit('code-sync-start');
}
function onDisconnect() {
codeSyncServeEnabled = false;
events.$emit('code-sync-stop');
}
function onEditSave(data) {
events.$emit('edit-save', data)
}
<template>
<div style="width: 100%; height: 100%" ref="container"></div>
</template>
<script>
import * as monaco from 'monaco-editor';
export default {
name: "MonacoEditor",
data() {
return {
}
},
props: {
value: {
type: String,
default: '',
},
language: {
type: String,
default: 'javascript',
},
theme: {
type: String,
default: 'vs',
},
editorOptions: {
type: Object,
default: function(){
return {
selectOnLineNumbers: true,
roundedSelection: false,
readOnly: false, // 只读
cursorStyle: 'line', //光标样式
automaticLayout: false, //自动布局
glyphMargin: true, //字形边缘
useTabStops: false,
fontSize: 28, //字体大小
autoIndent:true,//自动布局
}
},
},
},
mounted() {
this.initEditor();
},
watch: {
value(value){
if(this.editor && value !== this.editor.getValue()){
this.editor.setValue(value);
}
}
},
methods: {
initEditor() {
this.editor = monaco.editor.create(this.$refs.container, {
value: this.value,
language: this.language,
theme: this.theme,
lineNumbers: 'on',
tabSize: 2,
insertSpaces: false,
editorOptions: this.editorOptions,
});
this.editor.onDidChangeModelContent(event => {
const value = this.editor.getValue();
this.$emit('input', value);
});
this.editor.updateOptions({
})
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="menu"> <div class="menu">
<el-badge class="menu-item" v-for="(item, key) of data" :key="key" is-dot :hidden="!menuBadge(key)"> <el-badge class="menu-item" v-for="(item, key) of data" :key="key" is-dot :hidden="!menuBadge(key)">
<el-link @click="clickItem(key)" :icon="item.icon" :disabled="item.disabled"> <el-link @click="clickItem(key)" :icon="item.icon" :disabled="item.disabled">
{{item.text}} {{item.label || ''}}
</el-link> </el-link>
</el-badge> </el-badge>
</div> </div>
......
// import { set, lensPath } from 'ramda';
import properties from '../../../utils/properties';
export default {
component: require('./index.vue'),
properties: {
...properties.node,
...properties.image
},
props: {
type: 'image'
},
title: 'image'
};
<template>
<div class="zero-custom-picture"></div>
</template>
<style>
/* .zero-custom-picture {
height: 100%;
background-position: center;
} */
</style>
<script>
export default {
name: 'customImage',
props: {
properties: {
type: Object,
default: () => {}
}
}
};
</script>
// import { set, lensPath } from 'ramda';
import properties from '../../../utils/properties';
export default {
component: require('./index.vue'),
properties: {
...properties.node,
...properties.label
},
props: {
type: 'label'
},
title: 'label'
};
<template>
<div
class="zero-custom-text"
:class="showPlaceholder && 'placeholder'"
v-html="addNBSP + selfText"
></div>
</template>
<style>
.zero-custom-text.placeholder:after {
content: '请输入';
font-style: italic;
font-size: 12px;
}
</style>
<script>
export default {
name: 'customLabel',
props: {
properties: {
type: Object,
default: () => {}
},
isTyping: Boolean
},
data() {
return {
};
},
computed: {
showPlaceholder() {
return (
!this.isTyping &&
typeof this.properties.text !== 'undefined' &&
!this.properties.text.replace(/\s|(&nbsp;)/g, '')
);
},
addNBSP() {
return this.selfText.replace(/\s/g, '') ? '' : '&nbsp;';
},
selfText() {
return this.properties.text || '文字';
}
}
};
</script>
<template> <template>
<div :style="customStyle" class="zero-custom-cmp zero-custom-node"> <div :style="customStyle" @click.self.stop="clickViewHandle(view)" class="zero-custom-cmp zero-custom-node">
<p class="custom-node-html" v-if="selfText">{{selfText}}</p> <p class="custom-node-html" v-if="selfText">{{selfText}}</p>
<slot></slot> <slot></slot>
</div> </div>
...@@ -30,12 +30,21 @@ export default { ...@@ -30,12 +30,21 @@ export default {
properties: { properties: {
type: Object, type: Object,
default: () => {} default: () => {}
},
view: {
type: Object,
default: () => {}
} }
}, },
computed: { computed: {
selfText() { selfText() {
return this.properties ? this.properties.text || '' : ''; return this.properties ? this.properties.text || '' : '';
} }
},
methods: {
clickViewHandle(data) {
this.$store.dispatch('activeComponent', {data, fromPlayground: true});
}
} }
}; };
</script> </script>
// import { set, lensPath } from 'ramda';
import properties from '../../../utils/properties';
export default {
component: require('./index.vue'),
properties: {
...properties.node,
...properties.rect
},
props: {
type: 'rect'
},
title: 'rect'
};
<template>
<div class="zero-custom-shape-rect-blue" :style="`background-color: ${properties.fillColor};border-width: ${properties.strokeWidth}px; border-color: ${properties.strokeColor};`"></div>
</template>
<style>
.zero-custom-shape-rect-blue {
height: 100%;
}
</style>
<script>
export default {
name: 'customRect',
props: {
properties: {
type: Object,
default: () => {}
}
// fillColor: {
// type: Object,
// default: () => {
// return {
// title: '填充色',
// type: 'colorPicker',
// value: '#fff'
// };
// }
// },
// strokeColor: {
// type: Object,
// default: () => {
// return {
// title: '边框颜色',
// type: 'colorPicker',
// value: '#000'
// };
// }
// },
// strokeWidth: {
// type: Object,
// default: () => {
// return {
// title: '边框宽度',
// type: 'inputNumber',
// value: 1
// };
// }
// }
}
};
</script>
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
placement="top" placement="top"
trigger="hover" trigger="hover"
width="auto" width="auto"
:disabled="!legalUrl"
:content="url" :content="url"
> >
<img style="max-width: 200px;" v-if="url" :src="url" alt="" /> <img style="max-width: 200px;" v-if="url" :src="url" alt="" />
...@@ -29,7 +30,6 @@ export default { ...@@ -29,7 +30,6 @@ export default {
methods: { methods: {
drop(e) { drop(e) {
if (this.$store.state.project.dragUUID) { if (this.$store.state.project.dragUUID) {
// debugger;
console.log('native drop', this.$store.state); console.log('native drop', this.$store.state);
this.swvalue = `asset://${this.$store.state.project.dragUUID}` this.swvalue = `asset://${this.$store.state.project.dragUUID}`
} }
...@@ -51,6 +51,9 @@ export default { ...@@ -51,6 +51,9 @@ export default {
} else { } else {
return ''; return '';
} }
},
legalUrl: function() {
return (this.swvalue + '').indexOf('//') > -1;
} }
} }
}; };
......
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
export let API_HOST; export let API_HOST;
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
//API_HOST = 'http://10.10.95.74:7777'; //API_HOST = 'http://10.10.95.74:7777';
//API_HOST = 'http://10.10.94.134:7777'; //API_HOST = 'http://192.168.1.16:7777';
//API_HOST = 'http://192.168.0.104:7777';
//API_HOST = 'http://10.10.92.33:7777';
//API_HOST = 'http://localhost:3002'; //API_HOST = 'http://localhost:3002';
API_HOST = window.__data.apiHost; API_HOST = window.__data.apiHost;
//API_HOST = ''; //API_HOST = '';
...@@ -21,6 +23,9 @@ export const SSO_VERIFY_PAGE_URL = '/sso/logout'; ...@@ -21,6 +23,9 @@ export const SSO_VERIFY_PAGE_URL = '/sso/logout';
export const DOCK_POINT_OFFSET = 4; export const DOCK_POINT_OFFSET = 4;
export const PROJECT_PAGE_SIZE = 20;
export const HISTORY_PAGE_SIZE = 20;
//文件类型图标 t表示展示缩略图 //文件类型图标 t表示展示缩略图
export const fileTypeIcon = { export const fileTypeIcon = {
'': 'file-empty', '': 'file-empty',
...@@ -28,6 +33,7 @@ export const fileTypeIcon = { ...@@ -28,6 +33,7 @@ export const fileTypeIcon = {
'.json': 'file-text', '.json': 'file-text',
'.zip': 'file-zip', '.zip': 'file-zip',
'.fnt': 'file-font', '.fnt': 'file-font',
'.mp3': 'file-music',
'.jpg': 't', '.jpg': 't',
'.jpeg': 't', '.jpeg': 't',
......
...@@ -5,28 +5,45 @@ ...@@ -5,28 +5,45 @@
"Close": "Close", "Close": "Close",
"Still Close": "Still Close", "Still Close": "Still Close",
"Save": "Save", "Save": "Save",
"Save And Preview": "Save And Preview",
"Save And Close": "Save And Close",
"Reset": "Reset", "Reset": "Reset",
"Copy": "Copy", "Copy": "Copy",
"Assets config": "Assets config",
"Paste same level":"Paste(same level)",
"Paste child":"Paste(child)",
"Exit": "Exit", "Exit": "Exit",
"Props": "Props", "Props": "Props",
"Behavior": "Behavior", "Behavior": "Behavior",
"Only mine": "Only mine",
"Add": "Add", "Add": "Add",
"Delete": "Delete", "Delete": "Delete",
"Delete all": "Delete all", "Delete all": "Delete all",
"Not delete": "Not delete",
"Import": "Import", "Import": "Import",
"Export": "Export", "Export": "Export",
"Importing project": "Importing project",
"Exporting project": "Exporting project",
"Upload": "Upload", "Upload": "Upload",
"Uploading": "Uploading", "Uploading": "Uploading",
"Edit": "Edit", "Edit": "Edit",
"EditEnv": "EditEnv", "EditEnv": "EditEnv",
"EditCustomModule": "EditCustomModule", "EditCustomModule": "EditCustomModule",
"Script": "Script",
"CodeSyncServe": "CodeSyncServe",
"ID": "ID", "ID": "ID",
"Mock Editor": "Mock Editor",
"Enable mock serve": "Enable mock serve",
"Name": "Name", "Name": "Name",
"Alias": "Alias", "Alias": "Alias",
"Output": "Output", "Output": "Output",
"Code": "Code", "Code": "Code",
"Desc": "Desc", "Desc": "Desc",
"Empty": "Empty", "Empty": "Empty",
"Path": "Path",
"Timeout": "Timeout",
"SuccessRatio": "SuccessRatio",
"Data": "Data",
"Key": "Key", "Key": "Key",
"Default": "Default", "Default": "Default",
"Event": "Event", "Event": "Event",
...@@ -53,6 +70,8 @@ ...@@ -53,6 +70,8 @@
"Packing": "Packing", "Packing": "Packing",
"Type": "Type", "Type": "Type",
"Group": "Group", "Group": "Group",
"Select from history": "Select from history",
"Meta Search": "Meta Search",
"Access denied": "Access denied", "Access denied": "Access denied",
"Invalid router": "Invalid router", "Invalid router": "Invalid router",
"Jump after": "Jump after {cd}s", "Jump after": "Jump after {cd}s",
...@@ -75,9 +94,18 @@ ...@@ -75,9 +94,18 @@
"Env editor": "Env editor", "Env editor": "Env editor",
"As inline": "As inline", "As inline": "As inline",
"Project": "Project", "Project": "Project",
"Check template code": "Check template code",
"Missing behavior": "Missing behavior",
"Input event name": "Input event name",
"Invalid event name": "Invalid event name",
"Event name exists": "Event name exists",
"Builtin event should add directly": "Builtin event should add directly",
"Rename event": "Rename event",
"Env constant": "Env constant", "Env constant": "Env constant",
"Custom module": "Custom module", "Custom module": "Custom module",
"Custom module asset mapping": "Custom module asset mapping",
"Copy template to clipboard": "Copy template to clipboard", "Copy template to clipboard": "Copy template to clipboard",
"Copied process to clipboard": "Copied process to clipboard",
"Link to parent": "Link to parent", "Link to parent": "Link to parent",
"Input project name": "Input project name", "Input project name": "Input project name",
"Invalid project name": "Invalid project name", "Invalid project name": "Invalid project name",
...@@ -91,6 +119,7 @@ ...@@ -91,6 +119,7 @@
"Fetching projects": "Fetching projects…", "Fetching projects": "Fetching projects…",
"Failed to fetch env": "Failed to fetch env", "Failed to fetch env": "Failed to fetch env",
"Failed to fetch projects": "Failed to fetch projects", "Failed to fetch projects": "Failed to fetch projects",
"Failed to fetch history": "Failed to fetch history",
"Failed to fetch project": "Failed to fetch project", "Failed to fetch project": "Failed to fetch project",
"Failed to create project": "Failed to create project", "Failed to create project": "Failed to create project",
"Failed to duplicate project": "Failed to duplicate project", "Failed to duplicate project": "Failed to duplicate project",
...@@ -98,6 +127,7 @@ ...@@ -98,6 +127,7 @@
"Failed to get project": "Failed to get project", "Failed to get project": "Failed to get project",
"Failed to save project": "Failed to save project", "Failed to save project": "Failed to save project",
"Save project successfully": "Save project successfully", "Save project successfully": "Save project successfully",
"Input version remark": "Input version remark",
"Input view name": "Input view name", "Input view name": "Input view name",
"Invalid view name": "Invalid view name", "Invalid view name": "Invalid view name",
"Unsaved version found locally": "Unsaved version found locally, please select version to open", "Unsaved version found locally": "Unsaved version found locally, please select version to open",
...@@ -105,6 +135,7 @@ ...@@ -105,6 +135,7 @@
"Remote Version": "Remote Version", "Remote Version": "Remote Version",
"Confirm to exit the editor": "Confirm to exit the editor?", "Confirm to exit the editor": "Confirm to exit the editor?",
"Confirm to publish": "Confirm to publish?", "Confirm to publish": "Confirm to publish?",
"Are you sure to delete it's process": "Are you sure to delete it's process",
"Are you sure to delete this asset": "Are you sure to delete this asset", "Are you sure to delete this asset": "Are you sure to delete this asset",
"Are you sure to delete all assets": "Are you sure to delete all assets", "Are you sure to delete all assets": "Are you sure to delete all assets",
"Are you sure to delete this item": "Are you sure to delete this item", "Are you sure to delete this item": "Are you sure to delete this item",
...@@ -127,20 +158,40 @@ ...@@ -127,20 +158,40 @@
"Import multi": "Import multi", "Import multi": "Import multi",
"Import view success": "Import view success", "Import view success": "Import view success",
"menu": { "menu": {
"save": "Save", "save": {
"details": "Details", "label": "Save"
"preview": "Preview", },
"pack": "Pack", "details": {
"exit": "Exit", "label": "Details"
"undo": "Undo", },
"redo": "Redo" "preview-fast": {
"label": "Preview"
},
"publish": {
"label": "Publish"
},
"mock": {
"label": "Mock"
},
"exit": {
"label": "Exit"
},
"undo": {
"icon": "icon-undo"
},
"redo": {
"icon": "icon-redo"
}
}, },
"view_node_menu": { "view_node_menu": {
"node": "Node", "node": "Node",
"image": "Image", "image": "Image",
"label": "Label", "label": "Label",
"bitmapText": "BitmapText",
"rect": "Rect", "rect": "Rect",
"scrollView": "ScrollView" "scrollView": "ScrollView",
"scrollList": "ScrollList",
"svga": "SVGA"
}, },
"panes": { "panes": {
"Assets": "Assets", "Assets": "Assets",
...@@ -165,11 +216,16 @@ ...@@ -165,11 +216,16 @@
"dynamic": "Dynamic", "dynamic": "Dynamic",
"map": "Map" "map": "Map"
}, },
"nodeFilterPresets": {
"trigger": "Behavior Trigger",
"type": "Node Type"
},
"events": { "events": {
"init": "Init", "init": "Init",
"awake": "Awake", "awake": "Awake",
"sleep": "Sleep", "sleep": "Sleep",
"data-center": "DataCenter", "data-center": "DataCenter",
"update-data": "UpdateData",
"click": "Click", "click": "Click",
"touchstart": "Touchstart", "touchstart": "Touchstart",
"touchend": "Touchend", "touchend": "Touchend",
......
...@@ -5,28 +5,45 @@ ...@@ -5,28 +5,45 @@
"Close": "关闭", "Close": "关闭",
"Still Close": "仍然关闭", "Still Close": "仍然关闭",
"Save": "保存", "Save": "保存",
"Save And Preview": "保存并预览",
"Save And Close": "保存并关闭",
"Reset": "重置", "Reset": "重置",
"Copy": "复制", "Copy": "复制",
"Assets config": "素材配置",
"Paste same level":"粘贴(同级)",
"Paste child":"粘贴(子级)",
"Exit": "退出", "Exit": "退出",
"Props": "属性", "Props": "属性",
"Behavior": "行为", "Behavior": "行为",
"Only mine": "仅显示我的",
"Add": "添加", "Add": "添加",
"Delete": "删除", "Delete": "删除",
"Delete all": "删除全部", "Delete all": "删除全部",
"Not delete": "不删除",
"Import": "导入", "Import": "导入",
"Export": "导出", "Export": "导出",
"Importing project": "项目导入中",
"Exporting project": "项目导出中",
"Upload": "上传", "Upload": "上传",
"Uploading": "上传中", "Uploading": "上传中",
"Edit": "编辑", "Edit": "编辑",
"EditEnv": "编辑环境", "EditEnv": "编辑环境",
"EditCustomModule": "编辑自定义模块", "EditCustomModule": "编辑自定义模块",
"Script": "脚本",
"CodeSyncServe": "代码同步服务",
"ID": "ID", "ID": "ID",
"Mock Editor": "Mock编辑器",
"Enable mock serve": "启用Mock服务",
"Name": "名字", "Name": "名字",
"Alias": "别名", "Alias": "别名",
"Output": "输出", "Output": "输出",
"Code": "代码", "Code": "代码",
"Desc": "描述", "Desc": "描述",
"Empty": "空", "Empty": "空",
"Path": "路径",
"Timeout": "超时(ms)",
"SuccessRatio": "成功率",
"Data": "数据",
"Key": "属性名", "Key": "属性名",
"Default": "默认值", "Default": "默认值",
"Event": "事件", "Event": "事件",
...@@ -49,10 +66,13 @@ ...@@ -49,10 +66,13 @@
"Saving": "保存中…", "Saving": "保存中…",
"Divider": "分流节点", "Divider": "分流节点",
"Custom": "自定义节点", "Custom": "自定义节点",
"Custom event": "自定义事件",
"Entry": "入口", "Entry": "入口",
"Packing": "打包", "Packing": "打包",
"Type": "类型", "Type": "类型",
"Group": "分组", "Group": "分组",
"Select from history": "打开历史版本",
"Meta Search": "过程元查找",
"Access denied": "无权限", "Access denied": "无权限",
"Invalid router": "无效的页面", "Invalid router": "无效的页面",
"Jump after": "{cd}秒后跳转", "Jump after": "{cd}秒后跳转",
...@@ -75,9 +95,21 @@ ...@@ -75,9 +95,21 @@
"Env editor": "环境编辑器", "Env editor": "环境编辑器",
"As inline": "作为内联", "As inline": "作为内联",
"Project": "项目", "Project": "项目",
"Check template code": "查看模板代码",
"Missing behavior": "行为丢失",
"Add behavior": "添加行为",
"Edit trigger": "编辑触发",
"Delete trigger": "删除触发",
"Input event name": "输入事件名",
"Rename event": "事件重命名",
"Invalid event name": "无效的事件名",
"Event name exists": "事件名已存在",
"Builtin event should add directly": "内部事件请直接添加",
"Env constant": "自定义常量", "Env constant": "自定义常量",
"Custom module": "自定义模块", "Custom module": "自定义模块",
"Custom module asset mapping": "自定义模块素材映射",
"Copy template to clipboard": "复制模板到粘贴板", "Copy template to clipboard": "复制模板到粘贴板",
"Copied process to clipboard": "复制过程到粘贴板",
"Link to parent": "连接到父节点", "Link to parent": "连接到父节点",
"Input project name": "输入项目名", "Input project name": "输入项目名",
"Invalid project name": "无效的项目名", "Invalid project name": "无效的项目名",
...@@ -91,6 +123,7 @@ ...@@ -91,6 +123,7 @@
"Fetching projects": "获取项目列表…", "Fetching projects": "获取项目列表…",
"Failed to fetch env": "获取环境失败", "Failed to fetch env": "获取环境失败",
"Failed to fetch projects": "获取项目列表失败", "Failed to fetch projects": "获取项目列表失败",
"Failed to fetch history": "获取历史记录失败",
"Failed to fetch project": "获取项目失败", "Failed to fetch project": "获取项目失败",
"Failed to create project": "创建项目失败", "Failed to create project": "创建项目失败",
"Failed to duplicate project": "复制项目失败", "Failed to duplicate project": "复制项目失败",
...@@ -98,6 +131,7 @@ ...@@ -98,6 +131,7 @@
"Failed to get project": "获取项目失败", "Failed to get project": "获取项目失败",
"Failed to save project": "保存项目失败", "Failed to save project": "保存项目失败",
"Save project successfully": "保存项目成功", "Save project successfully": "保存项目成功",
"Input version remark": "输入版本备注",
"Input view name": "输入视图名", "Input view name": "输入视图名",
"Invalid view name": "无效的视图名", "Invalid view name": "无效的视图名",
"Unsaved version found locally": "本地发现了未保存保本,请选择版本打开", "Unsaved version found locally": "本地发现了未保存保本,请选择版本打开",
...@@ -105,6 +139,7 @@ ...@@ -105,6 +139,7 @@
"Remote Version": "远程版本", "Remote Version": "远程版本",
"Confirm to exit the editor": "确定退出编辑器吗?", "Confirm to exit the editor": "确定退出编辑器吗?",
"Confirm to publish": "确定发布吗?", "Confirm to publish": "确定发布吗?",
"Are you sure to delete it's process": "确定删除这对应的过程吗",
"Are you sure to delete this asset": "确定删除这个素材吗", "Are you sure to delete this asset": "确定删除这个素材吗",
"Are you sure to delete all assets": "确定删除全部素材吗", "Are you sure to delete all assets": "确定删除全部素材吗",
"Are you sure to delete this item": "确定删除这一项吗", "Are you sure to delete this item": "确定删除这一项吗",
...@@ -127,20 +162,42 @@ ...@@ -127,20 +162,42 @@
"Import multi": "导入多", "Import multi": "导入多",
"Import view success": "视图导入成功", "Import view success": "视图导入成功",
"menu": { "menu": {
"save": "保存", "save": {
"details": "详情", "label": "保存"
"preview": "预览", },
"pack": "发布", "details": {
"exit": "退出", "label": "详情"
"undo": "撤销", },
"redo": "重做" "preview-fast": {
"label": "预览"
},
"publish": {
"label": "发布"
},
"mock": {
"label": "Mock"
},
"exit": {
"label": "退出"
},
"undo": {
"icon": "icon-undo"
},
"redo": {
"icon": "icon-redo"
}
}, },
"view_node_menu": { "view_node_menu": {
"node": "节点", "node": "节点",
"image": "图片", "image": "图片",
"label": "标签", "label": "文本",
"bitmapText": "位图文本",
"rect": "矩形", "rect": "矩形",
"scrollView": "滚动视图" "circle": "圆形",
"textinput": "输入框",
"scrollView": "滚动视图",
"scrollList": "滚动列表",
"svga": "SVGA"
}, },
"panes": { "panes": {
"Assets": "素材", "Assets": "素材",
...@@ -165,11 +222,16 @@ ...@@ -165,11 +222,16 @@
"dynamic": "动态", "dynamic": "动态",
"map": "字典" "map": "字典"
}, },
"nodeFilterPresets": {
"trigger": "行为触发",
"type": "节点类型"
},
"events": { "events": {
"init": "初始化", "init": "初始化",
"awake": "激活", "awake": "激活",
"sleep": "入眠", "sleep": "入眠",
"data-center": "数据中心", "data-center": "数据中心",
"update-data": "更新数据",
"click": "触摸点击", "click": "触摸点击",
"touchstart": "触摸按下", "touchstart": "触摸按下",
"touchend": "触摸弹起", "touchend": "触摸弹起",
......
...@@ -18,5 +18,10 @@ export default new Router({ ...@@ -18,5 +18,10 @@ export default new Router({
name: 'editor', name: 'editor',
component: () => import('./views/Editor.vue') component: () => import('./views/Editor.vue')
}, },
{
path: '/preview/:projectID',
name: 'preview',
component: () => import('./views/Preview.vue')
},
] ]
}) })
...@@ -29,24 +29,21 @@ export default new Vuex.Store({ ...@@ -29,24 +29,21 @@ export default new Vuex.Store({
plugins: [ plugins: [
SaveToLocalPlugin({ SaveToLocalPlugin({
mutationTypes: [ mutationTypes: [
'makeDirty',
'modifyProject', 'modifyProject',
'addNode', 'addNode',
'deleteNode', 'deleteNode',
'copyNode',
'addAsset', 'addAsset',
'deleteAsset', 'deleteAsset',
'deleteAllAssets', 'deleteAllAssets',
'modifyAsset', 'modifyAsset',
'importView', 'importView',
'importAssets', 'importAssets',
'addDataMapping',
'deleteDataMapping',
'modifyDataMapping',
'addEnvMapping',
'deleteEnvMapping',
'modifyEnvMapping',
'modifyActiveView', 'modifyActiveView',
'behavior_save', 'behavior_save',
'modifyCustoms', 'modifyProjectDetails',
'modifyMocks',
] ]
}) })
] ]
......
...@@ -8,6 +8,7 @@ import Vue from "vue"; ...@@ -8,6 +8,7 @@ import Vue from "vue";
import i18n from "../../i18n"; import i18n from "../../i18n";
import generateUUID from "uuid/v4"; import generateUUID from "uuid/v4";
import {clonePureObj, metaInUse, updateProcesses} from "../../utils"; import {clonePureObj, metaInUse, updateProcesses} from "../../utils";
import {arrayFind} from "element-ui/src/utils/util";
export const behaviorStore = { export const behaviorStore = {
state: { state: {
...@@ -15,8 +16,7 @@ export const behaviorStore = { ...@@ -15,8 +16,7 @@ export const behaviorStore = {
currentBehavior: null, currentBehavior: null,
processContext: [], processContext: [],
originData: null, originData: null,
originBehaviors: null, currentProcess: null,
behaviors: null,
processStack: [], processStack: [],
editable: false, editable: false,
...@@ -29,53 +29,20 @@ export const behaviorStore = { ...@@ -29,53 +29,20 @@ export const behaviorStore = {
}, },
}, },
mutations: { mutations: {
behavior_startEdit(state, {originData, behaviors, event}) { behavior_startEdit(state, {originData, behavior}) {
state.originData = originData; state.originData = originData;
state.originBehaviors = behaviors;
state.behaviors = clonePureObj(behaviors);
state.data = clonePureObj(originData); state.data = clonePureObj(originData);
state.currentBehavior = clonePureObj(behavior);
if (state.behaviors.length > 0) { },
state.currentBehavior = state.behaviors[0]; behavior_save(state) {
} else { state.originData.processes = state.data.processes;
let metaUUID = generateUUID();
state.currentBehavior = state.behaviors[0] = {
uuid: generateUUID(),
alias: event,
meta: metaUUID,
};
let subEntryUUID = generateUUID();
state.data.processes.push({
id: metaUUID,
name: event,
props: {},
subEntry: subEntryUUID,
sub: {
[subEntryUUID]: {
uuid: subEntryUUID,
alias: i18n.t('Entry'),
meta: 'entry',
design: {
x: 10,
y: 10,
}
},
},
});
}
}, },
addProcessMeta(state, meta) { addProcessMeta(state, meta) {
state.data.processes.push(meta); state.data.processes.push(meta);
return process; return process;
}, },
behavior_save(state) {
state.originData.processes = state.data.processes;
state.originBehaviors[0] = state.currentBehavior;
},
updateProcesses(state, {targetMetaID, replaceMetaID}) { updateProcesses(state, {targetMetaID, replaceMetaID}) {
for (let process of state.data.processes) { updateProcesses(state.data.processes, targetMetaID, replaceMetaID);
updateProcesses(process, targetMetaID, replaceMetaID);
}
}, },
deleteProcessMeta(state, {meta, process}) { deleteProcessMeta(state, {meta, process}) {
let container; let container;
...@@ -85,27 +52,23 @@ export const behaviorStore = { ...@@ -85,27 +52,23 @@ export const behaviorStore = {
container = state.data.processes; container = state.data.processes;
} }
if (container) { if (container) {
for (let i = 0, li = container.length; i < li; i++) { deleteProcessMeta(meta.id, container);
const process = container[i];
if (process.id === meta.id) {
container.splice(i, 1);
break;
}
}
} }
}, },
clearProcessStack(state) { clearProcessStack(state) {
state.processStack.splice(0); state.processStack.splice(0);
updatePropsEditable(state); updatePropsEditable(state);
state.currentProcess = null;
}, },
pushProcessStack(state, process) { pushProcessStack(state, process) {
state.processStack.push(process); state.processStack.push(process);
updatePropsEditable(state); updatePropsEditable(state);
state.currentProcess = process;
}, },
popProcessStack(state, index) { popProcessStack(state, index) {
state.processStack.splice(index); state.processStack.splice(index);
updatePropsEditable(state); updatePropsEditable(state);
state.currentProcess = state.processStack[state.processStack.length - 1];
}, },
setScale(state, value) { setScale(state, value) {
let scale = state.drawState.boardScale; let scale = state.drawState.boardScale;
...@@ -153,6 +116,10 @@ export const behaviorStore = { ...@@ -153,6 +116,10 @@ export const behaviorStore = {
} }
}, },
actions: { actions: {
behavior_save({commit, state}) {
commit('behavior_save');
return state.currentBehavior;
},
addCustomProcessMeta({commit, state}, {masterProcess, isInline, processId}) { addCustomProcessMeta({commit, state}, {masterProcess, isInline, processId}) {
let meta = { let meta = {
id: generateUUID(), id: generateUUID(),
...@@ -175,7 +142,7 @@ export const behaviorStore = { ...@@ -175,7 +142,7 @@ export const behaviorStore = {
addProcessMeta(commit, isInline, masterProcess, meta); addProcessMeta(commit, isInline, masterProcess, meta);
return meta; return meta;
}, },
addProcessFromPrefab({commit, state}, {masterProcess, isInline, meta: pMeta}){ addProcessFromPrefab({commit, state}, {masterProcess, isInline, meta: pMeta}) {
let meta = clonePureObj(pMeta); let meta = clonePureObj(pMeta);
meta.id = generateUUID(); meta.id = generateUUID();
meta.isInline = isInline; meta.isInline = isInline;
...@@ -183,7 +150,10 @@ export const behaviorStore = { ...@@ -183,7 +150,10 @@ export const behaviorStore = {
addProcessMeta(commit, isInline, masterProcess, meta); addProcessMeta(commit, isInline, masterProcess, meta);
return meta; return meta;
} },
searchMeta({state}, {keyword}) {
searchMeta(state.data.processes, keyword);
},
} }
}; };
...@@ -191,7 +161,7 @@ function updatePropsEditable(state) { ...@@ -191,7 +161,7 @@ function updatePropsEditable(state) {
state.editable = state.processStack.filter(item => item.meta.type === 'builtin') <= 0; state.editable = state.processStack.filter(item => item.meta.type === 'builtin') <= 0;
} }
function addProcessMeta(commit, isInline, masterProcess, meta){ function addProcessMeta(commit, isInline, masterProcess, meta) {
if (isInline) { if (isInline) {
if (!masterProcess.meta.metas) { if (!masterProcess.meta.metas) {
Vue.set(masterProcess.meta, 'metas', []); Vue.set(masterProcess.meta, 'metas', []);
...@@ -201,3 +171,53 @@ function addProcessMeta(commit, isInline, masterProcess, meta){ ...@@ -201,3 +171,53 @@ function addProcessMeta(commit, isInline, masterProcess, meta){
commit('addProcessMeta', meta); commit('addProcessMeta', meta);
} }
} }
function searchMeta(processes, keyword, path) {
for (let process of processes) {
/*if(process.id === keyword || process.name === keyword){
}*/
}
}
export function addBehavior(alias, processes) {
let metaUUID = generateUUID();
let behavior = {
uuid: generateUUID(),
meta: metaUUID,
};
let subEntryUUID = generateUUID();
processes.push({
id: metaUUID,
name: alias,
props: {},
subEntry: subEntryUUID,
sub: {
[subEntryUUID]: {
uuid: subEntryUUID,
alias: i18n.t('Entry'),
meta: 'entry',
design: {
x: 10,
y: 10,
}
},
},
});
return behavior;
}
export function deleteProcessMeta(metaId, processes) {
for (let i = 0, li = processes.length; i < li; i++) {
const process = processes[i];
if (process.id === metaId) {
processes.splice(i, 1);
break;
}
}
}
export function findProcess(metaId, processes) {
return arrayFind(processes, process => process.id === metaId);
}
...@@ -7,11 +7,13 @@ ...@@ -7,11 +7,13 @@
import {envApi} from "../../api"; import {envApi} from "../../api";
import i18n from "../../i18n"; import i18n from "../../i18n";
const storeKey = 'code-sync-serve-config';
export const envStore = { export const envStore = {
state: { state: {
initialized: false, initialized: false,
name: 'Zeroing Editor', name: 'Zeroing Editor',
version: '1.0.1', version: '1.0.3',
templates: { templates: {
builtin: ['blank'], builtin: ['blank'],
custom: [], custom: [],
...@@ -19,6 +21,10 @@ export const envStore = { ...@@ -19,6 +21,10 @@ export const envStore = {
processes: [], processes: [],
scripts: [], scripts: [],
customs: [], customs: [],
codeSyncServeConfig: {
ip: 'localhost',
port: 7788,
}
}, },
mutations: { mutations: {
updateEnv(state, env) { updateEnv(state, env) {
...@@ -28,6 +34,15 @@ export const envStore = { ...@@ -28,6 +34,15 @@ export const envStore = {
state.customs = parseItem(state.customs); state.customs = parseItem(state.customs);
state.initialized = true; state.initialized = true;
let configStr = localStorage.getItem(storeKey);
if (configStr) {
state.codeSyncServeConfig = JSON.parse(configStr);
}
},
saveCodeSyncServeConfig(state, config) {
state.codeSyncServeConfig = config;
localStorage.setItem(storeKey, JSON.stringify(state.codeSyncServeConfig));
}, },
}, },
getters: { getters: {
...@@ -35,8 +50,14 @@ export const envStore = { ...@@ -35,8 +50,14 @@ export const envStore = {
return groupProcesses(state.processes, process => process.isPrefab); return groupProcesses(state.processes, process => process.isPrefab);
}, },
builtinProcessTree: state => { builtinProcessTree: state => {
const tree = groupProcesses(state.processes, process => !process.isPrefab); const builtin = groupProcesses(state.processes, process => !process.isPrefab);
let tree = [
{
name: 'builtin',
children: builtin,
}
];
const dividerProcess = { const dividerProcess = {
id: 'divider', id: 'divider',
name: i18n.t('Divider'), name: i18n.t('Divider'),
...@@ -71,7 +92,7 @@ export const envStore = { ...@@ -71,7 +92,7 @@ export const envStore = {
const env = await envApi.fetchEnv(); const env = await envApi.fetchEnv();
commit('updateEnv', env); commit('updateEnv', env);
} }
} },
} }
}; };
......
...@@ -7,10 +7,17 @@ import {projectApi} from "../../api"; ...@@ -7,10 +7,17 @@ import {projectApi} from "../../api";
import path from "path"; import path from "path";
import generateUUID from "uuid/v4"; import generateUUID from "uuid/v4";
import {getCmpProps, flattenViews, getCmpByUUID} from '../../utils/common'; import {getCmpProps, flattenViews, getCmpByUUID} from '../../utils/common';
import {clonePureObj, saveAs} from "../../utils"; import {clonePureObj, getMockServeEnabled, saveAs} from "../../utils";
import {template} from "../../template"; import {template} from "../../template";
import {importView, uploadFile} from "../../api/project"; import {importView, uploadFile} from "../../api/project";
import events from "@/global-events"; import events from "@/global-events";
import {packImages} from "../../utils/sheet-pack";
import {addBehavior, deleteProcessMeta, findProcess} from "./behavior";
import db from "../../utils/db-storage";
import {preprocess} from "../../views/Preview/preview-preprocess";
const storeName = 'project';
const defaultOptions = { const defaultOptions = {
pageTitle: 'no title', pageTitle: 'no title',
...@@ -34,6 +41,43 @@ function getDefaultOptions() { ...@@ -34,6 +41,43 @@ function getDefaultOptions() {
return clonePureObj(defaultOptions); return clonePureObj(defaultOptions);
} }
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])
setUUIDForAllChildren(node.children[i])
}
}
}
let copyNodeCatch = null;
function copyBaseRoot(node) {
let _node = JSON.parse(JSON.stringify(node));
let data;
if (_node.children && _node.children.length > 0) {
data = {
name: _node.name,
type: _node.type,
properties: _node.properties,
events: _node.events,
uuid: generateUUID(),
children: _node.children
};
} else {
data = {
name: _node.name,
type: _node.type,
properties: _node.properties,
events: _node.events,
uuid: generateUUID(),
};
}
return data
}
export const projectStore = { export const projectStore = {
state: { state: {
id: '', id: '',
...@@ -46,7 +90,10 @@ export const projectStore = { ...@@ -46,7 +90,10 @@ export const projectStore = {
dataMapping: [], dataMapping: [],
processes: [], processes: [],
customs: [], customs: [],
mock: [],
dependencies: {},
}, },
mockServeEnabled: false,
activeComponent: {}, activeComponent: {},
activeComponentCopy: {}, // 当前选中节点的镜像,用来处理拖拽时数据变化频繁的问题 activeComponentCopy: {}, // 当前选中节点的镜像,用来处理拖拽时数据变化频繁的问题
activeIdList: [], activeIdList: [],
...@@ -54,11 +101,15 @@ export const projectStore = { ...@@ -54,11 +101,15 @@ export const projectStore = {
dragUUID: '', dragUUID: '',
dirty: false, dirty: false,
operateStack: [], operateStack: [],
stackIndex: 0 stackIndex: 0,
packages: {},
}, },
mutations: { mutations: {
setDirty(state, dirty = true) { setDirty(state, dirty = true) {
state.dirty = dirty; state.dirty = dirty;
},
makeDirty() {
}, },
updateProject(state, project) { updateProject(state, project) {
const {id, name, creator, data} = project; const {id, name, creator, data} = project;
...@@ -68,7 +119,7 @@ export const projectStore = { ...@@ -68,7 +119,7 @@ export const projectStore = {
const localData = state.data; const localData = state.data;
if (data) { if (data) {
const {views, assets, dataMapping, processes, options, customs,} = JSON.parse(data); const {views, assets, dataMapping, processes, options, customs, mock, dependencies} = typeof data === 'string' ? JSON.parse(data) : data;
Vue.set(localData, 'options', options || getDefaultOptions()); Vue.set(localData, 'options', options || getDefaultOptions());
Vue.set(localData, 'views', views || []); Vue.set(localData, 'views', views || []);
...@@ -76,6 +127,8 @@ export const projectStore = { ...@@ -76,6 +127,8 @@ export const projectStore = {
Vue.set(localData, 'dataMapping', dataMapping || []); Vue.set(localData, 'dataMapping', dataMapping || []);
Vue.set(localData, 'processes', processes || []); Vue.set(localData, 'processes', processes || []);
Vue.set(localData, 'customs', customs || []); Vue.set(localData, 'customs', customs || []);
Vue.set(localData, 'mock', mock || []);
Vue.set(localData, 'dependencies', dependencies || {});
} else { } else {
Vue.set(localData, 'options', getDefaultOptions()); Vue.set(localData, 'options', getDefaultOptions());
Vue.set(localData, 'views', []); Vue.set(localData, 'views', []);
...@@ -83,7 +136,28 @@ export const projectStore = { ...@@ -83,7 +136,28 @@ export const projectStore = {
Vue.set(localData, 'dataMapping', []); Vue.set(localData, 'dataMapping', []);
Vue.set(localData, 'processes', []); Vue.set(localData, 'processes', []);
Vue.set(localData, 'customs', []); Vue.set(localData, 'customs', []);
Vue.set(localData, 'mock', []);
Vue.set(localData, 'dependencies', {});
} }
state.mockServeEnabled = getMockServeEnabled(id);
updateMock(localData.mock);
},
modifyOptions(state, value) {
state.data.options = value;
},
modifyEnv(state, value) {
state.data.options.env = value;
},
modifyDataMapping(state, value) {
state.data.dataMapping = value;
},
modifyCustoms(state, value) {
console.log(value);
state.data.customs = value;
},
modifyProjectDetails(state) {
}, },
/** /**
* 激活组件 * 激活组件
...@@ -280,6 +354,46 @@ export const projectStore = { ...@@ -280,6 +354,46 @@ export const projectStore = {
const index = parentChildren.indexOf(node); const index = parentChildren.indexOf(node);
parentChildren.splice(index, 1); parentChildren.splice(index, 1);
}, },
copyNode(state, {node, parentNode}) {
let _node1 = node
copyNodeCatch = _node1;
//localStorage.copyNodeCatch=_node1;
// let _node=copyBaseRoot(_node1);
// setUUIDForAllChildren(_node)
// if (parentNode) {
// if (!parentNode.children) {
// Vue.set(parentNode, 'children', []);
// }
// parentNode.children.push(_node);
// } else {
// state.data.views.push(_node);
// }
},
pasteNode(state, {node, parentNode, pasteState}) {
let _node1 = copyNodeCatch;
console.log(copyNodeCatch)
if (_node1) {
let _node = copyBaseRoot(_node1);
setUUIDForAllChildren(_node)
if (pasteState == 1) {
parentNode = parentNode;
} else {
parentNode = node;
}
if (parentNode) {
if (!parentNode.children) {
Vue.set(parentNode, 'children', []);
}
parentNode.children.push(_node);
} else {
state.data.views.push(_node);
}
} else {
console.warn("请先选择一个节点进行复制")
}
},
importAssets(state, assets) { importAssets(state, assets) {
state.data.assets.push(...assets); state.data.assets.push(...assets);
}, },
...@@ -292,6 +406,13 @@ export const projectStore = { ...@@ -292,6 +406,13 @@ export const projectStore = {
uuid: generateUUID(), uuid: generateUUID(),
}) })
}, },
replaceAsset(state, {uuid, url}) {
for (let asset of state.data.assets) {
if (asset.uuid === uuid) {
asset.url = url;
}
}
},
deleteAsset(state, uuid) { deleteAsset(state, uuid) {
const {assets} = state.data; const {assets} = state.data;
for (let i = 0, li = assets.length; i < li; i++) { for (let i = 0, li = assets.length; i < li; i++) {
...@@ -310,50 +431,15 @@ export const projectStore = { ...@@ -310,50 +431,15 @@ export const projectStore = {
}, },
addDataMapping(state, link) { modifyMocks(state, mocks) {
if (link) { state.data.mock = mocks;
state.data.dataMapping.push(link);
} else {
state.data.dataMapping.push({
name: '',
path: '',
});
}
},
deleteDataMapping(state, index) {
state.data.dataMapping.splice(index, 1);
},
modifyDataMapping(state) {
updateMock(mocks);
}, },
addEnvMapping(state, link) { setMockServeEnabled(state, enabled) {
if (!state.data.options.env) { state.mockServeEnabled = enabled;
Vue.set(state.data.options, 'env', []); }
}
if (link) {
state.data.options.env.push(link);
} else {
state.data.options.env.push({
name: '',
value: '',
});
}
},
deleteEnvMapping(state, index) {
state.data.options.env.splice(index, 1);
},
modifyEnvMapping(state) {
},
modifyCustoms(state, customs) {
state.data.customs = customs;
},
}, },
getters: { getters: {
project(state) { project(state) {
...@@ -408,7 +494,7 @@ export const projectStore = { ...@@ -408,7 +494,7 @@ export const projectStore = {
/** /**
* 返回Array格式的views * 返回Array格式的views
*/ */
views: state => { views(state) {
// 如果有选中的节点,则展示对应的视图组 // 如果有选中的节点,则展示对应的视图组
// 否则展示第一个视图组 // 否则展示第一个视图组
let _view = []; let _view = [];
...@@ -419,29 +505,39 @@ export const projectStore = { ...@@ -419,29 +505,39 @@ export const projectStore = {
} }
return _view; return _view;
} },
assets(state) {
return state.data.assets;
},
getProcess: state => behavior => {
return findProcess(behavior.meta, state.data.processes);
},
}, },
actions: { actions: {
saveToLocal({getters, commit}) { async saveToLocal({getters, commit}) {
const {project} = getters; const {project} = getters;
localStorage.setItem('project-' + project.id, JSON.stringify(project)); await db.set(storeName, {id: project.id, data: JSON.stringify(project)});
//localStorage.setItem('project-' + project.id, JSON.stringify(project));
commit('setDirty', true); commit('setDirty', true);
}, },
localVersionExist({commit}, projectID) { async localVersionExist({commit}, projectID) {
let json = localStorage.getItem('project-' + projectID); let json = await db.get(storeName, projectID);
//let json = localStorage.getItem('project-' + projectID);
return !!json; return !!json;
}, },
loadFromLocal({commit}, projectID) { async loadFromLocal({commit}, projectID) {
let json = localStorage.getItem('project-' + projectID); let json = await db.get(storeName, projectID);
//let json = localStorage.getItem('project-' + projectID);
if (json) { if (json) {
const project = JSON.parse(json); const project = JSON.parse(json.data);
commit('updateProject', project); commit('updateProject', project);
commit('setDirty', true); commit('setDirty', true);
} }
}, },
deleteLocalVersion({state, commit}, projectID) { async deleteLocalVersion({state, commit}, projectID) {
localStorage.removeItem('project-' + projectID); await db.remove(storeName, projectID);
//localStorage.removeItem('project-' + projectID);
commit('setDirty', false); commit('setDirty', false);
}, },
async loadFromRemote({commit, dispatch}, projectID) { async loadFromRemote({commit, dispatch}, projectID) {
...@@ -453,14 +549,34 @@ export const projectStore = { ...@@ -453,14 +549,34 @@ export const projectStore = {
throw new Error('Project does not exist') throw new Error('Project does not exist')
} }
}, },
async saveToRemote({state, dispatch, getters}) { async loadFromDataUrl({commit, dispatch}, {project, dataUrl}) {
await projectApi.saveOne(getters.project); const projectData = await projectApi.fetchOneFromDataUrl(dataUrl);
if (projectData) {
project.data = projectData;
commit('updateProject', project);
dispatch('saveToLocal');
} else {
throw new Error('Project does not exist')
}
},
async saveToRemote({state, dispatch, getters}, {remark}) {
await projectApi.saveOne(getters.project, remark);
dispatch('deleteLocalVersion', state.id); dispatch('deleteLocalVersion', state.id);
}, },
async updateProject({commit}, projectID) { async updateProject({commit}, projectID) {
const project = await projectApi.getData(projectID); const project = await projectApi.getData(projectID);
commit('updateProject', project); commit('updateProject', project);
}, },
async loadPackages({state, commit}) {
let packages = await db.get('packages', state.id);
},
async resolveDependencies({state}) {
for (let key in state.data.dependencies) {
let version = state.data.dependencies[key];
}
},
/** /**
* 选中节点 * 选中节点
* @param {*} context * @param {*} context
...@@ -475,10 +591,13 @@ export const projectStore = { ...@@ -475,10 +591,13 @@ export const projectStore = {
return getTopView(node.parent); return getTopView(node.parent);
} }
}; };
if (!data.fromPlayground) {
let _view = getTopView(data.node); // 点击视图区域选中节点
if (_view && _view.data) { // 则需要切换当前节点所在的最顶层视图
context.state.activeViews = _view.data.uuid; let _view = getTopView(data.node);
if (_view && _view.data) {
context.state.activeViews = _view.data.uuid;
}
} }
context.commit('activeComponent', data.data); context.commit('activeComponent', data.data);
...@@ -495,7 +614,9 @@ export const projectStore = { ...@@ -495,7 +614,9 @@ export const projectStore = {
}); });
if (_props.source) { if (_props.source) {
if (!_props.width || !_props.height) { // imageWidth imageHeight
// 设置image类型节点的原图高宽度
if (!_props.imageWidth || !_props.imageHeight) {
let _url = _props.source; //_source.value; let _url = _props.source; //_source.value;
if (_url.indexOf('asset://') === 0) { if (_url.indexOf('asset://') === 0) {
let uuid = _url.split('//')[1]; let uuid = _url.split('//')[1];
...@@ -505,13 +626,16 @@ export const projectStore = { ...@@ -505,13 +626,16 @@ export const projectStore = {
let _img = new Image(); let _img = new Image();
_img.src = _url; _img.src = _url;
_img.onload = function () { _img.onload = function () {
_props.width = _img.width; _props.imageWidth = _img.width;
_props.height = _img.height; _props.imageHeight = _img.height;
commit('modifyActiveView', { commit('modifyActiveView', {
properties: _props properties: _props
}); });
} }
} }
} else {
delete _props.imageWidth;
delete _props.imageHeight;
} }
commit('modifyActiveView', { commit('modifyActiveView', {
...@@ -532,11 +656,10 @@ export const projectStore = { ...@@ -532,11 +656,10 @@ export const projectStore = {
modifyActiveView({commit}, view) { modifyActiveView({commit}, view) {
commit('modifyActiveView', view) commit('modifyActiveView', view)
}, },
/** /**
* 新增节点脚本 * 新增节点脚本
* @param {*} param0 * @param {*} param0
* @param {*} data * @param {*} script
*/ */
addNodeScript({commit, state}, script) { addNodeScript({commit, state}, script) {
let _scripts = _.cloneDeep(state.activeComponent.scripts || []); let _scripts = _.cloneDeep(state.activeComponent.scripts || []);
...@@ -571,37 +694,115 @@ export const projectStore = { ...@@ -571,37 +694,115 @@ export const projectStore = {
}); });
}, },
async uploadFiles({commit}, files) { async uploadFiles({commit}, files) {
const failedList = []; const {failedList, result} = await uploadFiles(files);
let ps = [];
for (let file of files) {
events.$emit('upload-indicator', true);
ps.push(
uploadFile(file).catch(e => {
}).finally(() => {
events.$emit('upload-indicator', false);
})
);
}
const result = await Promise.all(ps);
for (let item of result) { for (let item of result) {
const {url, __originFile} = item; const {url, __originFile} = item;
commit('addAsset', {url, file: __originFile}); commit('addAsset', {url, file: __originFile});
} }
/*for (let file of files) {
try {
const {url} = await uploadFile(file);
commit('addAsset', {url, file});
}catch (e) {
failedList.push(file);
}
events.$emit('upload-indicator', false);
}*/
return failedList; return failedList;
}, },
async packProject({state}) { async replaceAsset({commit}, {uuid, file}) {
const result = await projectApi.pack(state.id); const {failedList, result} = await uploadFiles([file]);
commit('replaceAsset', {uuid, url: result[0].url,});
return failedList;
},
async packProject({state}, debug) {
let packedAssets;
if (!debug) {
packedAssets = await packAssets(state.data.assets);
}
const result = await projectApi.pack(state.id, debug, packedAssets);
console.log(result); console.log(result);
return result; return result;
},
savePreview({state, rootState, getters}) {
const {project} = getters;
const {processes, scripts, customs} = rootState.env;
/*const data = {
processes, scripts, customs,
data: state.data,
};
localStorage.setItem('preview-project-' + project.id, JSON.stringify(data));
localStorage.setItem('preview-ts', Date.now().toString());*/
preprocess(project, state.data, clonePureObj(processes), clonePureObj(scripts), clonePureObj(customs))
},
addBehaviorDirect({state}, {behaviors, alias}) {
let behavior = addBehavior(alias, state.data.processes);
behaviors.push(behavior);
},
deleteBehaviorDirect({state}, {behaviors, index, deleteMeta}) {
let behavior = behaviors.splice(index, 1)[0];
if (deleteMeta) {
deleteProcessMeta(behavior.meta, state.data.processes);
}
},
setMockServeEnabled({state, commit}, enabled) {
localStorage.setItem('mock-enabled-' + state.id, JSON.stringify(enabled));
commit('setMockServeEnabled', enabled);
return enabled;
} }
}, },
}; };
async function uploadFiles(files) {
const failedList = [];
let ps = [];
for (let file of files) {
events.$emit('upload-indicator', true);
ps.push(
uploadFile(file).catch(e => {
failedList.push(file);
}).finally(() => {
events.$emit('upload-indicator', false);
})
);
}
const result = await Promise.all(ps);
return {
failedList,
result,
};
}
async function packAssets(assets) {
let failedList = [];
let newAssets = assets.concat();
await packImages(newAssets);
for (let asset of newAssets) {
if (asset.file) {
const {url} = await uploadFile(asset.file, true).catch(e => {
failedList.push(asset);
});
let sheetConfig = {
file: url,
type: 'sheet',
frames: asset.frames,
};
let sheetConfigFile = new File([JSON.stringify(sheetConfig)], 'sheet.json', {type: 'plain/text'});
const {url: sheetConfigUrl} = await uploadFile(sheetConfigFile).catch(e => {
failedList.push(asset);
});
asset.url = sheetConfigUrl;
asset.uuid = generateUUID();
delete asset.file;
delete asset.frames;
}
}
return newAssets;
}
export async function updateMock(mocks) {
await db.clear('mock');
for (let mock of mocks) {
db.set('mock', mock);
}
}
...@@ -4,30 +4,43 @@ ...@@ -4,30 +4,43 @@
import {projectApi} from "../../api"; import {projectApi} from "../../api";
export const projectsStore = { export const projectsStore = {
state: [], state: {
projectCount: 0,
projects: [],
},
mutations: { mutations: {
updateProjects(state, projects) { updateProjects(state, {projects, projectCount}) {
state.splice(0); state.projects.splice(0);
state.push(...projects); state.projects.push(...projects);
state.projectCount = projectCount;
}, },
addProject(state, project) { addProject(state, project) {
state.unshift(project); state.projects.unshift(project);
}, },
deleteProject(state, projectID) { deleteProject(state, projectID) {
for (let i = 0, li = state.length; i < li; i++) { const {projects} = state;
const item = state[i];
for (let i = 0, li = projects.length; i < li; i++) {
const item = projects[i];
if (item.id === projectID) { if (item.id === projectID) {
state.splice(i, 1); projects.splice(i, 1);
break; break;
} }
} }
}, },
}, },
actions: { actions: {
async updateProjects({commit}) { async fetchProjects({commit, state}, {currentPage, pageSize, onlyMine}) {
const projects = await projectApi.fetchAll(); const data = await projectApi.fetchAll(currentPage, pageSize, onlyMine);
commit('updateProjects', projects); commit('updateProjects', data);
},
async fetchProject({commit}, projectId) {
return await projectApi.fetchOne(projectId);
},
async fetchHistory({commit}, {projectID, currentPage, pageSize}) {
return await projectApi.fetchHistory(projectID, currentPage, pageSize);
}, },
async createProject({commit}, data) { async createProject({commit}, data) {
const project = await projectApi.createOne(data); const project = await projectApi.createOne(data);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
*/ */
export const template = export const template =
`<!DOCTYPE html> `<!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
...@@ -17,27 +17,28 @@ content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, u ...@@ -17,27 +17,28 @@ content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, u
<meta name="x5-fullscreen" content="true"/> <meta name="x5-fullscreen" content="true"/>
<meta name="360-fullscreen" content="true"/> <meta name="360-fullscreen" content="true"/>
<style> <style>
html, html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
border: 0; border: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
position: absolute; position: absolute;
background-color: white; background-color: white;
} }
</style> </style>
</head> </head>
<body> <body>
<div id="$CONTAINER_ID$" style="line-height:0;font-size:0"></div> <div id="$CONTAINER_ID$" style="line-height:0;font-size:0"></div>
<script src="http://10.10.94.134:4002/dist/index.js"></script> <script src="${process.env.NODE_ENV === 'development' ? 'http://10.10.92.100:4002/debug/engine.js' : 'http://yun.duiba.com.cn/editor/zeroing/libs/engine.269ce0ee2f951a11e004eee2df2fa1d875fa0b62.js'}"></script>
$SCRIPTS$
<script> <script>
engine.launch('//yun.duiba.com.cn/aurora/$VERSION$-data.json'); engine.launch('//yun.duiba.com.cn/aurora/$VERSION$-data.json');
</script> </script>
</body> </body>
</html> </html>
`; `;
...@@ -54,108 +54,6 @@ ...@@ -54,108 +54,6 @@
.assets-scrollbar { .assets-scrollbar {
flex: 1; flex: 1;
.file-list {
display: flex;
flex: 1;
flex-direction: row;
flex-wrap: wrap;
.file-item {
width: 60px;
padding: 5px;
color: $--color-text-regular;
&:hover {
& > .icon > .operate-bar {
display: block;
}
& > .name > .full-name {
display: block;
}
}
.icon {
width: 100%;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.thumbnail {
max-width: 60px;
max-height: 60px;
}
.file-icon {
font-size: 60px;
}
.operate-bar {
display: none;
position: absolute;
right: 1px;
bottom: 1px;
.el-button {
padding: 2px;
margin-left: 2px;
}
}
}
.name {
text-align: center;
font-size: 12px;
position: relative;
margin-top: 2px;
span {
word-break: break-all;
overflow:hidden;
text-overflow: ellipsis;
display:-webkit-box;
-webkit-line-clamp:2;
-webkit-box-orient:vertical;
}
.full-name {
display: none;
position: absolute;
top: 0;
left: 0;
background-color: $--color-text-secondary;
color: $--color-white;
border-radius: 5px;
padding: 2px;
z-index: 2;
width: 95%;
line-height: 17px;
}
}
}
.file-uploader {
width: 60px;
height: 60px;
margin: 4px;
border: 1px dashed $--color-text-secondary;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
.file-uploader-icon {
font-size: 28px;
color: $--color-text-regular;
width: 60px;
height: 60px;
line-height: 60px;
text-align: center;
}
}
}
} }
} }
...@@ -166,6 +64,114 @@ ...@@ -166,6 +64,114 @@
} }
} }
.file-list {
display: flex;
flex: 1;
flex-direction: row;
flex-wrap: wrap;
.file-item {
width: 60px;
padding: 5px;
color: $--color-text-regular;
&:hover {
& > .icon > .operate-bar {
visibility: visible;
}
& > .name > .full-name {
visibility: visible;
}
}
.icon {
width: 100%;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.thumbnail {
max-width: 60px;
max-height: 60px;
}
.file-icon {
font-size: 60px;
}
.operate-bar {
visibility: hidden;
position: absolute;
right: 1px;
bottom: 1px;
display: flex;
.el-button + .el-button{
margin-left: 2px;
}
.el-button {
padding: 2px;
}
}
}
.name {
text-align: center;
font-size: 12px;
position: relative;
margin-top: 2px;
span {
word-break: break-all;
overflow:hidden;
text-overflow: ellipsis;
display:-webkit-box;
-webkit-line-clamp:2;
-webkit-box-orient:vertical;
}
.full-name {
visibility: hidden;
position: absolute;
top: 0;
left: 0;
background-color: $--color-text-secondary;
color: $--color-white;
border-radius: 5px;
padding: 2px;
z-index: 2;
width: 95%;
line-height: 17px;
}
}
}
.file-uploader {
width: 60px;
height: 60px;
margin: 4px;
border: 1px dashed $--color-text-secondary;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
.file-uploader-icon {
font-size: 28px;
color: $--color-text-regular;
width: 60px;
height: 60px;
line-height: 60px;
text-align: center;
}
}
}
.assets-show { .assets-show {
width: 100%; width: 100%;
height: 100%; height: 100%;
......
...@@ -2,15 +2,7 @@ ...@@ -2,15 +2,7 @@
$dock-pin-width: 9px; $dock-pin-width: 9px;
.behavior-editor-dialog { .behavior-editor-dialog {
display: flex;
flex-direction: column;
.el-dialog__body {
flex: 1;
padding: 5px;
}
.process-pane { .process-pane {
...@@ -35,6 +27,13 @@ $dock-pin-width: 9px; ...@@ -35,6 +27,13 @@ $dock-pin-width: 9px;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
font-size: 14px; font-size: 14px;
.current-node {
background-color: $--color-text-secondary;
color: $--color-white;
border-radius: 5px;
padding: 0 2px;
}
} }
.edit-button { .edit-button {
...@@ -56,14 +55,28 @@ $dock-pin-width: 9px; ...@@ -56,14 +55,28 @@ $dock-pin-width: 9px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.edit-path { .el-button {
padding: 5px; padding: 3px;
height: 14px; margin-top: 2px;
margin-right: 2px;
}
border-bottom: 1px solid $--border-color-light; .top-bar {
display: flex;
justify-content: space-between;
align-items: flex-start;
.edit-path {
flex: 1;
padding: 5px;
height: 14px;
border-bottom: 1px solid $--border-color-light;
}
} }
.operate-bar{
.operate-bar {
padding: 3px; padding: 3px;
border-bottom: 1px solid $--border-color-light; border-bottom: 1px solid $--border-color-light;
...@@ -100,6 +113,7 @@ $dock-pin-width: 9px; ...@@ -100,6 +113,7 @@ $dock-pin-width: 9px;
visibility: visible; visibility: visible;
} }
} }
/*&:hover { /*&:hover {
border-color: $block-border-hover-background-color; border-color: $block-border-hover-background-color;
...@@ -126,6 +140,10 @@ $dock-pin-width: 9px; ...@@ -126,6 +140,10 @@ $dock-pin-width: 9px;
align-self: flex-end; align-self: flex-end;
visibility: hidden; visibility: hidden;
.el-link {
padding: 3px 2px 2px 2px;
}
.el-link + .el-link { .el-link + .el-link {
margin-left: 5px; margin-left: 5px;
} }
...@@ -141,7 +159,7 @@ $dock-pin-width: 9px; ...@@ -141,7 +159,7 @@ $dock-pin-width: 9px;
color: white; color: white;
display: flex; display: flex;
i{ i {
display: block; display: block;
//color: $--color-warning; //color: $--color-warning;
margin-right: 3px; margin-right: 3px;
...@@ -299,8 +317,31 @@ $dock-pin-width: 9px; ...@@ -299,8 +317,31 @@ $dock-pin-width: 9px;
.scrollbar-view { .scrollbar-view {
padding-right: 10px; padding-right: 10px;
.input-wrapper {
.label {
p {
margin: 0;
//text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.alias {
line-height: 16px;
font-size: 14px;
}
.property-name {
line-height: 12px;
font-size: 12px;
color: $--color-text-secondary;
}
}
}
} }
.el-form-item__content { .el-form-item__content {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
...@@ -320,8 +361,9 @@ $dock-pin-width: 9px; ...@@ -320,8 +361,9 @@ $dock-pin-width: 9px;
} }
.meta-editor-wrapper { .meta-editor-wrapper {
height: 40vh; //height: 40vh;
display: flex; display: flex;
height: 100%;
padding: 5px; padding: 5px;
flex-direction: column; flex-direction: column;
...@@ -332,6 +374,15 @@ $dock-pin-width: 9px; ...@@ -332,6 +374,15 @@ $dock-pin-width: 9px;
.script-editor { .script-editor {
flex: 1; flex: 1;
} }
.vue-codemirror {
flex: 1;
height: 0;
}
.CodeMirror {
height: 100%;
}
} }
.props-editor-dialog { .props-editor-dialog {
...@@ -389,3 +440,23 @@ $dock-pin-width: 9px; ...@@ -389,3 +440,23 @@ $dock-pin-width: 9px;
margin-top: 5px; margin-top: 5px;
} }
} }
.property-name-popover{
p {
margin: 0;
//text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.alias {
line-height: 16px;
font-size: 14px;
}
.property-name {
line-height: 12px;
font-size: 12px;
color: $--color-text-secondary;
}
}
@import "var";
.code-sync-indicator {
padding: 5px;
cursor: pointer;
.indicator {
background-color: red;
width: 10px;
height: 10px;
border-radius: 50%;
}
.indicator.enabled {
background-color: limegreen;
}
}
.bottom-bar{
height: 20px;
display: flex;
align-items: center;
font-size: 12px;
color: $--color-text-secondary;
padding: 0 5px;
.editor-status{
}
.file-upload-indicator {
}
}
\ No newline at end of file
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
@import "views"; @import "views";
@import "assets"; @import "assets";
@import "behavior"; @import "behavior";
@import "bottom-bar";
.editor { .editor {
display: flex; display: flex;
...@@ -25,10 +26,6 @@ ...@@ -25,10 +26,6 @@
} }
} }
.file-upload-indicator{
color: $--color-text-secondary;
}
.right-part { .right-part {
color: $--color-text-regular; color: $--color-text-regular;
line-height: 15px; line-height: 15px;
...@@ -41,6 +38,7 @@ ...@@ -41,6 +38,7 @@
.pane-container { .pane-container {
flex: 1; flex: 1;
height: 0;
} }
} }
...@@ -56,11 +54,11 @@ ...@@ -56,11 +54,11 @@
align-self: flex-start; align-self: flex-start;
} }
.bottom-bar{ .bottom-bar {
align-self: flex-end; align-self: flex-end;
} }
.list{ .list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
...@@ -100,21 +98,46 @@ ...@@ -100,21 +98,46 @@
} }
.details-dialog { .details-dialog {
.operate-bar{ .operate-bar {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
padding: 5px; padding: 5px;
} }
.tabs {
height: 100%;
display: flex;
flex-direction: column;
.el-tabs__content {
flex: 1;
& > div {
height: 100%;
}
}
}
.project-editor {
height: 100%;
display: flex;
flex-direction: column;
.project-scrollbar {
flex: 1;
}
}
.scrollbar { .scrollbar {
height: 40vh; height: 100%;
.view { .view {
padding-right: 10px; padding-right: 10px;
.custom-module-list{ .custom-module-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.item + .item { .item + .item {
margin-top: 5px; margin-top: 5px;
} }
...@@ -122,3 +145,29 @@ ...@@ -122,3 +145,29 @@
} }
} }
} }
.mock-editor-dialog {
.mock-editor-view {
height: 100%;
display: flex;
flex-direction: column;
.enabled-switch {
align-self: flex-end;
}
.add-button {
align-self: flex-start;
}
.mock-table {
flex: 1;
}
}
.dialog-footer {
display: flex;
justify-content: space-between;
}
}
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
.editor-name { .editor-name {
font-size: 50px; font-size: 50px;
color: $--color-primary; color: $--color-primary;
margin: 10px 0;
} }
.editor-version { .editor-version {
...@@ -25,6 +26,7 @@ ...@@ -25,6 +26,7 @@
.project-list { .project-list {
flex: 1; flex: 1;
margin-bottom: 10px; margin-bottom: 10px;
height: 0;
} }
.project-info { .project-info {
...@@ -43,4 +45,32 @@ ...@@ -43,4 +45,32 @@
.project-detail { .project-detail {
} }
}
\ No newline at end of file .bottom-bar {
display: flex;
justify-content: space-between;
}
}
.project-history-table {
height: 50vh;
display: flex;
flex-direction: column;
.popover-remark {
width: 100%;
display: block;
.short-remark {
width: 100%;
display: block;
white-space: nowrap;
overflow: hidden;
}
}
.pagination {
align-self: flex-end;
margin-top: 5px;
}
}
/** /**
* Created by rockyl on 2019-09-18. * Created by rockyl on 2019-09-18.
*/ */
@import "./var.scss"; @import "./var.scss";
@import "./base.scss"; @import "./base.scss";
@import "./home.scss"; @import "./home.scss";
@import "./editor.scss"; @import "./editor.scss";
@import "./playground.scss"; @import "./playground.scss";
@import "./inspector.scss"; @import "./inspector.scss";
@import "~element-ui/packages/theme-chalk/src/index.scss"; @import "~element-ui/packages/theme-chalk/src/index.scss";
.alpha-image-background{
background-image: url('data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAiUlEQVQ4jZ1TSxLFIAhLOp7C+19NjyFvlQ7lUUvLRgXzgVGOMQyb6L3/5cwMJAEAxw6sy3FP8tw/EkhJqp7klQMBtKpWcpC1oVp7IoiqviXg4xBFRPL7EM/6WsviYDxwzrkVaBHsz5U4MlWfKxHEySpXIdk6qLRzcXBHVnKQfZRKCy1Ty979XfwApOBe0rB0KiIAAAAASUVORK5CYII=');
}
.flex-dialog {
display: flex;
flex-direction: column;
.el-dialog__body {
flex: 1;
height: 0;
padding: 5px;
}
}
.el-tree { .el-tree {
background: transparent; background: transparent;
} }
...@@ -23,15 +36,18 @@ ...@@ -23,15 +36,18 @@
overflow-y: hidden; overflow-y: hidden;
} }
.el-tree-node__content > .el-tree-node__expand-icon{ .el-tree-node__content > .el-tree-node__expand-icon {
padding: 6px 0; padding: 6px 0;
} }
.el-input-number.is-without-controls .el-input__inner{
padding: 0 5px;
}
.el-input__inner { .el-input__inner {
padding: 0 5px; padding: 0 5px;
} }
.el-input--suffix .el-input__inner{ .el-input--suffix .el-input__inner {
padding-right: 5px; padding-right: 5px;
} }
...@@ -43,13 +59,13 @@ ...@@ -43,13 +59,13 @@
height: 100%; height: 100%;
} }
.el-tabs--border-card > .el-tabs__content{ .el-tabs--border-card > .el-tabs__content {
padding: 5px 0 5px 5px; padding: 5px 0 5px 5px;
} }
.el-input-number.is-controls-right .el-input__inner { .el-input-number.is-controls-right .el-input__inner {
padding-left: 5px; padding-left: 5px;
padding-right: 40px; padding-right: 25px;
} }
.el-input-number--mini .el-input-number__increase, .el-input-number--mini .el-input-number__decrease { .el-input-number--mini .el-input-number__increase, .el-input-number--mini .el-input-number__decrease {
...@@ -60,3 +76,12 @@ ...@@ -60,3 +76,12 @@
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
} }
.CodeMirror-hints, .CodeMirror-lint-tooltip{
z-index: 3000 !important;
}
.el-button.micro {
padding: 3px;
align-self: center;
}
@import "var";
.inspector-tabs { .inspector-tabs {
flex: 1; flex: 1;
border: 0 !important; border: 0 !important;
...@@ -6,6 +8,12 @@ ...@@ -6,6 +8,12 @@
&>:last-child{ &>:last-child{
flex: 1; flex: 1;
height: 0;
}
.el-collapse-item__header{
height: 25px;
line-height: 25px;
} }
.el-tabs__item { .el-tabs__item {
...@@ -45,12 +53,93 @@ ...@@ -45,12 +53,93 @@
} }
} }
.zero-inspector-behavior-form { .zero-inspector-behavior {
height: 100%; height: 100%;
display: flex;
flex-direction: column;
.add-trigger{
align-self: flex-start;
}
.scrollbar{ .scrollbar{
margin-top: 5px;
height: 100%; height: 100%;
} }
.el-button + .el-button {
margin-left: 5px;
}
.trigger-list{
color: $--color-text-regular;
font-size: 12px;
padding-right: 10px;
.trigger-item + .trigger-item {
margin-top: 5px;
}
.trigger-item {
border: 1px solid lightgray;
border-radius: 3px;
padding: 3px;
.el-button{
padding: 3px;
}
.top-bar{
display: flex;
justify-content: space-between;
align-items: center;
.name{
color: $--color-primary;
font-size: 14px;
font-weight: bold;
flex: 1;
cursor: default;
}
}
.behavior-list {
.behavior-item {
&:hover {
& > .delete-button {
visibility: visible;
}
}
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 2px;
.name-field{
flex: 1;
margin-left: 3px;
}
.missing-behavior{
color: $--color-danger;
}
.name-input {
border: 0;
outline: transparent;
}
.edit-button {
}
.delete-button {
visibility: hidden;
}
}
}
}
}
} }
} }
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
} }
.active {
border: 2px dashed rgb(20, 100, 206);
}
} }
// .zero-playground-draw-panel{ // .zero-playground-draw-panel{
// min-height: 1200px; // min-height: 1200px;
...@@ -60,6 +64,3 @@ ...@@ -60,6 +64,3 @@
cursor: default; cursor: default;
user-select: none; user-select: none;
} }
.active {
border: 2px dashed rgb(20, 100, 206);
}
/**
* Based on the Public Domain MaxRectanglesBinPack.cpp source by Jukka Jylänki
* https://github.com/juj/RectangleBinPack/
*
* Based on C# port by Sven Magnus
* http://unifycommunity.com/wiki/index.php?title=MaxRectanglesBinPack
*
* Based on ActionScript3 by DUZENGQIANG
* http://www.duzengqiang.com/blog/post/971.html
*
* Ported to javascript by 06wj
* https://github.com/06wj/MaxRectsBinPack
*/
/**
* Rect
* @param {Number} x 矩形坐标x
* @param {Number} y 矩形坐标y
* @param {Number} width 矩形宽
* @param {Number} height 矩形高
*/
function Rect(x, y, width, height) {
this.x = x || 0;
this.y = y || 0;
this.width = width || 0;
this.height = height || 0;
}
Rect.prototype = {
constructor: Rect,
/**
* clone 复制
* @return {Rect}
*/
clone: function () {
return new Rect(this.x, this.y, this.width, this.height);
}
};
Rect.isContainedIn = function (a, b) {
return a.x >= b.x && a.y >= b.y
&& a.x + a.width <= b.x + b.width
&& a.y + a.height <= b.y + b.height;
};
var BestShortSideFit = 0; ///< -BSSF: Positions the Rectangle against the short side of a free Rectangle into which it fits the best.
var BestLongSideFit = 1; ///< -BLSF: Positions the Rectangle against the long side of a free Rectangle into which it fits the best.
var BestAreaFit = 2; ///< -BAF: Positions the Rectangle into the smallest free Rectangle into which it fits.
var BottomLeftRule = 3; ///< -BL: Does the Tetris placement.
var ContactPointRule = 4; ///< -CP: Choosest the placement where the Rectangle touches other Rectangles as much as possible.
/**
* MaxRectanglesBinPack
* @param {Number} width 容器宽度
* @param {Number} height 容器高度
* @param {Boolean} allowRotate 是否允许旋转
*/
export default function MaxRectsBinPack(width, height, allowRotate) {
this.binWidth = 0;
this.binHeight = 0;
this.allowRotate = false;
this.usedRectangles = [];
this.freeRectangles = [];
this.init(width, height, allowRotate);
}
MaxRectsBinPack.prototype = {
constructor: MaxRectsBinPack,
/**
* 初始化
* @param {Number} width 容器宽度
* @param {Number} height 容器高度
* @param {Boolean} allowRotate 是否允许旋转
*/
init: function (width, height, allowRotate) {
this.binWidth = width;
this.binHeight = height;
this.allowRotate = allowRotate || false;
this.usedRectangles.length = 0;
this.freeRectangles.length = 0;
this.freeRectangles.push(new Rect(0, 0, width, height));
},
/**
* insert a new rect
* @param {Number} width 矩形宽
* @param {Number} height 矩形高
* @param {Number} method 分配方法 0~4
* @return {Rect}
*/
insert: function (width, height, method) {
var newNode = new Rect();
var score1 = {
value: 0
};
var score2 = {
value: 0
};
method = method || 0;
switch (method) {
case BestShortSideFit:
newNode = this._findPositionForNewNodeBestShortSideFit(width, height, score1, score2);
break;
case BottomLeftRule:
newNode = this._findPositionForNewNodeBottomLeft(width, height, score1, score2);
break;
case ContactPointRule:
newNode = this._findPositionForNewNodeContactPoint(width, height, score1);
break;
case BestLongSideFit:
newNode = this._findPositionForNewNodeBestLongSideFit(width, height, score2, score1);
break;
case BestAreaFit:
newNode = this._findPositionForNewNodeBestAreaFit(width, height, score1, score2);
break;
}
if (newNode.height === 0) {
return newNode;
}
this._placeRectangle(newNode);
return newNode;
},
/**
* 插入一组矩形
* @param {Array} rectangles 矩形数组
* @param {Number} method 分配方法 0~4
* @return {Array} 成功插入的数组
*/
insertRects: function (rectangles, method) {
var res = [];
while (rectangles.length > 0) {
var bestScore1 = Infinity;
var bestScore2 = Infinity;
var bestRectangleIndex = -1;
var bestNode = new Rect();
for (var i = 0; i < rectangles.length; i++) {
var score1 = {
value: 0
};
var score2 = {
value: 0
};
var newNode = this._scoreRectangle(rectangles[i].width, rectangles[i].height, method, score1, score2);
if (score1.value < bestScore1 || (score1.value == bestScore1 && score2.value < bestScore2)) {
bestScore1 = score1.value;
bestScore2 = score2.value;
bestNode = newNode;
bestRectangleIndex = i;
}
}
if (bestRectangleIndex == -1) {
return res;
}
this._placeRectangle(bestNode);
var rect = rectangles.splice(bestRectangleIndex, 1)[0];
rect.x = bestNode.x;
rect.y = bestNode.y;
res.push(rect);
}
return res;
},
_placeRectangle: function (node) {
var numRectanglesToProcess = this.freeRectangles.length;
for (var i = 0; i < numRectanglesToProcess; i++) {
if (this._splitFreeNode(this.freeRectangles[i], node)) {
this.freeRectangles.splice(i, 1);
i--;
numRectanglesToProcess--;
}
}
this._pruneFreeList();
this.usedRectangles.push(node);
},
_scoreRectangle: function (width, height, method, score1, score2) {
var newNode = new Rect();
score1.value = Infinity;
score2.value = Infinity;
switch (method) {
case BestShortSideFit:
newNode = this._findPositionForNewNodeBestShortSideFit(width, height, score1, score2);
break;
case BottomLeftRule:
newNode = this._findPositionForNewNodeBottomLeft(width, height, score1, score2);
break;
case ContactPointRule:
newNode = this._findPositionForNewNodeContactPoint(width, height, score1);
// todo: reverse
score1 = -score1; // Reverse since we are minimizing, but for contact point score bigger is better.
break;
case BestLongSideFit:
newNode = this._findPositionForNewNodeBestLongSideFit(width, height, score2, score1);
break;
case BestAreaFit:
newNode = this._findPositionForNewNodeBestAreaFit(width, height, score1, score2);
break;
}
// Cannot fit the current Rectangle.
if (newNode.height === 0) {
score1.value = Infinity;
score2.value = Infinity;
}
return newNode;
},
_occupancy: function () {
var usedRectangles = this.usedRectangles;
var usedSurfaceArea = 0;
for (var i = 0; i < usedRectangles.length; i++) {
usedSurfaceArea += usedRectangles[i].width * usedRectangles[i].height;
}
return usedSurfaceArea / (this.binWidth * this.binHeight);
},
_findPositionForNewNodeBottomLeft: function (width, height, bestY, bestX) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(bestNode, 0, sizeof(Rectangle));
bestY.value = Infinity;
var rect;
var topSideY;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
topSideY = rect.y + height;
if (topSideY < bestY.value || (topSideY == bestY.value && rect.x < bestX.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestY.value = topSideY;
bestX.value = rect.x;
}
}
if (this.allowRotate && rect.width >= height && rect.height >= width) {
topSideY = rect.y + width;
if (topSideY < bestY.value || (topSideY == bestY.value && rect.x < bestX.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestY.value = topSideY;
bestX.value = rect.x;
}
}
}
return bestNode;
},
_findPositionForNewNodeBestShortSideFit: function (width, height, bestShortSideFit, bestLongSideFit) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rectangle));
bestShortSideFit.value = Infinity;
var rect;
var leftoverHoriz;
var leftoverVert;
var shortSideFit;
var longSideFit;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
leftoverHoriz = Math.abs(rect.width - width);
leftoverVert = Math.abs(rect.height - height);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
longSideFit = Math.max(leftoverHoriz, leftoverVert);
if (shortSideFit < bestShortSideFit.value || (shortSideFit == bestShortSideFit.value && longSideFit < bestLongSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestShortSideFit.value = shortSideFit;
bestLongSideFit.value = longSideFit;
}
}
var flippedLeftoverHoriz;
var flippedLeftoverVert;
var flippedShortSideFit;
var flippedLongSideFit;
if (this.allowRotate && rect.width >= height && rect.height >= width) {
flippedLeftoverHoriz = Math.abs(rect.width - height);
flippedLeftoverVert = Math.abs(rect.height - width);
flippedShortSideFit = Math.min(flippedLeftoverHoriz, flippedLeftoverVert);
flippedLongSideFit = Math.max(flippedLeftoverHoriz, flippedLeftoverVert);
if (flippedShortSideFit < bestShortSideFit.value || (flippedShortSideFit == bestShortSideFit.value && flippedLongSideFit < bestLongSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestShortSideFit.value = flippedShortSideFit;
bestLongSideFit.value = flippedLongSideFit;
}
}
}
return bestNode;
},
_findPositionForNewNodeBestLongSideFit: function (width, height, bestShortSideFit, bestLongSideFit) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rectangle));
bestLongSideFit.value = Infinity;
var rect;
var leftoverHoriz;
var leftoverVert;
var shortSideFit;
var longSideFit;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
leftoverHoriz = Math.abs(rect.width - width);
leftoverVert = Math.abs(rect.height - height);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
longSideFit = Math.max(leftoverHoriz, leftoverVert);
if (longSideFit < bestLongSideFit.value || (longSideFit == bestLongSideFit.value && shortSideFit < bestShortSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestShortSideFit.value = shortSideFit;
bestLongSideFit.value = longSideFit;
}
}
if (this.allowRotate && rect.width >= height && rect.height >= width) {
leftoverHoriz = Math.abs(rect.width - height);
leftoverVert = Math.abs(rect.height - width);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
longSideFit = Math.max(leftoverHoriz, leftoverVert);
if (longSideFit < bestLongSideFit.value || (longSideFit == bestLongSideFit.value && shortSideFit < bestShortSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestShortSideFit.value = shortSideFit;
bestLongSideFit.value = longSideFit;
}
}
}
return bestNode;
},
_findPositionForNewNodeBestAreaFit: function (width, height, bestAreaFit, bestShortSideFit) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rectangle));
bestAreaFit.value = Infinity;
var rect;
var leftoverHoriz;
var leftoverVert;
var shortSideFit;
var areaFit;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
areaFit = rect.width * rect.height - width * height;
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
leftoverHoriz = Math.abs(rect.width - width);
leftoverVert = Math.abs(rect.height - height);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
if (areaFit < bestAreaFit.value || (areaFit == bestAreaFit.value && shortSideFit < bestShortSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestShortSideFit.value = shortSideFit;
bestAreaFit = areaFit;
}
}
if (this.allowRotate && rect.width >= height && rect.height >= width) {
leftoverHoriz = Math.abs(rect.width - height);
leftoverVert = Math.abs(rect.height - width);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
if (areaFit < bestAreaFit.value || (areaFit == bestAreaFit.value && shortSideFit < bestShortSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestShortSideFit.value = shortSideFit;
bestAreaFit.value = areaFit;
}
}
}
return bestNode;
},
/// Returns 0 if the two intervals i1 and i2 are disjoint, or the length of their overlap otherwise.
_commonIntervalLength: function (i1start, i1end, i2start, i2end) {
if (i1end < i2start || i2end < i1start) {
return 0;
}
return Math.min(i1end, i2end) - Math.max(i1start, i2start);
},
_contactPointScoreNode: function (x, y, width, height) {
var usedRectangles = this.usedRectangles;
var score = 0;
if (x == 0 || x + width === this.binWidth)
score += height;
if (y == 0 || y + height === this.binHeight)
score += width;
var rect;
for (var i = 0; i < usedRectangles.length; i++) {
rect = usedRectangles[i];
if (rect.x == x + width || rect.x + rect.width == x)
score += this._commonIntervalLength(rect.y, rect.y + rect.height, y, y + height);
if (rect.y == y + height || rect.y + rect.height == y)
score += this._commonIntervalLength(rect.x, rect.x + rect.width, x, x + width);
}
return score;
},
_findPositionForNewNodeContactPoint: function (width, height, bestContactScore) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rectangle));
bestContactScore.value = -1;
var rect;
var score;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
score = this._contactPointScoreNode(rect.x, rect.y, width, height);
if (score > bestContactScore.value) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestContactScore = score;
}
}
if (this.allowRotate && rect.width >= height && rect.height >= width) {
score = this._contactPointScoreNode(rect.x, rect.y, height, width);
if (score > bestContactScore.value) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestContactScore.value = score;
}
}
}
return bestNode;
},
_splitFreeNode: function (freeNode, usedNode) {
var freeRectangles = this.freeRectangles;
// Test with SAT if the Rectangles even intersect.
if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x ||
usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y)
return false;
var newNode;
if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x) {
// New node at the top side of the used node.
if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height) {
newNode = freeNode.clone();
newNode.height = usedNode.y - newNode.y;
freeRectangles.push(newNode);
}
// New node at the bottom side of the used node.
if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) {
newNode = freeNode.clone();
newNode.y = usedNode.y + usedNode.height;
newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height);
freeRectangles.push(newNode);
}
}
if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y) {
// New node at the left side of the used node.
if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width) {
newNode = freeNode.clone();
newNode.width = usedNode.x - newNode.x;
freeRectangles.push(newNode);
}
// New node at the right side of the used node.
if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) {
newNode = freeNode.clone();
newNode.x = usedNode.x + usedNode.width;
newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width);
freeRectangles.push(newNode);
}
}
return true;
},
_pruneFreeList: function () {
var freeRectangles = this.freeRectangles;
for (var i = 0; i < freeRectangles.length; i++)
for (var j = i + 1; j < freeRectangles.length; j++) {
if (Rect.isContainedIn(freeRectangles[i], freeRectangles[j])) {
freeRectangles.splice(i, 1);
break;
}
if (Rect.isContainedIn(freeRectangles[j], freeRectangles[i])) {
freeRectangles.splice(j, 1);
}
}
}
};
import _ from 'lodash'; import _ from 'lodash';
import properties from './properties'; import properties from './properties';
export const componentsMap = [
{
label: '文本',
value: 'label'
},
{
label: '图片',
value: 'image'
},
{
label: '视图',
value: 'node'
},
{
label: '矩形',
value: 'rect'
},
{
label: '圆形',
value: 'circle'
},
{
label: '滚动视图',
value: 'scrollView'
},
];
// 属性的计算方法 // 属性的计算方法
const propsComputeRules = { const propsComputeRules = {
x: 'add', x: 'add',
...@@ -66,7 +39,7 @@ const attrShortMapper = { ...@@ -66,7 +39,7 @@ const attrShortMapper = {
// 编辑时想拖拽组件需要生成的css属性 // 编辑时想拖拽组件需要生成的css属性
// 只需要位置,不需要来源透明度等等 // 只需要位置,不需要来源透明度等等
const operatProps = ['x', 'y', 'left', 'top', 'right', 'bottom', 'width', 'height', 'rotation']; const operatProps = ['x', 'y', 'left', 'top', 'right', 'bottom', 'width', 'height', 'rotation', 'scaleX', 'scaleY'];
const ignoreProps = ['left', 'right', 'top', 'bottom']; const ignoreProps = ['left', 'right', 'top', 'bottom'];
// 属性单位 对照表, 如果是数值的时候需要添加单位 // 属性单位 对照表, 如果是数值的时候需要添加单位
...@@ -107,7 +80,7 @@ function getParentCmps(uuid, list) { ...@@ -107,7 +80,7 @@ function getParentCmps(uuid, list) {
return _self.parent ? parentLoop(_self.parent, list) : []; return _self.parent ? parentLoop(_self.parent, list) : [];
} }
export { getParentCmps, completeSelfProps }; export {getParentCmps, completeSelfProps};
export const getCmpByUUID = function (uuid, views) { export const getCmpByUUID = function (uuid, views) {
if (!uuid) { if (!uuid) {
...@@ -207,10 +180,10 @@ export const styles = { ...@@ -207,10 +180,10 @@ export const styles = {
return ['transform', [`rotate(${value}deg)`]]; //`transform: rotate(${value}deg);`; return ['transform', [`rotate(${value}deg)`]]; //`transform: rotate(${value}deg);`;
case 'background-image': case 'background-image':
return ['background-image', [`url(${value})`]]; //`background-image: url(${value});background-position:center;background-size:contain;`; return ['background-image', [`url(${value})`]]; //`background-image: url(${value});background-position:center;background-size:contain;`;
// case 'scale-x': case 'scale-x':
// return ['transform', [`scaleX(${value})`]]; //`transform: scaleX(${value});`; return ['transform', [`scaleX(${value})`]]; //`transform: scaleX(${value});`;
// case 'scale-y': case 'scale-y':
// return ['transform', [`scaleY(${value})`]]; //`transform: scaleY(${value});`; return ['transform', [`scaleY(${value})`]]; //`transform: scaleY(${value});`;
case 'visible': case 'visible':
return ['display', [value ? 'block' : 'none']]; // `display: ${value ? 'block' : 'none'};`; return ['display', [value ? 'block' : 'none']]; // `display: ${value ? 'block' : 'none'};`;
default: default:
...@@ -265,6 +238,11 @@ export const styles = { ...@@ -265,6 +238,11 @@ export const styles = {
}); });
} }
if (component.type === 'image') {
cmpSelfProps.width = cmpSelfProps.width || cmpSelfProps.imageWidth;
cmpSelfProps.height = cmpSelfProps.height || cmpSelfProps.imageHeight;
}
// 获取拖拽组件的样式,只生成operatProps中的属性 // 获取拖拽组件的样式,只生成operatProps中的属性
if (onlyOpera) { if (onlyOpera) {
_.forIn(cmpSelfProps, (val, key) => { _.forIn(cmpSelfProps, (val, key) => {
...@@ -276,14 +254,16 @@ export const styles = { ...@@ -276,14 +254,16 @@ export const styles = {
// 如果节点的width/height未定义,则设置拖拽组件的width/height为0 // 如果节点的width/height未定义,则设置拖拽组件的width/height为0
cmpSelfProps.width = cmpSelfProps.width || 0; cmpSelfProps.width = cmpSelfProps.width || 0;
cmpSelfProps.height = cmpSelfProps.height || 0; cmpSelfProps.height = cmpSelfProps.height || 0;
}
if (cmpSelfProps.scaleX && cmpSelfProps.width) { if (cmpSelfProps.scaleX && cmpSelfProps.width) {
cmpSelfProps.width *= cmpSelfProps.scaleX; cmpSelfProps.width *= cmpSelfProps.scaleX;
} delete cmpSelfProps.scaleX;
}
if (cmpSelfProps.scaleY && cmpSelfProps.height) { if (cmpSelfProps.scaleY && cmpSelfProps.height) {
cmpSelfProps.height *= cmpSelfProps.scaleY; cmpSelfProps.height *= cmpSelfProps.scaleY;
delete cmpSelfProps.scaleY;
}
} }
// 把不需要参与样式计算的属性干掉 // 把不需要参与样式计算的属性干掉
...@@ -291,10 +271,20 @@ export const styles = { ...@@ -291,10 +271,20 @@ export const styles = {
delete cmpSelfProps[prop]; delete cmpSelfProps[prop];
}); });
if (component.type === 'label') { if (component.type === 'label' || component.type === 'textinput') {
// 如果是label类型,把fillColor转换为color // 如果是label类型,把fillColor转换为color
cmpSelfProps.color = cmpSelfProps.fillColor; cmpSelfProps.color = cmpSelfProps.fillColor;
delete cmpSelfProps.fillColor; delete cmpSelfProps.fillColor;
if (!onlyOpera) {
if (cmpSelfProps.lineType === 'single') {
// word-break: keep-all;white-space: nowrap;
cmpSelfProps.wordBreak = 'keep-all';
cmpSelfProps.whiteSpace = 'nowrap';
} else {
cmpSelfProps.wordBreak = 'break-all';
cmpSelfProps.whiteSpace = 'normal';
}
}
} }
// console.log('cmpSelfProps after inherit ', cmpSelfProps); // console.log('cmpSelfProps after inherit ', cmpSelfProps);
...@@ -305,38 +295,47 @@ export const styles = { ...@@ -305,38 +295,47 @@ export const styles = {
result += 'border-style: solid;' result += 'border-style: solid;'
} }
}); });
result += `background-position: center;background-size: 100% 100%;` result += `background-position: center;background-size: 100% 100%;transform-origin: left top;`
if (component.type === 'circle' && !onlyOpera) { if (component.type === 'circle' && !onlyOpera) {
// 如果是circle类型,加圆角 // 如果是circle类型,加圆角
result += 'border-radius: 50%;' result += 'border-radius: 50%;'
} }
if (component.type === 'label' && !onlyOpera) { // if (component.type === 'label' && !onlyOpera) {
// 如果是label类型,禁止自动换行 // 如果是label类型,禁止自动换行
result += 'word-break: keep-all;white-space: nowrap;' // console.log('cmpSelfProps', cmpSelfProps);
}
// result += 'word-break: keep-all;white-space: nowrap;'
// }
// console.log('getComponentStyle',component.name, result); // console.log('getComponentStyle',component.name, result);
return result; return result;
} }
} }
export const getCmpProps = function (type) { const cmpPropsCache = {};
export function getCmpProps(type) {
if (!type) { if (!type) {
return {} return {}
} else { } else {
let _nodeProps = _.cloneDeep(properties.node); let cmpProps = cmpPropsCache[type];
let _typeProps = {}; if (cmpProps) {
if (type !== 'node') { cmpProps = _.cloneDeep(cmpProps);
_typeProps = _.cloneDeep(properties[type]); } else {
let typeProps = properties[type];
let inherits = [_.cloneDeep(typeProps)];
while (typeProps.base) {
typeProps = cmpPropsCache.hasOwnProperty(typeProps.base) ? cmpPropsCache[typeProps.base] : properties[typeProps.base];
inherits.unshift(_.cloneDeep(typeProps));
}
let result = Object.assign({}, ...inherits);
cmpPropsCache[type] = result;
delete result.base;
delete result.groupName;
} }
let result = {
..._nodeProps,
..._typeProps
};
delete result.groupName;
return result return cmpProps;
} }
} }
\ No newline at end of file
/**
* Created by rockyl on 2019-12-18.
*/
const version = 4;
const storeConfigs = [
{name: 'project', key: 'id'},
{name: 'mock', key: 'path'},
{name: 'preview', key: 'id'},
{name: 'packages', key: 'id'},
];
export default {
open(databaseName = 'store') {
return new Promise((resolve, reject) => {
let request = window.indexedDB.open(databaseName, version);
request.onerror = function (event) {
reject(event.target.error);
};
request.onsuccess = (event) => {
this.db = event.target.result;
resolve(request.result);
};
request.onupgradeneeded = (event) => {
this.db = event.target.result;
this.createStore(storeConfigs);
}
})
},
createStore(stores) {
for (let {name, key} of stores) {
if (!this.db.objectStoreNames.contains(name)) {
this.db.createObjectStore(name, {keyPath: key || 'id'});
}
}
},
get(storeName, id) {
return new Promise((resolve, reject) => {
let request = this.db.transaction([storeName])
.objectStore(storeName)
.get(id);
request.onerror = function (event) {
reject(event.target.error);
};
request.onsuccess = function (event) {
resolve(request.result);
};
})
},
getAll(storeName) {
return new Promise((resolve, reject) => {
let request = this.db.transaction([storeName])
.objectStore(storeName)
.getAll();
request.onerror = function (event) {
reject(event.target.error);
};
request.onsuccess = function (event) {
resolve(request.result);
};
})
},
add(storeName, data) {
return new Promise((resolve, reject) => {
let request = this.db.transaction([storeName], 'readwrite')
.objectStore(storeName)
.add(data);
request.onsuccess = function (event) {
resolve();
};
request.onerror = function (event) {
reject(event.target.error);
}
});
},
put(storeName, data) {
return new Promise((resolve, reject) => {
let request = this.db.transaction([storeName], 'readwrite')
.objectStore(storeName)
.put(data);
request.onsuccess = function (event) {
resolve();
};
request.onerror = function (event) {
reject(event.target.error);
}
});
},
async set(storeName, data) {
try {
await this.add(storeName, data);
} catch (e) {
await this.put(storeName, data);
}
},
remove(storeName, id) {
return new Promise((resolve, reject) => {
let request = this.db.transaction([storeName], 'readwrite')
.objectStore(storeName)
.delete(id);
request.onsuccess = function (event) {
resolve();
};
request.onerror = function (event) {
reject(event.target.error);
}
});
},
clear(storeName){
return new Promise((resolve, reject) => {
let request = this.db.transaction([storeName], 'readwrite')
.objectStore(storeName)
.clear();
request.onsuccess = function (event) {
resolve();
};
request.onerror = function (event) {
reject(event.target.error);
}
});
},
}
\ No newline at end of file
...@@ -14,7 +14,7 @@ export function messageError(e) { ...@@ -14,7 +14,7 @@ export function messageError(e) {
}) })
} }
export function playWaiting(promise, text) { export function playWaiting(promise, text, closeLoading = true) {
const loading = Loading.service({ const loading = Loading.service({
lock: true, lock: true,
text: text || i18n.t('In processing'), text: text || i18n.t('In processing'),
...@@ -24,7 +24,9 @@ export function playWaiting(promise, text) { ...@@ -24,7 +24,9 @@ export function playWaiting(promise, text) {
messageError(e); messageError(e);
throw e; throw e;
}).finally(() => { }).finally(() => {
loading.close(); if (closeLoading) {
loading.close();
}
}) })
} }
...@@ -45,35 +47,20 @@ export function strEllipsis(str, maxLength = 0, rightOffset = 0) { ...@@ -45,35 +47,20 @@ export function strEllipsis(str, maxLength = 0, rightOffset = 0) {
return result; return result;
} }
export function saveAs(blob, fileName) {
if ('msSaveOrOpenBlob' in navigator) {
navigator.msSaveOrOpenBlob(blob, fileName);
} else {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
setTimeout(() => {
URL.revokeObjectURL(url);
}, 500);
}
}
export function getInputDefaultValue(property) { export function getInputDefaultValue(property) {
return property ? property.hasOwnProperty('default') ? property.default + '' : 'unset' : 'unset'; return property ? property.hasOwnProperty('default') ? property.default + '' : 'unset' : 'unset';
} }
export function updateProcesses(process, targetMetaID, replaceMetaID) { export function updateProcesses(processes, targetMetaID, replaceMetaID) {
for (let key in process.sub) { for (let process of processes) {
let subProcess = process.sub[key]; for (let key in process.sub) {
if (subProcess.meta === targetMetaID) { let subProcess = process.sub[key];
subProcess.meta = replaceMetaID; if (subProcess.meta === targetMetaID) {
subProcess.meta = replaceMetaID;
}
}
if (process.metas) {
updateProcesses(process.metas, targetMetaID, replaceMetaID)
} }
} }
} }
...@@ -94,6 +81,26 @@ export function clonePureObj(obj) { ...@@ -94,6 +81,26 @@ export function clonePureObj(obj) {
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
} }
export function saveAs(blob, fileName) {
if ('msSaveOrOpenBlob' in navigator) {
navigator.msSaveOrOpenBlob(blob, fileName);
} else {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
setTimeout(() => {
URL.revokeObjectURL(url);
}, 500);
}
}
export function readTextFile(file) { export function readTextFile(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const fileReader = new FileReader(); const fileReader = new FileReader();
...@@ -109,12 +116,39 @@ export function readTextFile(file) { ...@@ -109,12 +116,39 @@ export function readTextFile(file) {
}) })
} }
export function selectFile(callback, {accept, multiple} = {}) { function scanEntries(item){
return new Promise(resolve => {
if (item.isDirectory) {
let directoryReader = item.createReader();
directoryReader.readEntries(function(es){
resolve(es);
});
}else{
resolve();
}
})
}
export async function scanFiles(item, container) {
const entries = await scanEntries(item);
if(entries){
for(let entry of entries){
container.push(entry);
await scanFiles(entry, container);
}
}
}
export function selectFile(callback, {accept, multiple, acceptDirectory} = {}) {
let input = document.createElement('input'); let input = document.createElement('input');
input.type = 'file'; input.type = 'file';
input.onchange = function (e) { input.onchange = function (e) {
callback(input.files); callback(input.files);
}; };
if(acceptDirectory){
input.webkitdirectory = true;
}
if (accept) { if (accept) {
input.accept = accept; input.accept = accept;
} }
...@@ -123,3 +157,27 @@ export function selectFile(callback, {accept, multiple} = {}) { ...@@ -123,3 +157,27 @@ export function selectFile(callback, {accept, multiple} = {}) {
} }
input.click(); input.click();
} }
export function openPreview(packResult) {
setTimeout(() => {
let url;
if (location.host.startsWith('localhost')) {
url = packResult.tplUrl;
} else {
url = '/preview?url=http:' + packResult.tplUrl;
}
window.open(url, 'blank');
}, 500);
}
export function newScriptEl(url) {
return `<script src="${url}"></script>`
}
export function getMockServeEnabled(projectID) {
let enabled = localStorage.getItem('mock-enabled-' + projectID);
if (enabled) {
enabled = JSON.parse(enabled);
}
return !!enabled;
}
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
import CodeMirror from 'codemirror';
var Pos = CodeMirror.Pos;
function forEach(arr, f) {
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
}
function arrayContains(arr, item) {
if (!Array.prototype.indexOf) {
var i = arr.length;
while (i--) {
if (arr[i] === item) {
return true;
}
}
return false;
}
return arr.indexOf(item) != -1;
}
function scriptHint(editor, keywords, getToken, options) {
// Find the token at the cursor
var cur = editor.getCursor(), token = getToken(editor, cur);
if (/\b(?:string|comment)\b/.test(token.type)) return;
var innerMode = CodeMirror.innerMode(editor.getMode(), token.state);
if (innerMode.mode.helperType === "json") return;
token.state = innerMode.state;
// If it's not a 'word-style' token, ignore the token.
if (!/^[\w$_]*$/.test(token.string)) {
token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
type: token.string == "." ? "property" : null};
} else if (token.end > cur.ch) {
token.end = cur.ch;
token.string = token.string.slice(0, cur.ch - token.start);
}
var tprop = token;
// If it is a property, find out what it is a property of.
while (tprop.type == "property") {
tprop = getToken(editor, Pos(cur.line, tprop.start));
if (tprop.string != ".") return;
tprop = getToken(editor, Pos(cur.line, tprop.start));
if (!context) var context = [];
context.push(tprop);
}
return {list: getCompletions(token, context, keywords, options),
from: Pos(cur.line, token.start),
to: Pos(cur.line, token.end)};
}
function javascriptHint(editor, options) {
return scriptHint(editor, javascriptKeywords,
function (e, cur) {return e.getTokenAt(cur);},
options);
};
CodeMirror.registerHelper("hint", "javascript", javascriptHint);
function getCoffeeScriptToken(editor, cur) {
// This getToken, it is for coffeescript, imitates the behavior of
// getTokenAt method in javascript.js, that is, returning "property"
// type and treat "." as indepenent token.
var token = editor.getTokenAt(cur);
if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
token.end = token.start;
token.string = '.';
token.type = "property";
}
else if (/^\.[\w$_]*$/.test(token.string)) {
token.type = "property";
token.start++;
token.string = token.string.replace(/\./, '');
}
return token;
}
function coffeescriptHint(editor, options) {
return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
}
CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint);
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
"toUpperCase toLowerCase split concat match replace search").split(" ");
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
var funcProps = "prototype apply call bind".split(" ");
var javascriptKeywords = ("break case catch class const continue debugger default delete do else export extends false finally for function " +
"if in import instanceof new null return super switch this throw true try typeof var void while with yield").split(" ");
var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
function forAllProps(obj, callback) {
if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
for (var name in obj) callback(name)
} else {
for (var o = obj; o; o = Object.getPrototypeOf(o))
Object.getOwnPropertyNames(o).forEach(callback)
}
}
function getCompletions(token, context, keywords, options) {
var found = [], start = token.string, global = options && options.globalScope || window;
function maybeAdd(str) {
var lstart=start.toLowerCase();
var lstr=str.toLowerCase();
if (lstr.lastIndexOf(lstart, 0) == 0 && !arrayContains(found, lstr)) found.push(str);
}
function gatherCompletions(obj) {
if (typeof obj == "string") forEach(stringProps, maybeAdd);
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
forAllProps(obj, maybeAdd)
}
if (context && context.length) {
// If this is a property, see if it belongs to some object we can
// find in the current environment.
var obj = context.pop(), base;
if (obj.type && obj.type.indexOf("variable") === 0) {
if (options && options.additionalContext)
base = options.additionalContext[obj.string];
if (!options || options.useGlobalScope !== false)
base = base || global[obj.string];
} else if (obj.type == "string") {
base = "";
} else if (obj.type == "atom") {
base = 1;
} else if (obj.type == "function") {
if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
(typeof global.jQuery == 'function'))
base = global.jQuery();
else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
base = global._();
}
while (base != null && context.length)
base = base[context.pop().string];
if (base != null) gatherCompletions(base);
} else {
// If not, just look in the global object and any local scope
// (reading into JS mode internals to get at the local and global variables)
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
for (var c = token.state.context; c; c = c.prev)
for (var v = c.vars; v; v = v.next) maybeAdd(v.name)
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
if (!options || options.useGlobalScope !== false)
gatherCompletions(global);
forEach(keywords, maybeAdd);
}
return found;
}
\ No newline at end of file
...@@ -3,6 +3,14 @@ ...@@ -3,6 +3,14 @@
export default { export default {
node: { node: {
groupName: '基础', groupName: '基础',
visible: {
title: '是否可见',
type: 'switch',
props: {
width: 40
},
value: true
},
x: { x: {
title: 'x坐标', title: 'x坐标',
type: 'inputNumber', type: 'inputNumber',
...@@ -47,13 +55,13 @@ export default { ...@@ -47,13 +55,13 @@ export default {
title: '水平偏移', title: '水平偏移',
type: 'inputNumber', type: 'inputNumber',
value: undefined, value: undefined,
desc: '相对于父元素中心点的水平偏移,0为正中心' //desc: '相对于父元素中心点的水平偏移,0为正中心'
}, },
verticalCenter: { verticalCenter: {
title: '垂直偏移', title: '垂直偏移',
type: 'inputNumber', type: 'inputNumber',
value: undefined, value: undefined,
desc: '相对于父元素中心点的垂直偏移,0为正中心' //desc: '相对于父元素中心点的垂直偏移,0为正中心'
}, },
rotation: { rotation: {
title: '旋转', title: '旋转',
...@@ -117,36 +125,57 @@ export default { ...@@ -117,36 +125,57 @@ export default {
}, },
value: true value: true
}, },
visible: {
title: '是否可见',
type: 'switch',
props: {
width: 40
},
value: true
}
}, },
label: { label: {
base: 'node',
groupName: '文本', groupName: '文本',
text: { text: {
title: '文本内容', title: '文本内容',
type: 'textArea',
value: ''
},
htmlText: {
title: 'HTML内容',
type: 'input', type: 'input',
value: '' value: ''
}, },
bold: {
title: '粗体',
type: 'switch',
props: {
width: 40
},
value: false,
},
fillColor: { fillColor: {
title: '颜色', title: '颜色',
type: 'colorPicker', type: 'colorPicker',
value: '#000' value: '#000'
}, },
lineType: {
title: '自动换行',
type: 'select',
options: [
{ label: '单行', value: 'single' },
{ label: '多行', value: 'multi' }
],
value: 'single'
},
size: { size: {
title: '字体大小', title: '字体大小',
type: 'swSelect', type: 'inputNumber',
/*type: 'swSelect',
props: { props: {
optionType: 'fontSize' optionType: 'fontSize'
}, },*/
value: 12 value: 12
}, },
lineHeight: {
title: '行高',
type: 'inputNumber',
value: 1
},
textAlign: { textAlign: {
title: '文本对齐', title: '文本对齐',
type: 'select', type: 'select',
...@@ -161,23 +190,129 @@ export default { ...@@ -161,23 +190,129 @@ export default {
title: '纵向对齐', title: '纵向对齐',
type: 'select', type: 'select',
options: [ options: [
{ label: '靠上', value: 'top' }, { label: '靠上', value: 'up' },
{ label: '居中', value: 'middle' }, { label: '居中', value: 'middle' },
{ label: '靠下', value: 'bottom' } { label: '靠下', value: 'down' }
], ],
value: 'top' value: 'top'
}, },
}, },
bitmapText: {
base: 'node',
groupName: '图片文本',
text: {
title: '文本内容',
type: 'input',
value: ''
},
font: {
title: '字体名',
type: 'input',
value: ''
},
letterSpacing: {
title: '字间距',
type: 'inputNumber',
value: 0
},
verticalAlign: {
title: '纵向对齐',
type: 'select',
options: [
{ label: '靠上', value: 'up' },
{ label: '居中', value: 'middle' },
{ label: '靠下', value: 'down' }
],
value: 'down'
},
},
textinput: {
base: 'label',
groupName: '输入框',
type: {
title: '输入类型',
type: 'select',
options: [
{ label: '文本', value: 'text' },
{ label: '密码', value: 'password' },
],
value: 'text'
},
pattern: {
title: '输入模式',
type: 'input',
value: ''
},
maxLength: {
title: '最大长度',
type: 'inputNumber',
value: undefined,
},
placeholder: {
title: '提示文字',
type: 'input',
value: ''
},
placeholderColor: {
title: '提示颜色',
type: 'colorPicker',
value: '#666666'
},
},
image: { image: {
groupName: '来源', base: 'node',
groupName: '图片',
source: { source: {
title: '来源', title: '来源',
type: 'source', type: 'source',
value: '' value: ''
},
imageWidth: {
title: '原图宽度',
type: 'input',
value: '',
desc: '原图片的宽度',
props: {
disabled: true
}
},
imageHeight: {
title: '原图高度',
type: 'input',
value: '',
desc: '原图片的高度',
props: {
disabled: true
}
} }
}, },
rect: { svga: {
groupName: '矩形', base: 'node',
groupName: 'SVGA',
source: {
title: '来源',
type: 'source',
value: ''
},
lockStep: {
title: '锁步',
type: 'switch',
props: {
width: 40
},
value: false
},
autoPlay: {
title: '自动播放',
type: 'switch',
props: {
width: 40
},
value: false
},
},
shape: {
base: 'node',
fillColor: { fillColor: {
title: '填充色', title: '填充色',
type: 'colorPicker', type: 'colorPicker',
...@@ -195,7 +330,11 @@ export default { ...@@ -195,7 +330,11 @@ export default {
props: { props: {
min: 0 min: 0
} }
}, }
},
rect: {
base: 'shape',
groupName: '矩形',
borderRadius: { borderRadius: {
title: '圆角', title: '圆角',
type: 'inputNumber', type: 'inputNumber',
...@@ -206,35 +345,20 @@ export default { ...@@ -206,35 +345,20 @@ export default {
} }
}, },
circle: { circle: {
base: 'shape',
groupName: '圆形', groupName: '圆形',
fillColor: {
title: '填充色',
type: 'colorPicker',
value: '#fff'
},
strokeColor: {
title: '边框颜色',
type: 'colorPicker',
value: '#000'
},
strokeWidth: {
title: '边框宽度',
type: 'inputNumber',
value: 0,
props: {
min: 0
}
}
}, },
scrollView: { scrollView: {
base: 'node',
groupName: '滚动视图', groupName: '滚动视图',
isVertical: { direction: {
title: '纵向锁定', title: '滚动方向',
type: 'switch', type: 'select',
props: { options: [
width: 40 { label: '纵向', value: 'vertical' },
}, { label: '横向', value: 'horizontal' }
value: true, ],
value: 'vertical',
}, },
isSpringBack: { isSpringBack: {
title: '回弹效果', title: '回弹效果',
...@@ -244,11 +368,6 @@ export default { ...@@ -244,11 +368,6 @@ export default {
}, },
value: true, value: true,
}, },
/*maxDistance: {
title: '最大距离',
type: 'inputNumber',
value: 1040,
},*/
maxSpeed: { maxSpeed: {
title: '最大速度', title: '最大速度',
type: 'inputNumber', type: 'inputNumber',
...@@ -259,5 +378,47 @@ export default { ...@@ -259,5 +378,47 @@ export default {
type: 'inputNumber', type: 'inputNumber',
value: 20, value: 20,
}, },
} },
scrollList: {
base: 'scrollView',
groupName: '滚动列表',
itemWidth: {
title: '单项宽度',
type: 'inputNumber',
value: 0,
},
itemHeight: {
title: '单项高度',
type: 'inputNumber',
value: 0,
},
itemCol: {
title: '列数',
type: 'inputNumber',
value: 1,
},
cloneEvents: {
title: '克隆事件',
type: 'switch',
props:{
width: 40,
},
value: true,
},
cloneScripts: {
title: '克隆脚本',
type: 'switch',
props:{
width: 40,
},
value: true,
},
},
/*htmlView: {
groupName: 'HTML视图',
htmlElement: {
title: 'HTML内容',
type: 'input',
},
}*/
} }
/**
* Created by rockyl on 2019-12-08.
*/
import MaxRectsBinPack from "./MaxRectsBinPack";
import {clonePureObj} from "./index";
const packExts = ['.png']; //, '.jpg', '.jpeg', '.bmp'
let canvas = document.createElement('canvas');
export async function packImages(asssts, options = {}) {
const padding = options.padding || 1;
const maxSize = options.maxSize || 2048;
const mode = options.mode || 0;
const images = await preProcessing(asssts);
let rects = [], singles = [];
for (let item of images) {
const {image, assets, image: {width, height}} = item;
if (width < maxSize && height < maxSize) {
const rectWidth = width + padding * 2;
const rectHeight = height + padding * 2;
rects.push({
image,
assets,
width: rectWidth,
height: rectHeight,
area: rectWidth * rectHeight,
sourceW: width,
sourceH: height,
offX: 0,
offY: 0,
})
} else {
asssts.push({
name: assets[0].name,
ext: assets[0].ext,
url: assets[0].url,
uuids: assets.map(asset => asset.uuid),
})
}
}
rects.sort((a, b) => {
return b.area - a.area;
});
let remainRects = rects.concat();
let index = 0;
while (remainRects.length > 0) {
let name = 'sheet' + index;
let pack = new MaxRectsBinPack(maxSize, maxSize, false);
let packedRects = pack.insertRects(remainRects, mode);
//document.body.append(canvas);
canvas.width = canvas.height = maxSize;
let context = canvas.getContext('2d');
//context.fillStyle = `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 1)`;
//context.strokeRect(0, 0, maxSize, maxSize);
for (let rect of packedRects) {
context.drawImage(rect.image, rect.x + padding, rect.y + padding);
}
let blob = await new Promise(resolve => {
canvas.toBlob(function (blob) {
resolve(blob);
}, 'image/png');
});
let frames = {};
let i = 0;
for (let rect of packedRects) {
let sprite = {
x: rect.x + padding,
y: rect.y + padding,
w: rect.width - padding * 2,
h: rect.height - padding * 2,
ox: rect.offX,
oy: rect.offY,
sw: rect.sourceW,
sh: rect.sourceH,
};
for (let asset of rect.assets) {
frames[asset.uuid] = Object.assign({}, sprite, {name: asset.name});
i++;
}
}
asssts.push({
ext: '.sht',
file: new File([blob], name + '.png'),
frames,
});
index++;
}
}
function loadImage(url, assets) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function () {
resolve({
image: img,
assets,
})
};
img.onerror = reject;
img.src = url;
})
}
async function preProcessing(assets) {
let targetAssets = [];
for (let i = 0, li = assets.length; i < li; i++) {
const asset = assets[i];
if (packExts.includes(asset.ext)) {
targetAssets.push(assets.splice(i, 1)[0]);
i--;
li--;
}
}
let groups = {};
for (let asset of targetAssets) {
let group = groups[asset.url];
if (!group) {
group = groups[asset.url] = [];
}
group.push(asset);
}
let ps = [];
for (let url in groups) {
ps.push(loadImage(url, groups[url]))
}
return await Promise.all(ps);
}
...@@ -7,17 +7,20 @@ ...@@ -7,17 +7,20 @@
<views splitpanes-min="20" :splitpanes-size="getSize(2, 0)"></views> <views splitpanes-min="20" :splitpanes-size="getSize(2, 0)"></views>
<playground splitpanes-min="20" :splitpanes-size="getSize(2, 1)"></playground> <playground splitpanes-min="20" :splitpanes-size="getSize(2, 1)"></playground>
</split-panes> </split-panes>
<assets splitpanes-min="20" :splitpanes-size="getSize(1, 1)"></assets> <assets splitpanes-min="25" :splitpanes-size="getSize(1, 1)"></assets>
</split-panes> </split-panes>
<inspector splitpanes-min="20" :splitpanes-size="getSize(0, 1)"></inspector> <inspector splitpanes-min="20" :splitpanes-size="getSize(0, 1)"></inspector>
</split-panes> </split-panes>
<bottom-bar/>
<details-dialog ref="dialogsDialog"/> <details-dialog ref="dialogsDialog"/>
<pack-result-dialog ref="packResultDialog"/> <pack-result-dialog ref="packResultDialog"/>
<mock-editor-dialog ref="mockEditorDialog"/>
<code-sync-serve-dialog ref="codeSyncServeDialog"/>
</div> </div>
</template> </template>
<script> <script>
import {mapGetters, mapActions} from 'vuex' import {mapState, mapActions} from 'vuex'
import SplitPanes from 'splitpanes' import SplitPanes from 'splitpanes'
import ToolBar from "./Editor/ToolBar"; import ToolBar from "./Editor/ToolBar";
import Inspector from "./Editor/Inspector"; import Inspector from "./Editor/Inspector";
...@@ -25,13 +28,23 @@ ...@@ -25,13 +28,23 @@
import Playground from "./Editor/Playground"; import Playground from "./Editor/Playground";
import Assets from "./Editor/Assets"; import Assets from "./Editor/Assets";
import DetailsDialog from "./Editor/dialogs/DetailsDialog"; import DetailsDialog from "./Editor/dialogs/DetailsDialog";
import {playWaiting} from "../utils"; import {openPreview, playWaiting} from "../utils";
import i18n from "../i18n"; import i18n from "../i18n";
import PackResultDialog from "./Editor/dialogs/PackResultDialog"; import PackResultDialog from "./Editor/dialogs/PackResultDialog";
import events from "@/global-events.js"
import db from "../utils/db-storage";
import MockEditorDialog from "./Editor/dialogs/MockEditorDialog";
import {startCodeSyncServe} from "../code-sync-serve";
import BottomBar from "./Editor/BottomBar";
import CodeSyncServeDialog from "./Editor/dialogs/CodeSyncServeDialog";
export default { export default {
name: 'Editor', name: 'Editor',
components: { components: {
CodeSyncServeDialog,
BottomBar,
MockEditorDialog,
PackResultDialog, PackResultDialog,
DetailsDialog, DetailsDialog,
Assets, Assets,
...@@ -50,7 +63,7 @@ ...@@ -50,7 +63,7 @@
}, },
watch: { watch: {
$route: { $route: {
handler: function(val, oldVal){ handler: function (val, oldVal) {
console.log('router changed'); console.log('router changed');
this.loadProject(); this.loadProject();
}, },
...@@ -58,26 +71,43 @@ ...@@ -58,26 +71,43 @@
} }
}, },
computed: { computed: {
...mapGetters([]), ...mapState({
codeSyncServeConfig(state) {
return state.env.codeSyncServeConfig;
},
}),
}, },
async mounted() { async mounted() {
document.addEventListener('keydown', this.onKeyPress); document.addEventListener('keydown', this.onKeyPress);
events.$on('save-and-preview', () => {
this.clickMenu("preview");
});
events.$on('show-code-sync-serve-dialog', () => {
this.$refs.codeSyncServeDialog.show();
});
await playWaiting(this.prepare(), this.$t('Preparing')).catch(e => {}); await playWaiting(this.prepare(), this.$t('Preparing')).catch(e => {
});
setTimeout(() => {
startCodeSyncServe(this.codeSyncServeConfig);
}, 100);
this.loadProject(); this.loadProject();
}, },
destroyed(){ destroyed() {
document.removeEventListener('keydown', this.onKeyPress) document.removeEventListener('keydown', this.onKeyPress)
}, },
methods: { methods: {
prepare(){ prepare() {
return Promise.all([ return Promise.all([
this.updateEnv(), this.updateEnv(),
db.open('store'),
]) ])
}, },
onKeyPress(e){ onKeyPress(e) {
if(e.key === 's' && (e.ctrlKey||e.metaKey)){ if (e.key === 's' && (e.ctrlKey || e.metaKey)) {
e.preventDefault(); e.preventDefault();
this.saveProject(); this.saveProject();
...@@ -85,8 +115,8 @@ ...@@ -85,8 +115,8 @@
return false; return false;
} }
}, },
async loadProject(){ async loadProject() {
const {projectID} = this.$route.params; const {projectID, project, dataUrl} = this.$route.params;
if (await this.localVersionExist(projectID)) { if (await this.localVersionExist(projectID)) {
this.$confirm(this.$t('Unsaved version found locally'), this.$t('Alert'), { this.$confirm(this.$t('Unsaved version found locally'), this.$t('Alert'), {
showClose: false, showClose: false,
...@@ -94,34 +124,35 @@ ...@@ -94,34 +124,35 @@
closeOnPressEscape: false, closeOnPressEscape: false,
confirmButtonText: this.$t('Local Version'), confirmButtonText: this.$t('Local Version'),
cancelButtonText: this.$t('Remote Version'), cancelButtonText: this.$t('Remote Version'),
type: 'warning' type: 'warning',
}).then(() => { }).then(() => {
this.loadLocalVersion(projectID); this.loadLocalVersion(projectID);
}).catch((e) => { }).catch((e) => {
if (e === 'cancel') { if (e === 'cancel') {
this.loadRemoteVersion(projectID); this.loadRemoteVersion(projectID, project, dataUrl);
} else { } else {
console.log(e); console.log(e);
} }
}); });
} else { } else {
this.loadRemoteVersion(projectID); this.loadRemoteVersion(projectID, project, dataUrl);
} }
}, },
loadLocalVersion(projectID) { loadLocalVersion(projectID) {
this.ready = false; this.ready = false;
this.loadFromLocal(projectID); this.loadFromLocal(projectID);
this.$nextTick(()=>{ this.$nextTick(() => {
this.ready = true; this.ready = true;
}); });
}, },
async loadRemoteVersion(projectID) { async loadRemoteVersion(projectID, project, dataUrl) {
if (projectID) { if (projectID) {
this.ready = false; this.ready = false;
await playWaiting(this.loadFromRemote(projectID), this.$t('Preparing')).catch(e => { let p = dataUrl ? this.loadFromDataUrl({project, dataUrl}) : this.loadFromRemote(projectID);
await playWaiting(p, this.$t('Preparing')).catch(e => {
this.$alert(this.$t('Project does not exist'), this.$t('Alert'), { this.$alert(this.$t('Project does not exist'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'), confirmButtonText: this.$t('Confirm'),
callback: action=>{ callback: action => {
this.$router.replace({name: 'home'}); this.$router.replace({name: 'home'});
} }
}); });
...@@ -139,12 +170,28 @@ ...@@ -139,12 +170,28 @@
this.panesConfig[id] = configs[0].width / 100; this.panesConfig[id] = configs[0].width / 100;
localStorage.panesConfig = JSON.stringify(this.panesConfig); localStorage.panesConfig = JSON.stringify(this.panesConfig);
}, },
async saveProject() { async saveProject(closeLoading) {
await playWaiting(this.saveToRemote(), this.$t('Saving')); let remark, cancel;
this.$message({ await this.$prompt(this.$t('Input version remark'), this.$t('Alert'), {
message: i18n.t('Save project successfully'), //因为message是异步出现,但是当路由回退的时候,this.i18n的实例已经置空,所以要用全局的i18n实例 confirmButtonText: this.$t('Confirm'),
type: 'success' showClose: false,
inputPattern: /^.{0,256}$/,
}).then(
({value}) => {
remark = value;
}
).catch((action) => {
cancel = true;
}); });
if (!cancel) {
await playWaiting(this.saveToRemote({remark}), this.$t('Saving'), closeLoading);
this.$message({
message: i18n.t('Save project successfully'), //因为message是异步出现,但是当路由回退的时候,this.i18n的实例已经置空,所以要用全局的i18n实例
type: 'success'
});
}
return cancel;
}, },
async clickMenu(menuItem) { async clickMenu(menuItem) {
switch (menuItem) { switch (menuItem) {
...@@ -157,8 +204,18 @@ ...@@ -157,8 +204,18 @@
case 'details': case 'details':
this.$refs.dialogsDialog.show(); this.$refs.dialogsDialog.show();
break; break;
case 'pack': case 'preview':
await this.pack(); this.preview();
await this.pack(true);
break;
case 'preview-fast':
this.preview();
break;
case 'publish':
await this.publish();
break;
case 'mock':
this.$refs.mockEditorDialog.show();
break; break;
case 'undo': case 'undo':
this.$store.commit('undoRedo', 1); this.$store.commit('undoRedo', 1);
...@@ -173,9 +230,9 @@ ...@@ -173,9 +230,9 @@
confirmButtonText: this.$t('Save'), confirmButtonText: this.$t('Save'),
cancelButtonText: this.$t('Exit'), cancelButtonText: this.$t('Exit'),
type: 'warning' type: 'warning'
}).then(() => { }).then(async () => {
try { try {
this.saveProject(); await this.saveProject();
this.backToHome(); this.backToHome();
} catch (e) { } catch (e) {
} }
...@@ -188,20 +245,33 @@ ...@@ -188,20 +245,33 @@
break; break;
} }
}, },
async pack(){ async publish() {
this.pack();
},
async pack(debug = false) {
let cancel;
if (!debug) {
cancel = await this.saveProject(false);
}
if(cancel){
return;
}
const loading = this.$loading({ const loading = this.$loading({
lock: true, lock: true,
text: this.$t('Packing'), text: this.$t('Packing'),
}); });
try{ try {
await this.saveProject(); const packResult = await this.packProject(debug);
const packResult = await this.packProject();
this.$message({ this.$message({
message: this.$t('Pack project successfully'), message: this.$t('Pack project successfully'),
type: 'success', type: 'success',
duration: 1000, duration: 500,
}); });
this.$refs.packResultDialog.show(packResult); if (debug) {
openPreview(packResult);
} else {
this.$refs.packResultDialog.show(packResult);
}
/*this.$confirm(this.$t('Pack project successfully'), this.$t('Alert'), { /*this.$confirm(this.$t('Pack project successfully'), this.$t('Alert'), {
confirmButtonText: this.$t('Open in new tab'), confirmButtonText: this.$t('Open in new tab'),
cancelButtonText: this.$t('Close'), cancelButtonText: this.$t('Close'),
...@@ -213,7 +283,8 @@ ...@@ -213,7 +283,8 @@
}).catch(() => { }).catch(() => {
});*/ });*/
}catch (e) { } catch (e) {
console.log(e);
this.$message({ this.$message({
message: this.$t('Pack project failed'), message: this.$t('Pack project failed'),
type: 'error', type: 'error',
...@@ -225,14 +296,29 @@ ...@@ -225,14 +296,29 @@
backToHome() { backToHome() {
this.$router.replace({name: 'home'}); this.$router.replace({name: 'home'});
}, },
preview() {
this.savePreview();
const {projectID} = this.$route.params;
let previewUrl = new URL(location.href);
previewUrl.hash = '#/preview/' + projectID;
setTimeout(() => {
window.open(previewUrl.href, 'blank');
}, 300);
},
...mapActions([ ...mapActions([
'localVersionExist', 'localVersionExist',
'loadFromLocal', 'loadFromLocal',
'loadFromRemote', 'loadFromRemote',
'loadFromDataUrl',
"saveToLocal", "saveToLocal",
"saveToRemote", "saveToRemote",
'updateEnv', 'updateEnv',
'packProject', 'packProject',
'savePreview',
'packImages',
]) ])
} }
} }
......
...@@ -22,13 +22,11 @@ ...@@ -22,13 +22,11 @@
</div> </div>
<el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden" <el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view"> view-class="scrollbar-view">
<div class="file-list"> <asset-list editable @show-file-details="showFileDetails" @click-item="onItemClick">
<div class="file-uploader" @click="toUploadFile"> <div slot="first" class="file-uploader" @click="toUploadFile" @dragover.prevent @drop="onDropFile">
<i class="el-icon-plus file-uploader-icon"></i> <i class="el-icon-plus file-uploader-icon"></i>
</div> </div>
<file-item v-for="(asset, index) in assets" :data="asset" :key="index" @show-file-details="showFileDetails" </asset-list>
@click="onItemClick(asset)"/>
</div>
</el-scrollbar> </el-scrollbar>
</div> </div>
</div> </div>
...@@ -37,16 +35,17 @@ ...@@ -37,16 +35,17 @@
</template> </template>
<script> <script>
import {mapState, mapMutations, mapActions} from 'vuex' import {mapGetters, mapMutations, mapActions} from 'vuex'
import Pane from "../../components/Pane"; import Pane from "../../components/Pane";
import FileItem from "./Assets/FileItem"; import FileItem from "./Assets/FileItem";
import AssetsShow from "./Assets/AssetsShow"; import AssetsShow from "./Assets/AssetsShow";
import SplitPanes from 'splitpanes' import SplitPanes from 'splitpanes'
import {selectFile} from "../../utils"; import {scanFiles, selectFile} from "../../utils";
import AssetList from "./Assets/AssetList";
export default { export default {
name: "Assets", name: "Assets",
components: {AssetsShow, FileItem, Pane, SplitPanes}, components: {AssetList, AssetsShow, FileItem, Pane, SplitPanes},
data() { data() {
return { return {
showFields: ['url', 'uuid'], showFields: ['url', 'uuid'],
...@@ -57,9 +56,7 @@ ...@@ -57,9 +56,7 @@
} }
}, },
computed: { computed: {
...mapState({ ...mapGetters(['assets']),
assets: state => state.project.data.assets
}),
}, },
mounted() { mounted() {
this.currentItem = null; this.currentItem = null;
...@@ -70,8 +67,25 @@ ...@@ -70,8 +67,25 @@
this.uploadFiles(files); this.uploadFiles(files);
}, {multiple: true}) }, {multiple: true})
}, },
showFileDetails(file) { async onDropFile(e) {
this.$refs.assetsShow.show(file); const items = e.dataTransfer.items;
let allFiles = [];
let allEntries = [];
for (let item of items) {
await scanFiles(item.webkitGetAsEntry(), allEntries);
}
for (let entry of allEntries) {
await new Promise(resolve => {
entry.file(function (file) {
allFiles.push(file);
resolve();
});
})
}
this.uploadFiles(allFiles);
},
showFileDetails(asset) {
this.$refs.assetsShow.show(asset);
}, },
onItemClick(asset) { onItemClick(asset) {
this.currentItem = asset; this.currentItem = asset;
......
<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)"/>
</div>
</template>
<script>
import {mapGetters} from 'vuex';
import FileItem from "./FileItem";
export default {
name: "AssetList",
components: {FileItem},
props: {
editable:{
type: Boolean,
default: false,
}
},
computed: {
...mapGetters([
'assets',
])
},
methods: {
showFileDetails(asset) {
this.$emit('show-file-details', asset);
},
onClickItem(asset) {
this.$emit('click-item', asset);
},
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template> <template>
<div class="file-item" @click="$emit('click', $event)"> <div class="file-item" @click="$emit('click', $event)">
<div class="icon"> <div class="icon">
<i v-if="!showThumbnail" draggable="true" class="file-icon" :class="fileIcon"></i> <i @dragstart="assetDragStart(data)" v-if="!showThumbnail" draggable="true" class="file-icon"
<img @dragstart="assetDragStart(data)" v-if="showThumbnail" draggable="true" class="thumbnail" :src="thumbnailUrl" alt="thumb" @dblclick="onDbclick()"> :class="fileIcon"></i>
<div class="operate-bar"> <img @dragstart="assetDragStart(data)" v-if="showThumbnail" draggable="true"
class="thumbnail alpha-image-background" :src="thumbnailUrl"
alt="thumb" @dblclick="onDbclick()">
<div v-if="editable" class="operate-bar">
<el-button circle size="mini" type="success" icon="icon-upload" @dblclick.native.stop
@click="onClickReplace"/>
<el-button circle size="mini" type="success" icon="el-icon-edit" @dblclick.native.stop @click="onClickEdit"/> <el-button circle size="mini" type="success" icon="el-icon-edit" @dblclick.native.stop @click="onClickEdit"/>
<el-button circle size="mini" type="danger" icon="el-icon-delete" @dblclick.native.stop <el-button circle size="mini" type="danger" icon="el-icon-delete" @dblclick.native.stop
@click="onClickDelete"/> @click="onClickDelete"/>
...@@ -17,21 +22,20 @@ ...@@ -17,21 +22,20 @@
</template> </template>
<script> <script>
import {mapMutations} from "vuex"; import {mapMutations, mapActions} from "vuex";
import {fileTypeIcon} from "../../../config"; import {fileTypeIcon} from "../../../config";
import {strEllipsis} from "../../../utils"; import {selectFile, strEllipsis} from "../../../utils";
export default { export default {
name: "FileItem", name: "FileItem",
props: ['data'], props: {
data() { data: {type: Object},
return { editable: {type: Boolean, default: false},
}
}, },
watch: { data() {
return {}
}, },
watch: {},
computed: { computed: {
fileIcon() { fileIcon() {
let icon = fileTypeIcon[this.data.ext]; let icon = fileTypeIcon[this.data.ext];
...@@ -43,7 +47,7 @@ ...@@ -43,7 +47,7 @@
thumbnailUrl() { thumbnailUrl() {
return this.data.url; return this.data.url;
}, },
ellipsisName(){ ellipsisName() {
return strEllipsis(this.data.name, 14, 5); return strEllipsis(this.data.name, 14, 5);
}, },
}, },
...@@ -78,11 +82,23 @@ ...@@ -78,11 +82,23 @@
}).catch(() => { }).catch(() => {
}); });
}, },
onClickReplace() {
selectFile((files) => {
let file = files[0];
this.replaceAsset({
file: file,
uuid: this.data.uuid,
});
})
},
...mapMutations([ ...mapMutations([
'deleteAsset', 'deleteAsset',
'modifyAsset', 'modifyAsset',
'assetDragStart' 'assetDragStart',
]), ]),
...mapActions([
'replaceAsset'
])
} }
} }
</script> </script>
......
<template>
<div class="bottom-bar">
<div class="editor-status">Ready</div>
<code-sync-indicator/>
<upload-indicator/>
</div>
</template>
<script>
import UploadIndicator from "./BottomBar/UploadIndicator";
import CodeSyncIndicator from "./BottomBar/CodeSyncIndicator";
export default {
name: "BottomBar",
components: {CodeSyncIndicator, UploadIndicator},
}
</script>
<style scoped>
</style>
<template>
<div class="code-sync-indicator">
<div class="indicator" :class="{enabled: enabled}" @click="onClick"></div>
</div>
</template>
<script>
import events from "../../../global-events";
import {codeSyncServeEnabled} from "../../../code-sync-serve";
export default {
name: "CodeSyncIndicator",
data() {
return {
enabled: codeSyncServeEnabled,
}
},
mounted() {
events.$on('code-sync-start', this.onCodeSyncStart);
events.$on('code-sync-stop', this.onCodeSyncStop);
},
destroyed() {
events.$off('code-sync-start', this.onCodeSyncStart);
events.$off('code-sync-stop', this.onCodeSyncStop);
},
methods: {
onCodeSyncStart() {
this.enabled = true;
},
onCodeSyncStop() {
this.enabled = false;
},
onClick() {
events.$emit('show-code-sync-serve-dialog');
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
<el-tab-pane :label="$t('Props')" name="properties"> <el-tab-pane :label="$t('Props')" name="properties">
<props-tab/> <props-tab/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('Script')" name="script">
<scripts-tab/>
</el-tab-pane>
<el-tab-pane :label="$t('Behavior')" name="behavior"> <el-tab-pane :label="$t('Behavior')" name="behavior">
<behavior-tab/> <behavior-tab/>
</el-tab-pane> </el-tab-pane>
...@@ -15,10 +18,11 @@ ...@@ -15,10 +18,11 @@
import Pane from '../../components/Pane'; import Pane from '../../components/Pane';
import BehaviorTab from "./Inspector/BehaviorTab"; import BehaviorTab from "./Inspector/BehaviorTab";
import PropsTab from "./Inspector/PropsTab"; import PropsTab from "./Inspector/PropsTab";
import ScriptsTab from "./Inspector/ScriptsTab";
export default { export default {
name: 'Inspector', name: 'Inspector',
components: {BehaviorTab, PropsTab, Pane}, components: {ScriptsTab, BehaviorTab, PropsTab, Pane},
data() { data() {
return { return {
tab: 'properties' tab: 'properties'
......
<template> <template>
<div class="zero-inspector-behavior-form" v-if="activeComponent.uuid"> <div class="zero-inspector-behavior" v-if="activeComponent.uuid">
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden"> <el-dropdown trigger="click" size="mini" @command="addTrigger" class="add-trigger">
<el-form ref="form" size="mini" label-width="60px"> <el-link icon="el-icon-plus">添加触发</el-link>
<div v-for="(evn, key) in eventsObj" :key="key"> <el-dropdown-menu slot="dropdown">
<el-form-item :label="$t('Event') + ':'"> <el-dropdown-item :disabled="!!activeComponent.events[key]" v-for="(item, key) in builtinEvents" :command="key"
<div>{{events[key]}}</div> :key="key">{{item}}
<div > </el-dropdown-item>
<!--<el-tooltip :content="$t('Trigger once')" placement="top"> <el-dropdown-item divided command="custom">{{$t('Custom event')}}</el-dropdown-item>
<el-switch v-model="evn.once" @change="v => handleOnceChange(key, v)"></el-switch> </el-dropdown-menu>
</el-tooltip>--> </el-dropdown>
<el-button size="mini" @click="showBehaviorEditor(evn, key)" style="margin-left: 20px;" icon="el-icon-edit"> <el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="trigger-list">
<!--<i v-if="evn.behaviors && evn.behaviors.length" class="el-icon-check el-icon&#45;&#45;right"></i>--> <div class="trigger-item" v-for="(trigger, name, index) in activeComponent.events" :key="index">
</el-button> <div class="top-bar">
<el-button icon="el-icon-delete" size="mini" v-if="evn.behaviors && evn.behaviors.length" @click="deleteBehavior(key)"> <span class="el-icon-caret-right el-tree-node__expand-icon expanded-arrow"
</el-button> :class="{expanded: !trigger.collapse}"></span>
<span class="name" @click="toggleCollapse(trigger)">
{{$t('events')[name] || name}}({{trigger.behaviors.length}})
</span>
<el-dropdown trigger="click" size="mini" @command="command=>clickTriggerMore(command, trigger, name, index)">
<el-link icon="el-icon-more" :underline="false"></el-link>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-plus" command="addBehavior">{{$t('Add behavior')}}</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" command="editTrigger" :disabled="!trigger.custom" divided>{{$t('Edit trigger')}}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-minus" command="deleteTrigger" :disabled="trigger.behaviors.length > 0">
{{$t('Delete trigger')}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-collapse-transition>
<div class="behavior-list" v-show="!trigger.collapse">
<div v-for="(behavior, index) in trigger.behaviors" class="behavior-item">
<enabled-setter :target="behavior"/>
<span v-if="!getBehavior(behavior)" class="name-field missing-behavior">{{$t('Missing behavior')}}</span>
<input v-else class="name-field name-input" type="text" v-model="getBehavior(behavior).name" @change="onBehaviorNameChange"></input>
<el-button icon="el-icon-minus" class="delete-button" type="danger" plain circle size="mini"
@click="deleteBehavior(index, trigger.behaviors)"/>
<el-button icon="el-icon-edit" class="edit-button" type="primary" plain circle size="mini"
@click="editBehavior(behavior, trigger.behaviors)"/>
</div>
</div> </div>
</el-form-item> </el-collapse-transition>
</div> </div>
</el-form>
</el-scrollbar> </el-scrollbar>
<behavior-editor-dialog :behaviors="behaviors" @change="v => handleBehaviorsChange(v)" ref="behaviorEditorDialog"></behavior-editor-dialog> <behavior-editor-dialog @change="handleBehaviorsChange" ref="behaviorEditorDialog"></behavior-editor-dialog>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'; import {mapGetters, mapActions, mapMutations} from 'vuex';
import _ from 'lodash'; import BehaviorEditorDialog from "../dialogs/BehaviorEditorDialog";
import BehaviorEditorDialog from '../dialogs/BehaviorEditorDialog'; import events from "@/global-events.js"
import EnabledSetter from "../components/EnabledSetter";
export default { export default {
name: 'BehaviorTab', name: "BehaviorTab",
data() { components: {EnabledSetter, BehaviorEditorDialog},
let eventsObj = {}; data() {
const events = this.$t('events'); const builtinEvents = this.$t('events');
Object.keys(events).forEach(event => { return {
eventsObj[event] = { builtinEvents,
once: false, }
behaviors: [] },
}; watch: {
}); 'activeComponent.uuid': function () {
return { this.updateEvents();
events, }
eventsObj, },
behaviors: [], computed: {
currentEvent: '' ...mapGetters(['activeComponent', 'componentList'])
}; },
}, methods: {
components: { BehaviorEditorDialog }, getBehavior(behavior) {
computed: { let process = this.$store.getters.getProcess(behavior);
...mapGetters(['activeComponent', 'componentList']) if (!process) {
}, console.warn('missing process:', behavior.meta);
methods: { }
showBehaviorEditor(evn, key) { return process;
this.currentEvent = key; },
this.behaviors = evn.behaviors || []; updateEvents() {
this.$refs.behaviorEditorDialog.show(this.behaviors, this.activeComponent.name + '_' + key); if (!this.activeComponent.events) {
}, this.$set(this.activeComponent, 'events', {});
/** }
* 当前选中组件发生变化时,更新eventsObj的数据 },
*/ addTrigger(command) {
updateEventsObj() { let trigger = {behaviors: []};
let _events = this.activeComponent.events || {}; if (command === 'custom') {
trigger.custom = true;
}
this.addBehavior(command, trigger.behaviors);
this.$set(this.activeComponent.events, command, trigger);
},
toggleCollapse(trigger) {
this.$set(trigger, 'collapse', !trigger.collapse);
},
clickTriggerMore(command, trigger, name, index) {
switch (command) {
case 'addBehavior':
this.addBehavior(name, trigger.behaviors);
this.$set(trigger, 'collapse', false);
break;
case 'editTrigger':
this.editTriggerName(name);
break;
case 'deleteTrigger':
this.deleteTrigger(name);
break;
}
},
addBehavior(eventName, behaviors) {
const alias = `${this.activeComponent.name}_${eventName}` + (behaviors.length > 0 ? '_' + (behaviors.length + 1) : '');
_.forIn(this.eventsObj, (value, key) => { this.addBehaviorDirect({
if (_events[key]) { alias, behaviors,
this.$set(this.eventsObj, key, _.cloneDeep(_events[key])); });
} else {
value.once = false;
value.behaviors = [];
}
});
},
/**
* 是否只执行一次
*/
handleOnceChange(key, v) {
let _event = this.eventsObj[key];
// 如果没有behavior,once没有用处,则不执行
if (_event.behaviors && _event.behaviors.length) {
let event = {};
event[key] = {
once: v,
behaviors: _event.behaviors
};
let events = this.activeComponent.events;
this.$store.dispatch('modifyActiveView', { this.makeDirty();
events: _.merge({}, events, event) },
}); editTriggerName(name) {
} this.$prompt(this.$t('Input event name'), this.$t('Rename event'), {
}, inputValue: name,
/** confirmButtonText: this.$t('Confirm'),
* 行为发生变化,同步数据 cancelButtonText: this.$t('Cancel'),
*/ closeOnClickModal: false,
handleBehaviorsChange(v) { inputPattern: /^.{1,256}$/,
if (this.currentEvent /* && v && v.length*/) { inputErrorMessage: this.$t('Invalid event name'),
let event = {}; }).then(({value}) => {
let currentEvent = this.eventsObj[this.currentEvent]; if (name !== value) {
event[this.currentEvent] = Object.assign(currentEvent /*, { behaviors: v }*/); const events = this.activeComponent.events;
let events = this.activeComponent.events; if (events.hasOwnProperty(value)) {
this.$store.dispatch('modifyActiveView', { this.$alert(this.$t('Event name exists'))
events: _.merge({}, events, event) } else if (this.builtinEvents.hasOwnProperty(value)) {
}); this.$alert(this.$t('Builtin event should add directly'))
this.updateEventsObj(); } else {
} this.$set(events, value, events[name]);
}, this.$delete(events, name);
/**
* 删除行为
*/
deleteBehavior(key) {
let _events = this.activeComponent.events || {};
delete _events[key];
this.$store.dispatch('modifyActiveView', { this.makeDirty();
events: _events }
}); }
this.updateEventsObj(); }).catch(() => {
} });
}, },
watch: { deleteTrigger(name) {
'activeComponent.uuid': function() { this.$delete(this.activeComponent.events, name);
this.updateEventsObj();
console.log('activeComponent.uuid change'); this.makeDirty();
} },
} editBehavior(behavior, behaviors) {
}; this.$refs.behaviorEditorDialog.show(behavior, behaviors);
},
handleBehaviorsChange(isPreview) {
if (isPreview) {
events.$emit('save-and-preview')
}
this.makeDirty();
},
onBehaviorNameChange(){
this.makeDirty();
},
async deleteBehavior(index, behaviors) {
let deleteMeta = false;
let close;
await this.$confirm(this.$t('Are you sure to delete it\'s process'), this.$t('Alert'), {
confirmButtonText: this.$t('Delete'),
cancelButtonText: this.$t('Not delete'),
distinguishCancelAndClose: true,
type: 'warning'
}).then(() => {
deleteMeta = true;
this.makeDirty();
}).catch(action => {
if(action === 'close'){
close = true;
}
});
if(!close){
this.deleteBehaviorDirect({
behaviors,
index,
deleteMeta,
})
}
},
...mapActions([
'modifyActiveView',
'getProcess',
'addBehaviorDirect',
'deleteBehaviorDirect',
]),
...mapMutations([
'makeDirty',
]),
}
}
</script> </script>
<style scoped> <style scoped lang="scss">
</style> </style>
\ No newline at end of file
<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="60px">
<div v-for="(evn, key) in eventsObj" :key="key">
<el-form-item :label="$t('Event') + ':'">
<div>{{events[key]}}</div>
<div >
<!--<el-tooltip :content="$t('Trigger once')" placement="top">
<el-switch v-model="evn.once" @change="v => handleOnceChange(key, v)"></el-switch>
</el-tooltip>-->
<el-button size="mini" @click="showBehaviorEditor(evn, key)" style="margin-left: 20px;" icon="el-icon-edit">
<!--<i v-if="evn.behaviors && evn.behaviors.length" class="el-icon-check el-icon&#45;&#45;right"></i>-->
</el-button>
<el-button icon="el-icon-delete" size="mini" v-if="evn.behaviors && evn.behaviors.length" @click="deleteBehavior(key)">
</el-button>
</div>
</el-form-item>
</div>
</el-form>
</el-scrollbar>
<behavior-editor-dialog :behaviors="behaviors" @change="isPreview => handleBehaviorsChange(isPreview)" ref="behaviorEditorDialog"></behavior-editor-dialog>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import _ from 'lodash';
import BehaviorEditorDialog from '../dialogs/BehaviorEditorDialog';
import events from "@/global-events.js"
export default {
name: 'BehaviorTab2',
data() {
let eventsObj = {};
const events = this.$t('events');
Object.keys(events).forEach(event => {
eventsObj[event] = {
once: false,
behaviors: []
};
});
return {
events,
eventsObj,
behaviors: [],
currentEvent: ''
};
},
components: { BehaviorEditorDialog },
computed: {
...mapGetters(['activeComponent', 'componentList'])
},
methods: {
showBehaviorEditor(evn, key) {
this.currentEvent = key;
this.behaviors = evn.behaviors || [];
this.$refs.behaviorEditorDialog.show(this.behaviors, this.activeComponent.name + '_' + key);
},
/**
* 当前选中组件发生变化时,更新eventsObj的数据
*/
updateEventsObj() {
let _events = this.activeComponent.events || {};
_.forIn(this.eventsObj, (value, key) => {
if (_events[key]) {
this.$set(this.eventsObj, key, _.cloneDeep(_events[key]));
} else {
value.once = false;
value.behaviors = [];
}
});
},
/**
* 是否只执行一次
*/
handleOnceChange(key, v) {
let _event = this.eventsObj[key];
// 如果没有behavior,once没有用处,则不执行
if (_event.behaviors && _event.behaviors.length) {
let event = {};
event[key] = {
once: v,
behaviors: _event.behaviors
};
let events = this.activeComponent.events;
this.$store.dispatch('modifyActiveView', {
events: _.merge({}, events, event)
});
}
},
/**
* 行为发生变化,同步数据
*/
handleBehaviorsChange(isPreview) {
if (this.currentEvent /* && v && v.length*/) {
let event = {};
let currentEvent = this.eventsObj[this.currentEvent];
event[this.currentEvent] = Object.assign(currentEvent /*, { behaviors: v }*/);
let events = this.activeComponent.events;
this.$store.dispatch('modifyActiveView', {
events: _.merge({}, events, event)
});
this.updateEventsObj();
}
if(isPreview){
events.$emit('saveAndPreview')
}
},
/**
* 删除行为
*/
deleteBehavior(key) {
let _events = this.activeComponent.events || {};
delete _events[key];
this.$store.dispatch('modifyActiveView', {
events: _events
});
this.updateEventsObj();
}
},
watch: {
'activeComponent.uuid': function() {
this.updateEventsObj();
console.log('activeComponent.uuid change');
}
}
};
</script>
<style scoped>
</style>
\ No newline at end of file
...@@ -2,68 +2,34 @@ ...@@ -2,68 +2,34 @@
<div class="zero-inspector-props-form" v-if="activeComponent.uuid"> <div class="zero-inspector-props-form" v-if="activeComponent.uuid">
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden"> <el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden">
<el-form ref="form" size="mini" :model="form" label-width="80px" @submit.native.prevent> <el-form ref="form" size="mini" :model="form" label-width="80px" @submit.native.prevent>
<el-collapse v-model="configColl"> <el-form-item label="名称">
<el-collapse-item title="配置" name="properties"> <el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
<div class="zero-inspector-props-group"> </el-form-item>
<el-form-item label="名称"> <el-form-item label="类型">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input> <el-select v-model="form.type" @change="v => handleChange('type', v)" placeholder="请选择类型">
</el-form-item> <el-option v-for="(cmp, key) in componentsMap" :key="key" :label="cmp" :value="key"></el-option>
<el-form-item label="类型"> </el-select>
<el-select v-model="form.type" @change="v => handleChange('type', v)" placeholder="请选择类型"> </el-form-item>
<el-option v-for="cmp in componentsMap" :key="cmp.value" :label="cmp.label" :value="cmp.value"></el-option> <template v-for="(p, key) in cmpProps">
</el-select> <el-form-item v-if="key !== 'groupName'" :id="activeComponent.uuid + '-' + key" :key="activeComponent.uuid + key" :label="p.title">
</el-form-item> <!-- {{key}} -->
<template v-for="(p, key) in cmpProps"> <el-tooltip :disabled="!p.desc" placement="top-start">
<el-form-item v-if="key !== 'groupName'" :id="activeComponent.uuid + '-' + key" :key="activeComponent.uuid + key" :label="p.title"> <div slot="content">{{p.desc}}</div>
<!-- {{key}} --> <div>
<el-tooltip :disabled="!p.desc" placement="top-start"> <dynamic-component :component-value="getPropValue(p, key)" :component-props="getPropProps(p)" :component-type="getPropCmpType(p)" @onChange="v => handlePropertiesChange(key, v)"></dynamic-component>
<div slot="content">{{p.desc}}</div> </div>
<div> </el-tooltip>
<dynamic-component :component-value="getPropValue(p, key)" :component-props="getPropProps(p)" :component-type="getPropCmpType(p)" @onChange="v => handlePropertiesChange(key, v)"></dynamic-component> </el-form-item>
</div> </template>
</el-tooltip>
</el-form-item>
</template>
</div>
</el-collapse-item>
<el-collapse-item title="脚本" name="scripts">
<el-collapse accordion v-if="activeComponent.scripts && activeComponent.scripts.length">
<template v-for="(script, index) in activeComponent.scripts">
<el-collapse-item :title="getScriptName(script.script)" :key="script + index">
<template v-for="(p, key) in getScriptOptions(script.script)">
<el-form-item :key="activeComponent.uuid + index + key" :label="p.alias|| key">
<dynamic-component :component-value="getScriptValue(p, key, index)" :component-props="getScriptProps(p, index)" :component-type="getScriptType(p, index)" @onChange="v => handleScriptChange(index, key, v)"></dynamic-component>
</el-form-item>
</template>
<el-form-item label="">
<el-button @click="deleteNodeScript(index)">删除</el-button>
</el-form-item>
</el-collapse-item>
</template>
</el-collapse>
<div style="padding-top: 15px;text-align: center;">
<el-popover
placement="top"
width="300"
v-model="scriptDialog"
trigger="manual">
<div class="script-config-dialog">
<el-tree :data="scripts" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
</div>
<el-button slot="reference" @click="scriptDialog = !scriptDialog" size="mini">add script</el-button>
</el-popover>
</div>
</el-collapse-item>
</el-collapse>
</el-form> </el-form>
</el-scrollbar> </el-scrollbar>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters, mapState } from 'vuex';
import _ from 'lodash'; import _ from 'lodash';
import { componentsMap, getCmpProps } from '../../../utils/common'; import { getCmpProps } from '../../../utils/common';
import dynamicComponent from '../components/dynamicComponent'; import dynamicComponent from '../components/dynamicComponent';
const componentMapper = { const componentMapper = {
...@@ -95,44 +61,24 @@ const componentMapper = { ...@@ -95,44 +61,24 @@ const componentMapper = {
'controls-position': 'right' 'controls-position': 'right'
} }
}, },
input: { textArea: {
component: 'el-input' component: 'el-input',
},
colorPicker: {
component: 'el-color-picker',
props: { props: {
'show-alpha': true type: 'textarea',
} autosize: {
} minRows: 3,
}; maxRows: 10
}
const scriptTypeMap = {
number: {
component: 'el-input-number',
props: {
size: 'mini'
} }
}, },
string: { input: {
component: 'el-input' component: 'el-input'
}, },
boolean: { colorPicker: {
component: 'el-switch',
props: {
width: 40
}
},
color: {
component: 'el-color-picker', component: 'el-color-picker',
props: { props: {
'show-alpha': true 'show-alpha': true
} }
},
select: {
component: 'el-select',
props: {
slotComponent: 'el-option'
}
} }
}; };
...@@ -140,20 +86,14 @@ export default { ...@@ -140,20 +86,14 @@ export default {
name: 'PropsTab', name: 'PropsTab',
components: { 'dynamic-component': dynamicComponent }, components: { 'dynamic-component': dynamicComponent },
data() { data() {
const componentsMap = this.$t('view_node_menu');
return { return {
componentsMap, componentsMap,
scriptDialog: false,
form: { form: {
name: '', name: '',
type: '', type: '',
properties: {}, properties: {},
scripts: []
}, },
defaultProps: {
children: 'children',
label: 'name'
},
configColl: ['properties']
}; };
}, },
computed: { computed: {
...@@ -162,13 +102,6 @@ export default { ...@@ -162,13 +102,6 @@ export default {
// 获取properties.js中的默认配置 // 获取properties.js中的默认配置
return getCmpProps(this.activeComponent.type); return getCmpProps(this.activeComponent.type);
}, },
scripts: function() {
console.log('scripts', this.$store.state.env.scripts);
return _.filter(this.$store.state.env.scripts, s => {
return s !== null && s !== undefined;
});
}
}, },
watch: { watch: {
activeComponent: { activeComponent: {
...@@ -186,14 +119,6 @@ export default { ...@@ -186,14 +119,6 @@ export default {
_view[label] = v; _view[label] = v;
this.$store.dispatch('modifyActiveView', _view); this.$store.dispatch('modifyActiveView', _view);
}, },
/**
* 脚本预设对象选中
*/
handleNodeClick(script) {
console.log('handleNodeClick', script);
this.$store.dispatch('addNodeScript', script.id);
this.scriptDialog = false;
},
/** /**
* 基础属性发生改变 * 基础属性发生改变
*/ */
...@@ -204,20 +129,6 @@ export default { ...@@ -204,20 +129,6 @@ export default {
_prop[key] = v; _prop[key] = v;
this.$store.dispatch('modifyProperties', _prop); this.$store.dispatch('modifyProperties', _prop);
}, },
/**
* 事件属性发生改变
*/
handleScriptChange(index, key, v) {
let _props = {};
_props[key] = v;
let _scripts = _.cloneDeep(this.activeComponent.scripts);
let _script = _scripts[index];
_script.props = _.assign(_script.props, _props);
_scripts[index] = _script;
this.$store.dispatch('modifyActiveView', {
scripts: _scripts
});
},
/** /**
* 获取动态组件的类型 * 获取动态组件的类型
*/ */
...@@ -243,61 +154,6 @@ export default { ...@@ -243,61 +154,6 @@ export default {
let _properties = this.activeComponentCopy.properties; let _properties = this.activeComponentCopy.properties;
return _properties[key] === undefined || _properties[key] === null ? item.value : _properties[key]; return _properties[key] === undefined || _properties[key] === null ? item.value : _properties[key];
}, },
getScriptValue(item, key, index) {
let _script = this.activeComponent.scripts[index];
// let result =
return _script.props[key] || item.default;
},
getScriptProps(item, index) {
let _type = item.type;
let _options = [];
if (_type === 'enum') {
// 如果脚本选项对应的类型是数组,说明是枚举类型
_options = item.enum.map(i => {
return {
label: i,
value: i
};
});
_type = 'select';
}
let _cmp = scriptTypeMap[_type];
return {
size: 'mini',
...(_cmp.props || {}),
options: _options
};
},
getScriptType(item, index) {
// 如果脚本选项对应的类型是数组,说明是枚举类型
let _type = item.type;
if (_type === 'enum') {
_type = 'select';
}
return scriptTypeMap[_type].component;
},
getScriptName(id) {
let _script = this.scripts.find(script => script.id === id);
return _script ? _script.name : '';
},
getScriptOptions(id) {
let _script = this.scripts.find(script => script.id === id);
return _script ? _script.props : {};
},
/**
* 删除节点脚本
*/
deleteNodeScript(index) {
let _scripts = _.cloneDeep(this.activeComponent.scripts);
_.remove(_scripts, (s, sindex) => {
return sindex === index;
});
this.$store.dispatch('modifyActiveView', {
scripts: _scripts
});
}
} }
}; };
</script> </script>
...@@ -306,17 +162,11 @@ export default { ...@@ -306,17 +162,11 @@ export default {
.zero-inspector-props-form { .zero-inspector-props-form {
width: 100%; width: 100%;
padding-right: 10px; padding-right: 10px;
.el-form-item--mini.el-form-item {
margin-bottom: 10px;
}
.el-divider__text { .el-divider__text {
background-color: #e9e9e9; background-color: #e9e9e9;
} }
.zero-inspector-props-group { .zero-inspector-props-group {
width: 100%; max-height: 500px;
max-height: 600px;
overflow-x: hidden;
overflow-y: auto;
} }
} }
.script-config-dialog { .script-config-dialog {
......
<template> <template>
<div class="zero-inspector-script-form" v-if="activeComponent.uuid"> <div class="zero-inspector-props-form" v-if="activeComponent.uuid">
<el-collapse v-model="configColl"> <el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden">
<el-collapse-item title="配置" name="properties"> <el-form ref="form" size="mini" :model="form" label-width="80px" @submit.native.prevent>
<el-form ref="form" size="mini" :model="form" label-width="80px" @submit.native.prevent> <el-collapse accordion v-if="activeComponent.scripts && activeComponent.scripts.length">
<el-form-item label="名称"> <template v-for="(script, index) in activeComponent.scripts">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input> <el-collapse-item :title="getScriptName(script.script)" :key="script + index">
</el-form-item> <template v-for="(p, key) in getScriptOptions(script.script)">
<el-form-item label="类型"> <el-form-item :key="activeComponent.uuid + index + key" :label="p.alias|| key">
<el-select v-model="form.type" @change="v => handleChange('type', v)" placeholder="请选择类型"> <dynamic-component :component-value="getScriptValue(p, key, index)" :component-props="getScriptProps(p, index)" :component-type="getScriptType(p, index)" @onChange="v => handleScriptChange(index, key, v)"></dynamic-component>
<el-option v-for="cmp in componentsMap" :key="cmp.value" :label="cmp.label" :value="cmp.value"></el-option> </el-form-item>
</el-select> </template>
</el-form-item> <el-form-item label="">
<template v-for="(p, key) in cmpProps"> <el-button @click="deleteNodeScript(index)">删除</el-button>
<el-form-item v-if="key !== 'groupName'" :id="activeComponent.uuid + '-' + key" :key="activeComponent.uuid + key" :label="p.title"> </el-form-item>
<!-- {{key}} --> </el-collapse-item>
<dynamic-component :label="key" :item="p" :properties="activeComponent.properties"></dynamic-component>
</el-form-item>
</template> </template>
</el-form> </el-collapse>
</el-collapse-item> <div style="padding-top: 15px;text-align: center;">
<el-collapse-item title="脚本" name="scripts"> <el-dropdown trigger="click" @command="handleAddScript" placement="top" size="small">
</el-collapse-item> <el-button size="mini">{{$t('Add')}}</el-button>
</el-collapse> <el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="(script, key) of scripts" :command="script.id" :key="key">{{script.name}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-form>
</el-scrollbar>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters, mapState } from 'vuex';
import { componentsMap, getCmpProps } from '../../../utils/common'; import _ from 'lodash';
import dynamicComponent from '../components/dynamicComponent'; import dynamicComponent from '../components/dynamicComponent';
const scriptTypeMap = {
number: {
component: 'el-input-number',
props: {
size: 'mini'
}
},
string: {
component: 'el-input'
},
boolean: {
component: 'el-switch',
props: {
width: 40
}
},
color: {
component: 'el-color-picker',
props: {
'show-alpha': true
}
},
select: {
component: 'el-select',
props: {
slotComponent: 'el-option'
}
}
};
export default { export default {
name: 'PropsTab', name: 'ScriptsTab',
components: { 'dynamic-component': dynamicComponent }, components: { 'dynamic-component': dynamicComponent },
data() { data() {
return { return {
componentsMap,
form: { form: {
name: '', scripts: []
type: '',
properties: {}
}, },
configColl: ['properties'] configColl: ['properties']
}; };
}, },
computed: { computed: {
...mapGetters(['activeComponent', 'componentList']), ...mapGetters(['activeComponent', 'activeComponentCopy', 'componentList']),
cmpProps: function() { ...mapState({
return getCmpProps(this.activeComponent.type); scripts(state){
} return state.env.scripts;
},
watch: {
activeComponent: {
deep: true,
handler: function(val) {
this.form.name = val.name || '';
this.form.type = val.type || '';
this.form.properties = val.properties || {};
} }
} }),
}, },
methods: { methods: {
handleChange(label, v) { /**
this.$store.dispatch('modifyComponent', { * 脚本预设对象选中
label: label, */
value: v handleAddScript(command) {
console.log('handleAddScript', command);
this.$store.dispatch('addNodeScript', command);
},
/**
* 事件属性发生改变
*/
handleScriptChange(index, key, v) {
let _props = {};
_props[key] = v;
let _scripts = _.cloneDeep(this.activeComponent.scripts);
let _script = _scripts[index];
_script.props = _.assign(_script.props, _props);
_scripts[index] = _script;
this.$store.dispatch('modifyActiveView', {
scripts: _scripts
});
},
getScriptValue(item, key, index) {
let _script = this.activeComponent.scripts[index];
let v = _script.props[key];
return v === undefined ? item.default : v;
},
getScriptProps(item, index) {
let _type = item.type;
let _options = [];
if (_type === 'enum') {
// 如果脚本选项对应的类型是数组,说明是枚举类型
_options = item.enum.map(i => {
return {
label: i,
value: i
};
});
_type = 'select';
}
let _cmp = scriptTypeMap[_type];
return {
size: 'mini',
...(_cmp.props || {}),
options: _options
};
},
getScriptType(item, index) {
// 如果脚本选项对应的类型是数组,说明是枚举类型
let _type = item.type;
if (_type === 'enum') {
_type = 'select';
}
return scriptTypeMap[_type].component;
},
getScriptName(id) {
let _script = this.scripts.find(script => script.id === id);
return _script ? _script.name : '';
},
getScriptOptions(id) {
let _script = this.scripts.find(script => script.id === id);
return _script ? _script.props : {};
},
/**
* 删除节点脚本
*/
deleteNodeScript(index) {
let _scripts = _.cloneDeep(this.activeComponent.scripts);
_.remove(_scripts, (s, sindex) => {
return sindex === index;
});
this.$store.dispatch('modifyActiveView', {
scripts: _scripts
}); });
} }
} }
...@@ -72,14 +165,17 @@ export default { ...@@ -72,14 +165,17 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
.zero-inspector-script-form { .zero-inspector-props-form {
width: 100%; width: 100%;
padding-right: 10px; padding-right: 10px;
.el-form-item--mini.el-form-item {
margin-bottom: 10px;
}
.el-divider__text { .el-divider__text {
background-color: #e9e9e9; background-color: #e9e9e9;
} }
.zero-inspector-props-group {
max-height: 500px;
}
}
.script-config-dialog {
height: 350px;
} }
</style> </style>
\ No newline at end of file
<template> <template>
<div class="tool-bar"> <div class="tool-bar">
<sample-menu :data="menu" @click-menu="clickMenu"/> <sample-menu :data="menu" @click-menu="clickMenu"/>
<upload-indicator/>
<div style="flex: 1"></div> <div style="flex: 1"></div>
<div class="right-part"> <div class="right-part">
<span> <span>
...@@ -15,11 +14,10 @@ ...@@ -15,11 +14,10 @@
<script> <script>
import {mapState, mapActions, mapMutations} from 'vuex' import {mapState, mapActions, mapMutations} from 'vuex'
import SampleMenu from "../../components/SampleMenu"; import SampleMenu from "../../components/SampleMenu";
import UploadIndicator from "./ToolBar/UploadIndicator";
export default { export default {
name: "ToolBar", name: "ToolBar",
components: {UploadIndicator, SampleMenu}, components: {SampleMenu},
data() { data() {
return {} return {}
}, },
...@@ -30,8 +28,9 @@ ...@@ -30,8 +28,9 @@
menu: function () { menu: function () {
const menuConfig = this.$t('menu'); const menuConfig = this.$t('menu');
let menu = {}; let menu = {};
for (let item of Object.keys(menuConfig)) { for (let key of Object.keys(menuConfig)) {
menu[item] = {text: menuConfig[item], disabled: false} let item = menuConfig[key];
menu[key] = {label: item.label, icon: item.icon, disabled: false}
} }
if (menu['undo']) { if (menu['undo']) {
menu['undo']['disabled'] = !this.project.operateStack.length || this.project.operateStack.length === this.project.stackIndex + 1 menu['undo']['disabled'] = !this.project.operateStack.length || this.project.operateStack.length === this.project.stackIndex + 1
...@@ -42,6 +41,11 @@ ...@@ -42,6 +41,11 @@
return menu; return menu;
} }
}, },
async mounted() {
//监听键盘事件
//document.addEventListener('keydown', this.onKeyPress);
},
methods: { methods: {
clickMenu(menuItem) { clickMenu(menuItem) {
this.$emit('click-menu', menuItem); this.$emit('click-menu', menuItem);
...@@ -60,6 +64,25 @@ ...@@ -60,6 +64,25 @@
}).catch(() => { }).catch(() => {
}); });
}, },
onKeyPress(e) {
if (e.key === 'z' && (e.ctrlKey || e.metaKey) && (!e.shiftKey)) {
//快捷键ctrl+z执行撤销
e.preventDefault();
//是否可执行撤销
if(!(!this.project.operateStack.length || this.project.operateStack.length === this.project.stackIndex + 1)){
this.clickMenu("undo");
}
return false;
}else if (e.key === 'z' && (e.ctrlKey || e.metaKey) && (e.shiftKey)) {
//快捷键ctrl+shift+z执行重做
e.preventDefault();
//是否可以执行重做
if(!(this.project.stackIndex === 0)){
this.clickMenu("redo");
}
return false;
}
},
...mapMutations([ ...mapMutations([
'modifyProject' 'modifyProject'
]), ]),
......
...@@ -12,18 +12,33 @@ ...@@ -12,18 +12,33 @@
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</div> </div>
<div class="filter-bar">
<el-input class="filter-input" prefix-icon="el-icon-search" v-model="filterText" size="mini" clearable/>
<el-dropdown class="more-button" trigger="click" size="mini" @command="onFilterCommand">
<el-link icon="el-icon-more" :underline="false"></el-link>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="(item, key) in nodeFilterPresets" :command="key" :key="key">{{item}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<!--<el-button size="mini" class="micro" icon="el-icon-magic-stick" circle @click="filterByBehavior"></el-button>-->
</div>
<el-scrollbar class="tree-scrollbar" wrap-class="wrap-x-hidden"> <el-scrollbar class="tree-scrollbar" wrap-class="wrap-x-hidden">
<el-tree <el-tree
ref="tree"
:data="views" :data="views"
:props="defaultProps" :props="defaultProps"
:expand-on-click-node="false" :expand-on-click-node="false"
:default-expanded-keys="expandedKeys"
draggable draggable
node-key="uuid"
highlight-current highlight-current
:default-expand-all="false" :default-expand-all="false"
@node-click="handleNodeClick"
empty-text="" empty-text=""
:allow-drag="allowDrag" :allow-drag="allowDrag"
:allow-drop="allowDrop" :allow-drop="allowDrop"
:filter-node-method="filterNodeMethod"
@node-click="handleNodeClick"
> >
<div slot-scope="{ node, data }" class="tree-node"> <div slot-scope="{ node, data }" class="tree-node">
<div class="node-name"> <div class="node-name">
...@@ -35,6 +50,9 @@ ...@@ -35,6 +50,9 @@
<el-link icon="el-icon-more" :underline="false" @click.stop/> <el-link icon="el-icon-more" :underline="false" @click.stop/>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item command="delete">{{$t('Delete')}}</el-dropdown-item> <el-dropdown-item command="delete">{{$t('Delete')}}</el-dropdown-item>
<el-dropdown-item command="copy">{{$t('Copy')}}</el-dropdown-item>
<el-dropdown-item command="paste">{{$t('Paste same level')}}</el-dropdown-item>
<el-dropdown-item command="pasteChild">{{$t('Paste child')}}</el-dropdown-item>
<el-dropdown-item command="export" divided v-if="!node.parent.parent">{{$t('Export')}} <el-dropdown-item command="export" divided v-if="!node.parent.parent">{{$t('Export')}}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item v-for="(type, key, index) in $t('view_node_menu')" <el-dropdown-item v-for="(type, key, index) in $t('view_node_menu')"
...@@ -53,7 +71,7 @@ ...@@ -53,7 +71,7 @@
</template> </template>
<script> <script>
import {mapState, mapMutations, mapActions} from 'vuex'; import {mapState, mapMutations, mapActions, mapGetters} from 'vuex';
import Pane from '../../components/Pane'; import Pane from '../../components/Pane';
import {selectFile} from "../../utils"; import {selectFile} from "../../utils";
import events from "../../global-events"; import events from "../../global-events";
...@@ -63,6 +81,7 @@ ...@@ -63,6 +81,7 @@
components: {Pane}, components: {Pane},
data() { data() {
return { return {
filterText: '',
defaultProps: { defaultProps: {
children: 'children', children: 'children',
label: 'name' label: 'name'
...@@ -70,14 +89,85 @@ ...@@ -70,14 +89,85 @@
uploadHeaders: { uploadHeaders: {
authorization: 'Bearer ' + window['zeroing_token'], authorization: 'Bearer ' + window['zeroing_token'],
}, },
expandedKeys: [],
nodeFilterPresets: this.$t('nodeFilterPresets'),
}; };
}, },
computed: { computed: {
...mapGetters(['activeComponentId']),
...mapState({ ...mapState({
views: state => state.project.data.views views: state => state.project.data.views
}) })
}, },
watch: {
activeComponentId: function (val) {
this.$refs.tree.setCheckedKeys([val]); // 设置选中节点
this.$refs.tree.setCurrentKey(val); // 设置高亮节点
this.expandedKeys = [val]; // 展开对应的节点
},
filterText(val) {
this.updateFilter();
},
},
methods: { methods: {
updateFilter() {
if (this.$refs.tree) {
this.$refs.tree.filter(this.filterText);
}
},
filterNodeMethod(filterText, data) {
let visible = false;
if (filterText.startsWith(':')) {
let remain = filterText.substr(1);
let arr = remain.split('|');
let cmd = arr[0];
let param = arr[1];
switch (cmd) {
case 'trigger':
if (data.events && Object.keys(data.events).length > 0) {
visible = true;
if (param && param.length > 0) {
let events = param.split(',');
let exists = false;
for (let event of events) {
if (data.events[event]) {
exists = true;
break;
}
}
visible = exists;
}
}
break;
case 'type':
if (param && param.length > 0) {
let types = param.split(',');
visible = types.includes(data.type);
}else{
visible = true;
}
break;
default:
visible = true;
}
} else {
visible = data.name.toLowerCase().includes(filterText.toLowerCase());
}
return visible;
},
onFilterCommand(command) {
let filterText;
switch (command) {
case 'trigger':
filterText = ':trigger';
break;
case 'type':
filterText = ':type|node';
break;
}
this.filterText = filterText;
},
allowDrag(draggingNode) { allowDrag(draggingNode) {
return draggingNode.parent.parent; return draggingNode.parent.parent;
}, },
...@@ -140,6 +230,26 @@ ...@@ -140,6 +230,26 @@
parentNode: node.parent.data parentNode: node.parent.data
}); });
break; break;
case 'copy':
this.copyNode({
node: data,
parentNode: node.parent.data
});
break;
case 'paste':
this.pasteNode({
node: data,
parentNode: node.parent.data,
pasteState: 1
});
break;
case 'pasteChild':
this.pasteNode({
node: data,
parentNode: node.parent.data,
pasteState: 2
});
break;
case 'export': case 'export':
this.exportView(data); this.exportView(data);
break; break;
...@@ -155,11 +265,23 @@ ...@@ -155,11 +265,23 @@
break; break;
} }
}, },
...mapMutations(['deleteNode', 'addNode']), ...mapMutations(['copyNode', 'pasteNode', 'deleteNode', 'addNode']),
...mapActions(['exportView', 'importView']) ...mapActions(['exportView', 'importView'])
} }
}; };
</script> </script>
<style scoped> <style scoped lang="scss">
.filter-bar {
display: flex;
align-items: center;
}
.filter-input {
flex: 1;
}
.more-button {
margin: 0 3px;
}
</style> </style>
\ No newline at end of file
...@@ -4,20 +4,23 @@ ...@@ -4,20 +4,23 @@
<split-panes splitpanes-min="10" :splitpanes-size="10" horizontal> <split-panes splitpanes-min="10" :splitpanes-size="10" horizontal>
<process-list @edit-meta="onEditMeta" @delete-meta="onDeleteMeta" :data="prefabProcessTree" <process-list @edit-meta="onEditMeta" @delete-meta="onDeleteMeta" :data="prefabProcessTree"
class="background full-size" splitpanes-min="20" class="background full-size" splitpanes-min="20"
:splitpanes-size="30"/> :splitpanes-size="20"/>
<process-list @edit-meta="onEditMeta" @delete-meta="onDeleteMeta" :data="normalProcessTree" <process-list @edit-meta="onEditMeta" @delete-meta="onDeleteMeta" :data="normalProcessTree"
class="background full-size" splitpanes-min="20" class="background full-size" splitpanes-min="20"
:splitpanes-size="70"/> :splitpanes-size="80"/>
</split-panes> </split-panes>
<div class="center full-size background" splitpanes-min="20" :splitpanes-size="70"> <div class="center full-size background" splitpanes-min="20" :splitpanes-size="70">
<edit-path :processStack="processStack" @pop="onPop"/> <div class="top-bar">
<div class="operate-bar"> <edit-path :processStack="processStack" @pop="onPop"/>
<!--<el-button-group> <el-button icon="el-icon-search" circle plain size="mini" @click="onClickSearch"></el-button>
</div>
<!--<div class="operate-bar">
<el-button-group>
<el-button size="mini" icon="el-icon-zoom-out" @click="setScale(-0.1)"/> <el-button size="mini" icon="el-icon-zoom-out" @click="setScale(-0.1)"/>
<el-button size="mini" @click="setScale(0)">1:1</el-button> <el-button size="mini" @click="setScale(0)">1:1</el-button>
<el-button size="mini" icon="el-icon-zoom-in" @click="setScale(0.1)"/> <el-button size="mini" icon="el-icon-zoom-in" @click="setScale(0.1)"/>
</el-button-group>--> </el-button-group>
</div> </div>-->
<board ref="board" @select-process-node="onSelectProcessNode" @edit-process="editProcess" @edit-meta="onEditMeta"/> <board ref="board" @select-process-node="onSelectProcessNode" @edit-process="editProcess" @edit-meta="onEditMeta"/>
</div> </div>
<div class="properties background full-size" splitpanes-min="20" :splitpanes-size="20"> <div class="properties background full-size" splitpanes-min="20" :splitpanes-size="20">
...@@ -25,6 +28,7 @@ ...@@ -25,6 +28,7 @@
</div> </div>
</split-panes> </split-panes>
<meta-editor-dialog ref="metaEditorDialog" @input="onSaveMeta"/> <meta-editor-dialog ref="metaEditorDialog" @input="onSaveMeta"/>
<meta-search-dialog ref="metaSearchDialog"/>
</div> </div>
</template> </template>
...@@ -37,10 +41,13 @@ ...@@ -37,10 +41,13 @@
import EditPath from "./Board/EditPath"; import EditPath from "./Board/EditPath";
import Process from "./Board/Process"; import Process from "./Board/Process";
import MetaEditorDialog from "./MetaEditorDialog"; import MetaEditorDialog from "./MetaEditorDialog";
import events from "@/global-events.js"
import MetaSearchDialog from "./MetaSearchDialog";
export default { export default {
name: "BehaviorEditor", name: "BehaviorEditor",
components: {MetaEditorDialog, PropertiesEditor, EditPath, ProcessList, Board, SplitPanes,}, components: {MetaSearchDialog, MetaEditorDialog, PropertiesEditor, EditPath, ProcessList, Board, SplitPanes,},
props: [], props: [],
data() { data() {
return { return {
...@@ -97,6 +104,9 @@ ...@@ -97,6 +104,9 @@
this.$refs.board.edit(process, this.resolveProcess); this.$refs.board.edit(process, this.resolveProcess);
this.$refs.properties.edit(); this.$refs.properties.edit();
}, },
onClickSearch(){
this.$refs.metaSearchDialog.show();
},
onEditMeta(meta) { onEditMeta(meta) {
this.metaInEditing = meta; this.metaInEditing = meta;
this.$refs.metaEditorDialog.edit(meta); this.$refs.metaEditorDialog.edit(meta);
...@@ -118,7 +128,7 @@ ...@@ -118,7 +128,7 @@
}); });
} }
}, },
onSaveMeta(meta) { onSaveMeta(meta,isPreview) {
let oldMetaID = this.metaInEditing.id; let oldMetaID = this.metaInEditing.id;
for (let key in meta) { for (let key in meta) {
this.metaInEditing[key] = meta[key]; this.metaInEditing[key] = meta[key];
...@@ -131,6 +141,10 @@ ...@@ -131,6 +141,10 @@
}); });
} }
this.$refs.board.updateProcessNode(meta.id); this.$refs.board.updateProcessNode(meta.id);
//是否预览
if(isPreview){
events.$emit('behaviorSave',isPreview)
}
}, },
...mapMutations([ ...mapMutations([
'updateProcesses', 'updateProcesses',
......
<template> <template>
<div class="board" @dragover="onDragOver" @drop="onDrop" v-resize="onResize"> <div class="board" @dragover="onDragOver" @drop="onDrop" @paste="onPaste" v-resize="onResize">
<div style="height: 1px;"></div>
<!--<el-scrollbar class="full-size">--> <!--<el-scrollbar class="full-size">-->
<svg ref="board" class="svg-board full-size" version="1.1" xmlns="http://www.w3.org/2000/svg" <svg ref="board" class="svg-board full-size" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" @mousedown="onBoardMouseDown"> xmlns:xlink="http://www.w3.org/1999/xlink" @mousedown="onBoardMouseDown">
...@@ -14,8 +15,9 @@ ...@@ -14,8 +15,9 @@
@click="onClickProcessNode(process, key)" @click="onClickProcessNode(process, key)"
@hover-pin="onPinHover" @hover-pin="onPinHover"
@leave-pin="onPinLeave" @leave-pin="onPinLeave"
@down-pin="onPintDown" @down-pin="onPinDown"
@delete="onProcessNodeDelete" @delete="onProcessNodeDelete"
@copy="onProcessNodeCopy"
@edit-meta="onEditMeta" @edit-meta="onEditMeta"
@dblclick="editSubProcess(process)" @dblclick="editSubProcess(process)"
@meta-modified="onProcessMetaModified" @meta-modified="onProcessMetaModified"
...@@ -25,7 +27,6 @@ ...@@ -25,7 +27,6 @@
</svg> </svg>
<!--</el-scrollbar>--> <!--</el-scrollbar>-->
<tool-tip ref="toolTip"/> <tool-tip ref="toolTip"/>
<inline-choose-dialog ref="inlineChooseDialog"/>
</div> </div>
</template> </template>
...@@ -39,13 +40,14 @@ ...@@ -39,13 +40,14 @@
import {DOCK_POINT_OFFSET} from "../../../config"; import {DOCK_POINT_OFFSET} from "../../../config";
import events from "../../../global-events"; import events from "../../../global-events";
import generateUUID from "uuid/v4"; import generateUUID from "uuid/v4";
import InlineChooseDialog from "./InlineChooseDialog"; import copy from 'copy-to-clipboard'
import {clonePureObj} from "../../../utils";
const customs = ['custom', 'divider']; const customs = ['custom', 'divider'];
export default { export default {
name: "Board", name: "Board",
components: {InlineChooseDialog, ToolTip, LinkLine, ProcessNode,}, components: {ToolTip, LinkLine, ProcessNode,},
props: [], props: [],
data() { data() {
return { return {
...@@ -113,30 +115,39 @@ ...@@ -113,30 +115,39 @@
}, },
addSubProcess(uuid, data) { addSubProcess(uuid, data) {
const process = new Process(this.process, data, this.resolveProcess); const process = new Process(this.process, data, this.resolveProcess);
this.$set(this.subProcessMap, uuid, process); if (process.meta) {
this.$set(this.subProcessMap, uuid, process);
} else {
console.log('节点丢失:', uuid);
}
},
showInlineChoose() {
return this.$confirm(this.$t('As inline'));
}, },
async addSubProcessData(processId, pos) { async addSubProcessData(processId, pos) {
let meta, isInline; let meta, isInline;
if (customs.includes(processId)) { if (customs.includes(processId)) {
try { try {
const result = await this.$refs.inlineChooseDialog.show(); await this.showInlineChoose();
isInline = result.isInline; isInline = true;
} catch (e) { } catch (e) {
return;
} }
meta = await this.addCustomProcessMeta({masterProcess: this.process, isInline, processId}); meta = await this.addCustomProcessMeta({masterProcess: this.process, isInline, processId});
}else{ } else {
meta = this.process.resolveMeta(processId); meta = this.process.resolveMeta(processId);
} }
if(meta.isPrefab){ if (meta.isPrefab) {
try { try {
const result = await this.$refs.inlineChooseDialog.show(); await this.showInlineChoose();
isInline = result.isInline; isInline = true;
} catch (e) { } catch (e) {
return;
} }
meta = await this.addProcessFromPrefab({masterProcess: this.process, isInline, meta}) meta = await this.addProcessFromPrefab({masterProcess: this.process, isInline, meta})
} }
...@@ -170,7 +181,7 @@ ...@@ -170,7 +181,7 @@
const data = await this.addSubProcessData(processId, { const data = await this.addSubProcessData(processId, {
x: e.offsetX - this.boardOffset.x - 9, x: e.offsetX - this.boardOffset.x - 9,
y: e.offsetY - this.boardOffset.y, y: e.offsetY - this.boardOffset.y - 20,
}); });
if (!data) { if (!data) {
return; return;
...@@ -181,6 +192,30 @@ ...@@ -181,6 +192,30 @@
events.$emit('update-dock-pin-pos'); events.$emit('update-dock-pin-pos');
}); });
}, },
onPaste(e) {
let dataStr = e.clipboardData.getData("process-data");
if (dataStr) {
this.addProcessFromCopy(dataStr);
}
},
addProcessFromCopy(avatar) {
if (typeof avatar === 'string') {
avatar = JSON.parse(avatar);
}
avatar.uuid = generateUUID();
avatar.design.x += 20;
avatar.design.y += 20;
avatar.output = {};
delete avatar.design['output'];
delete avatar.design['input'];
this.$set(this.process.meta.sub, avatar.uuid, avatar);
this.addSubProcess(avatar.uuid, avatar);
this.$nextTick(() => {
events.$emit('update-dock-pin-pos');
});
},
onResize() { onResize() {
const {x, y} = this.$el.getBoundingClientRect(); const {x, y} = this.$el.getBoundingClientRect();
this.drawState.boardOffset.x = x + this.boardOffset.x; this.drawState.boardOffset.x = x + this.boardOffset.x;
...@@ -220,6 +255,17 @@ ...@@ -220,6 +255,17 @@
outputIndex, outputIndex,
}); });
this.drawState.lineID++; this.drawState.lineID++;
} else {
for (let uuid in this.subProcessMap) {
const process = this.subProcessMap[uuid];
for (let key in process.data.output) {
let item = process.data.output[key];
let index = item.indexOf(outputID);
if(index >= 0){
item.splice(index, 1);
}
}
}
} }
}, },
onPinHover(x, y, pin) { onPinHover(x, y, pin) {
...@@ -228,7 +274,7 @@ ...@@ -228,7 +274,7 @@
onPinLeave(x, y, pin) { onPinLeave(x, y, pin) {
this.$refs.toolTip.hide(); this.$refs.toolTip.hide();
}, },
onPintDown(e, process, pin) { onPinDown(e, process, pin) {
document.addEventListener("mousemove", this.onMouseMove); document.addEventListener("mousemove", this.onMouseMove);
document.addEventListener("mouseup", this.onMouseUp); document.addEventListener("mouseup", this.onMouseUp);
...@@ -270,6 +316,14 @@ ...@@ -270,6 +316,14 @@
this.$delete(this.subProcessMap, process.uuid); this.$delete(this.subProcessMap, process.uuid);
this.$delete(this.process.meta.sub, process.uuid); this.$delete(this.process.meta.sub, process.uuid);
let remain = 0;
for(let key in this.process.meta.sub){
let p = this.process.meta.sub[key];
if (p.meta === meta.id) {
remain ++;
}
}
for (let id in this.lines) { for (let id in this.lines) {
const line = this.lines[id]; const line = this.lines[id];
const {prev, next} = line; const {prev, next} = line;
...@@ -280,9 +334,25 @@ ...@@ -280,9 +334,25 @@
} }
if (meta.isDivider || meta.isInline) { //如果是分流节点或者内联节点还要删除对应的meta if (meta.isDivider || meta.isInline) { //如果是分流节点或者内联节点还要删除对应的meta
this.deleteProcessMeta({ if(remain === 0){
meta, this.deleteProcessMeta({
process: this.process, meta,
process: this.process,
});
}
}
},
onProcessNodeCopy(data, meta) {
if (meta.isInline) { //内联直接复制
let avatar = clonePureObj(data);
this.addProcessFromCopy(avatar);
} else {
copy(JSON.stringify(data), {format: 'process-data'});
this.$message({
message: this.$t('Copied process to clipboard'),
type: 'success',
duration: 700,
}); });
} }
}, },
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
}, },
methods: { methods: {
parseName(process){ parseName(process){
return process.data.alias || process.meta.name; return process.meta.name;
}, },
onClickItem(process, index){ onClickItem(process, index){
this.$emit('pop', index); this.$emit('pop', index);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<div class="top-bar" v-if="meta.id !== 'entry' && editable"> <div class="top-bar" v-if="meta.id !== 'entry' && editable">
<el-link icon="el-icon-delete" :underline="false" @mousedown.stop.prevent @click.stop="onClickDelete"/> <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"/> <el-link icon="el-icon-edit" :underline="false" v-if="meta.type !== 'builtin' && !meta.isDivider" @mousedown.stop.prevent @click.stop="onClickEdit"/>
<el-link icon="el-icon-document-copy" :underline="false" @mousedown.stop.prevent @click.stop="onClickCopy"/>
</div> </div>
<div class="header"> <div class="header">
<i v-if="meta.isInline">i</i> <i v-if="meta.isInline">i</i>
...@@ -231,6 +232,9 @@ ...@@ -231,6 +232,9 @@
onClickEdit() { onClickEdit() {
this.$emit('edit-meta', this.data, this.meta); this.$emit('edit-meta', this.data, this.meta);
}, },
onClickCopy() {
this.$emit('copy', this.data, this.meta);
},
outputPointModify(action) { outputPointModify(action) {
let output = this.meta.output; let output = this.meta.output;
let count = output.length; let count = output.length;
......
<template>
<codemirror ref="codeEditor"
:value="value"
:options="cmOptions"
@input="onChange"
@inputRead="inputRead"/>
</template>
<script>
import {codemirror} from "vue-codemirror";
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/ayu-mirage.css'
import 'codemirror/addon/edit/closebrackets.js'
import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/lint/lint.js'
import 'codemirror/addon/lint/lint.css'
import 'codemirror/addon/lint/javascript-lint.js'
import 'codemirror/addon/search/searchcursor.js'
import 'codemirror/addon/search/search.js'
import 'codemirror/addon/search/jump-to-line.js'
import 'codemirror/addon/dialog/dialog.js'
import 'codemirror/addon/dialog/dialog.css'
import '../../../utils/javascript-hint.js'
export default {
name: "CodeEditor",
components: {codemirror},
data(){
return {
cmOptions: {
tabSize: 2,
styleActiveLine: true,
theme: 'ayu-mirage',
line: true,
matchBrackets: true,
autoCloseBrackets: true,
extraKeys: {
"Alt-Space": "autocomplete",
},
lineNumbers: true,
mode: {name: 'javascript', globalVars: true},
gutters: ["CodeMirror-lint-markers"],
lint: {
esversion: 6
},
keyword: {
caseInsensitive: true,
}
}
}
},
props: {
value: {
type: String,
default: '',
}
},
methods:{
inputRead(codemirror) {
if (codemirror.state.completionActive) {
return;
}
let cur = codemirror.getCursor();
let token = codemirror.getTokenAt(cur);
let string = '';
if (token.string.match(/^[.`\w@]\w*$/)) {
string = token.string;
}
if (string.length > 0) {
codemirror.showHint({ completeSingle: false });
}
},
onChange(v){
this.$emit('input', v);
},
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<el-dialog
:title="$t('Alert')"
:visible.sync="dialogVisible"
append-to-body
@close="onClose"
>
<el-checkbox v-model="asInline">{{$t('As inline')}}</el-checkbox>
<span slot="footer" class="dialog-footer">
<el-button size="mini" @click="dialogVisible = false">{{$t('Cancel')}}</el-button>
<el-button size="mini" type="primary" @click="onConfirm">{{$t('Confirm')}}</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name: "InlineChooseDialog",
data() {
return {
dialogVisible: false,
asInline: false,
}
},
methods: {
show() {
this.dialogVisible = true;
return new Promise((resolve, reject) => {
this.p = {resolve, reject};
});
},
onClose() {
if (this.p) {
this.p.reject();
this.p = null;
}
this.dialogVisible = false;
},
onConfirm() {
if (this.p) {
this.p.resolve({
isInline: this.asInline,
});
this.p = null;
}
this.dialogVisible = false;
},
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template> <template>
<el-dialog :title="$t('Meta Editor')" width="80%" :visible.sync="visible" <el-dialog
:close-on-click-modal="false" :title="$t('Meta Editor')"
:append-to-body="true"> width="80%"
<div class="meta-editor-wrapper"> :visible.sync="visible"
<el-form ref="form" v-if="meta" :model="meta" :rules="rules" :show-message="false" class="info-editor" size="mini" label-position="right" label-width="70px" @submit.native.prevent> :close-on-click-modal="false"
<template> :close-on-press-escape="false"
<el-form-item prop="id" :label="$t('ID')"> :show-close="false"
<el-input v-model="meta.id" :placeholder="$t('ID')" :readonly="!editable"/> fullscreen
</el-form-item> :append-to-body="true"
<el-form-item prop="name" :label="$t('Name')"> custom-class="flex-dialog behavior-editor-dialog"
<el-input v-model="meta.name" :placeholder="$t('Name')" :readonly="!editable"/> >
</el-form-item> <div class="meta-editor-wrapper">
<el-form-item prop="desc" :label="$t('Desc')"> <el-form
<el-input v-model="meta.desc" :placeholder="$t('Description')" :readonly="!editable"/> ref="form"
</el-form-item> v-if="meta"
<!--<el-form-item prop="type" label="Type"> :model="meta"
:rules="rules"
:show-message="false"
class="info-editor"
size="mini"
label-position="right"
label-width="70px"
@submit.native.prevent
>
<template>
<el-form-item prop="id" :label="$t('ID')">
<el-input
v-model="meta.id"
:placeholder="$t('ID')"
:readonly="!editable"
/>
</el-form-item>
<el-form-item prop="name" :label="$t('Name')">
<el-input
v-model="meta.name"
:placeholder="$t('Name')"
:readonly="!editable"
/>
</el-form-item>
<el-form-item prop="desc" :label="$t('Desc')">
<el-input
v-model="meta.desc"
:placeholder="$t('Description')"
:readonly="!editable"
/>
</el-form-item>
<!--<el-form-item prop="type" label="Type">
<el-input v-model="meta.type" :placeholder="$t('Type')" :readonly="!editable"/> <el-input v-model="meta.type" :placeholder="$t('Type')" :readonly="!editable"/>
</el-form-item> </el-form-item>
<el-form-item prop="group" label="Group"> <el-form-item prop="group" label="Group">
<el-input v-model="meta.group" :placeholder="$t('Group')" :readonly="!editable"/> <el-input v-model="meta.group" :placeholder="$t('Group')" :readonly="!editable"/>
</el-form-item>--> </el-form-item>-->
<el-form-item :label="$t('Props')"> <el-form-item :label="$t('Props')">
<el-link :underline="false" @click="onClickEditProps" :disabled="!editable"> <el-link
<template v-if="Object.keys(meta.props).length"> :underline="false"
<el-tag type="success" size="mini" v-for="(option, key) in meta.props" :key="key">{{key}}</el-tag> @click="onClickEditProps"
</template> :disabled="!editable"
<template v-else>{{$t('Empty')}}</template> >
</el-link> <template v-if="Object.keys(meta.props).length">
</el-form-item> <el-tag
<el-form-item :label="$t('Output')"> type="success"
<div style="display: flex;flex: 1;"> size="mini"
<el-select style="flex: 1;" v-model="meta.output" :disabled="!editable" allow-create filterable multiple v-for="(option, key) in meta.props"
:placeholder="$t('Output')"/> :key="key"
</div> >{{ key }}</el-tag
</el-form-item> >
</template> </template>
</el-form> <template v-else>{{ $t("Empty") }}</template>
<div style="margin-top: 5px;"> </el-link>
<el-tag v-for="item in exposeVariables" size="mini">{{item}}</el-tag> </el-form-item>
</div> <el-form-item :label="$t('Output')">
<el-input v-if="meta" class="script-editor" :readonly=" !editable" type="textarea" :placeholder="$t('Code')" <div style="display: flex;flex: 1;">
v-model="meta.script"></el-input> <el-select
</div> style="flex: 1;"
<div slot="footer" class="dialog-footer"> v-model="meta.output"
<div class="button-bar"> :disabled="!editable"
<el-button-group style="margin-right: 20px;"> allow-create
<el-button size="mini" plain @click="copyMeta">CopyMeta</el-button> filterable
<el-popover> multiple
<div ref="pasteBoard" @paste="onPaste">Click and Ctrl+V</div> :placeholder="$t('Output')"
<el-button size="mini" slot="reference" plain @click="pasteMeta">PasteMeta</el-button> />
</el-popover> </div>
</el-button-group> </el-form-item>
<el-button size="mini" plain @click="cancel">{{$t('Cancel')}}</el-button> </template>
<el-button size="mini" plain @click="save">{{$t('Save')}}</el-button> </el-form>
</div> <div style="margin-top: 5px;">
</div> <code-sync-indicator />
<props-editor-dialog ref="propsEditorDialog"/> <el-tag v-for="(item, key) in exposeVariables" :key="key" size="mini">{{
</el-dialog> item
}}</el-tag>
</div>
<!--<el-input v-if="meta" class="script-editor" :readonly=" !editable" type="textarea" :placeholder="$t('Code')"
v-model="meta.script"></el-input>-->
<!-- <codemirror
ref="codeEditor"
v-if="meta"
v-model="meta.script"
:options="cmOptions"
@cursorActivity="onCodeChange"
/>-->
<monaco-editor
ref="codeEditor"
v-if="meta"
:code="meta.script"
:options="monacoConfig"
/>
</div>
<div slot="footer" class="dialog-footer">
<div class="button-bar">
<el-button-group style="margin-right: 20px;">
<el-button size="mini" plain @click="copyMeta">CopyMeta</el-button>
<el-popover>
<div ref="pasteBoard" @paste="onPaste">Click and Ctrl+V</div>
<el-button size="mini" slot="reference" plain @click="pasteMeta"
>PasteMeta</el-button
>
</el-popover>
</el-button-group>
<el-button size="mini" plain @click="cancel">{{
$t("Cancel")
}}</el-button>
<!--<el-button size="mini" plain @click="save(true)">{{$t('Save And Preview')}}</el-button>-->
<el-button size="mini" type="primary" plain @click="save(false)">{{
$t("Save")
}}</el-button>
</div>
</div>
<props-editor-dialog ref="propsEditorDialog" />
</el-dialog>
</template> </template>
<script> <script>
import ElFormItem from "./inputs/form-item"; import ElFormItem from "./inputs/form-item";
import PropsEditorDialog from "./PropsEditorDialog"; import PropsEditorDialog from "./PropsEditorDialog";
import copy from 'copy-to-clipboard' import * as monaco from "monaco-editor";
import {clonePureObj} from "../../../utils"; import MonacoEditor from "../components/MonacoEditor";
import copy from "copy-to-clipboard";
import { clonePureObj } from "../../../utils";
import events from "../../../global-events";
import CodeSyncIndicator from "../BottomBar/CodeSyncIndicator";
const exposeVariables = ['args', 'props', 'target', 'global', 'vm', 'engine']; const exposeVariables = ["args", "props", "target", "global", "vm", "engine"];
export default { export default {
name: "MetaEditorDialog", name: "MetaEditorDialog",
components: {PropsEditorDialog, ElFormItem}, components: {
data() { CodeSyncIndicator,
return { PropsEditorDialog,
visible: false, ElFormItem,
meta: null, monaco,
propsEditorVisible: false, "monaco-editor": MonacoEditor
exposeVariables, },
data() {
return {
visible: false,
meta: null,
propsEditorVisible: false,
exposeVariables,
rules: { rules: {
id: [ id: [{ required: true, trigger: "blur" }],
{ required: true, trigger: 'blur' }, name: [{ required: true, trigger: "blur" }]
], },
name: [ cmOptions: {
{ required: true, trigger: 'blur' } tabSize: 2,
], styleActiveLine: true,
}, theme: "default",
} line: true,
}, matchBrackets: true,
computed: { autoCloseBrackets: true,
editable() { extraKeys: {
return this.meta;// && this.meta.type !== 'builtin'; "Alt-Space": "autocomplete"
}, },
props() {
return Object.keys(this.meta.props).join(',')
},
},
methods: {
edit(meta) {
this.visible = true;
this.meta = clonePureObj(meta);
this.oldMetaID = this.meta.id;
},
onClickEditProps() {
this.$refs.propsEditorDialog.edit(this.meta.props);
},
save() {
this.$refs.form.validate((valid) => {
if (valid) {
if (this.oldMetaID !== this.meta.id && this.$store.getters.metaIDExists(this.meta.id)) {
this.$alert(this.$t('This Meta ID is in use, can not save'), this.$t('Alert'))
.catch((e) => {
});
} else {
this.$emit('input', this.meta);
this.visible = false;
}
} else {
return false;
}
});
},
cancel() {
this.visible = false;
},
copyMeta() {
copy(JSON.stringify(this.meta));
},
pasteMeta() {
//setTimeout(this.focusPasteBoard, 1000, this);
},
onPaste(e) {
let metaStr = e.clipboardData.getData("Text");
if(metaStr){
try {
let meta = JSON.parse(metaStr);
this.meta = meta;
}catch (e) {
} lineNumbers: true,
} mode: { name: "javascript", globalVars: true },
}, gutters: ["CodeMirror-lint-markers"],
focusPasteBoard(){ lint: true
this.$refs.pasteBoard.focus(); },
}, monacoConfig: {
} language: "javascript"
} }
</script> };
},
mounted() {
events.$on("edit-save", this.onEditSave);
},
destroyed() {
events.$off("edit-save", this.onEditSave);
},
computed: {
editable() {
return this.meta; // && this.meta.type !== 'builtin';
},
props() {
return Object.keys(this.meta.props).join(",");
}
},
methods: {
edit(meta) {
this.visible = true;
this.meta = clonePureObj(meta);
this.oldMetaID = this.meta.id;
<style scoped> events.$emit("edit-open", this.meta.script);
},
onEditSave(code) {
this.$set(this.meta, "script", code);
},
onClickEditProps() {
this.$refs.propsEditorDialog.edit(this.meta.props);
},
save(isPreview) {
this.$refs.form.validate(valid => {
if (valid) {
if (
this.oldMetaID !== this.meta.id &&
this.$store.getters.metaIDExists(this.meta.id)
) {
this.$alert(
this.$t("This Meta ID is in use, can not save"),
this.$t("Alert")
).catch(e => {});
} else {
this.meta.script = this.$refs.codeEditor.editor.getValue();
this.$emit("input", this.meta, isPreview);
this.visible = false;
}
} else {
return false;
}
});
},
cancel() {
this.visible = false;
},
copyMeta() {
copy(JSON.stringify(this.meta));
},
pasteMeta() {
//setTimeout(this.focusPasteBoard, 1000, this);
},
onPaste(e) {
let metaStr = e.clipboardData.getData("Text");
if (metaStr) {
try {
let meta = JSON.parse(metaStr);
this.meta = meta;
} catch (e) {}
}
},
focusPasteBoard() {
this.$refs.pasteBoard.focus();
},
onCodeChange(codemirror) {
//codemirror.showHint();
//console.log(code);
}
}
};
</script>
</style> <style scoped></style>
\ No newline at end of file
<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">
<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
<template> <template>
<div class="process-tree"> <div class="process-tree">
<el-input v-model="filterText" size="mini"/> <el-input v-model="filterText" prefix-icon="el-icon-search" size="mini" clearable/>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden"> <el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden">
<el-tree <el-tree
ref="tree" ref="tree"
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
> >
<div slot-scope="{ node, data }" class="process-tree-node"> <div slot-scope="{ node, data }" class="process-tree-node">
<div class="node-name"> <div class="node-name">
<span :draggable="draggable(data)" @dragstart.stop="dragProcessStart(data, $event)">{{data.name}}</span> <span :class="{'current-node': isCurrentProcess(data)}" :draggable="draggable(data)" @dragstart.stop="dragProcessStart(data, $event)">{{data.name}}</span>
</div> </div>
<el-dropdown v-if="nodeMenu(data)" class="more-button" size="mini" trigger="click" <el-dropdown v-if="nodeMenu(data)" class="more-button" size="mini" trigger="click"
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
</template> </template>
<script> <script>
import {mapState} from 'vuex'
const editableIds = ['custom', 'divider']; const editableIds = ['custom', 'divider'];
export default { export default {
...@@ -59,6 +61,13 @@ ...@@ -59,6 +61,13 @@
deep: true deep: true
} }
}, },
computed: {
...mapState({
currentProcess(state){
return state.behavior.currentProcess;
}
}),
},
methods: { methods: {
updateFilter(){ updateFilter(){
if(this.$refs.tree){ if(this.$refs.tree){
...@@ -99,6 +108,13 @@ ...@@ -99,6 +108,13 @@
let filterDivider = data.isDivider !== true; let filterDivider = data.isDivider !== true;
if (!value) return filterDivider; if (!value) return filterDivider;
return filterDivider && !data.hasOwnProperty('children') && data.name.toUpperCase().indexOf(value.toUpperCase()) >= 0; return filterDivider && !data.hasOwnProperty('children') && data.name.toUpperCase().indexOf(value.toUpperCase()) >= 0;
},
isCurrentProcess(data){
let yes= this.currentProcess && this.currentProcess.meta && data.id && data.id === this.currentProcess.meta.id;
if(yes){
console.log();
}
return yes;
} }
}, },
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
</el-input> </el-input>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" <el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view"> view-class="scrollbar-view">
<el-form v-model="process" size="mini" label-width="80px" label-position="left" @submit.native.prevent> <el-form v-model="process" size="mini" label-width="100px" label-position="left" @submit.native.prevent>
<component v-for="(property, key) in process.meta.props" <component v-for="(property, key) in process.meta.props"
:is="getInput(property)" :is="getInput(property)"
v-model="process.data.props[key]" v-model="process.data.props[key]"
......
<template> <template>
<el-form-item class="input-wrapper" :label="property.alias || propertyName" content-float="right" <el-form-item class="input-wrapper" :label="propertyName" content-float="right"
:content-width="contentWidth" :labelOffsetTop="labelOffsetTop"> :content-width="contentWidth" :labelOffsetTop="labelOffsetTop">
<el-tooltip placement="top" class="label" slot="label" trigger="hover" :open-delay="500" :enterable="false">
<div slot="content" class="property-name-popover">
<p>{{$t('Alias')}}{{property.alias}}</p>
<p>{{$t('Name')}}{{propertyName}}</p>
</div>
<div>
<p class="alias">{{property.alias}}</p>
<p class="property-name">{{propertyName}}</p>
</div>
</el-tooltip>
<template v-if="linked"> <template v-if="linked">
<span class="linked">Linked to parent</span> <span class="linked">Linked to parent</span>
</template> </template>
...@@ -48,13 +58,11 @@ ...@@ -48,13 +58,11 @@
default: 0, default: 0,
}, },
}, },
data(){ data() {
return { return {}
}
}, },
watch: { watch: {
value(v){ value(v) {
} }
}, },
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<dynamic-selector style="flex: 1;" v-model="item.value" <dynamic-selector style="flex: 1;" v-model="item.value"
:editable="editable"/> :editable="editable"/>
<el-button class="delete-button" icon="el-icon-minus" size="mini" plain circle type="danger" <el-button class="delete-button" icon="el-icon-minus" size="mini" plain circle type="danger"
@click="addMapItem"/> @click="deleteMapItem(index)"/>
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
...@@ -85,6 +85,9 @@ ...@@ -85,6 +85,9 @@
addMapItem() { addMapItem() {
this.editValue.push({}); this.editValue.push({});
}, },
deleteMapItem(index) {
this.editValue.splice(index, 1);
},
save() { save() {
let v = {type: 'map', value: {}}; let v = {type: 'map', value: {}};
for (let item of this.editValue) { for (let item of this.editValue) {
......
<template>
<el-checkbox v-model="editValue"/>
</template>
<script>
export default {
name: "EnabledSetter",
props: ['target'],
computed: {
editValue: {
get() {
return !this.target.disabled;
},
set(v) {
this.$set(this.target, 'disabled', !v);
}
}
},
methods: {}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<div id="container"></div>
</template>
<script>
import * as monaco from "monaco-editor";
export default {
name: "MonacoEditor",
props: {
options: Object,
code: String
},
mounted() {
const { options, code } = this;
const defaultConfig = {
language: "javascript",
value: "",
minimap: {
enabled: false
}
};
// compiler options
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES6,
allowNonTsExtensions: true
});
// extra libraries
monaco.languages.typescript.javascriptDefaults.addExtraLib(
[window.__data.types].join("\n")
);
const config = Object.assign({}, defaultConfig, options, { value: code });
console.log(config);
this.editor = monaco.editor.create(
document.getElementById("container"),
config
);
}
};
</script>
<style lang="scss" scoped>
#container {
width: 100%;
height: 100%;
}
</style>
\ No newline at end of file
<template> <template>
<div class="views-tree"> <div class="views-tree">
<custom-node v-for="view in views" :key="view.uuid" :custom-style="styleObject(view)" :properties="view.properties"> <custom-node v-for="view in views" :key="view.uuid" :view="view" :custom-style="styleObject(view)" :properties="view.properties">
<template v-if="view.children"> <template v-if="view.children">
<views-tree :views="view.children"></views-tree> <views-tree :views="view.children"></views-tree>
</template> </template>
......
<template> <template>
<el-dialog :title="$t('Behavior Editor')" :visible.sync="visible" :before-close="beforeClose" @opened="onOpened" <el-dialog :title="$t('Behavior Editor')" :visible.sync="visible" :before-close="beforeClose" @opened="onOpened"
:fullscreen="true" :close-on-click-modal="false" :close-on-press-escape="false" :fullscreen="true" :close-on-click-modal="false" :close-on-press-escape="false"
:append-to-body="true" custom-class="behavior-editor-dialog"> :append-to-body="true" custom-class="flex-dialog behavior-editor-dialog">
<behavior-editor v-if="editorReady" 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"> <div slot="footer" class="dialog-footer">
<el-button size="mini" type="primary" @click="onSave">{{$t('Save')}}</el-button> <el-button size="mini" @click="onSave(true)">{{$t('Save And Preview')}}</el-button>
<el-button size="mini" type="primary" @click="onSave(false)">{{$t('Save And Close')}}</el-button>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import {mapState, mapMutations} from 'vuex' import {mapState, mapMutations, mapActions} from 'vuex'
import BehaviorEditor from "../behavior-editor/BehaviorEditor"; import BehaviorEditor from "../behavior-editor/BehaviorEditor";
import events from "@/global-events.js"
export default { export default {
name: "BehaviorEditorDialog", name: "BehaviorEditorDialog",
...@@ -28,20 +30,34 @@ ...@@ -28,20 +30,34 @@
}), }),
}, },
created() {
events.$on('behaviorSave', (isPreview) => {
this.onSave(isPreview)
});
},
methods: { methods: {
show(behaviors, event) { show(behavior, behaviors) {
this.behaviors = behaviors;
this.behavior_startEdit({ this.behavior_startEdit({
originData: this.data, originData: this.data,
behaviors, behavior,
event,
}); });
this.editorReady = false; this.editorReady = false;
this.visible = true; this.visible = true;
}, },
onSave() { async onSave(isPreview) {
this.behavior_save(); const behavior = await this.behavior_save();
this.visible = false; for (let i = 0, li = this.behaviors.length; i < li; i++) {
this.$emit('change'); 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;
}
}, },
beforeClose(done) { beforeClose(done) {
this.$confirm(this.$t('Save this behavior before'), this.$t('Alert'), { this.$confirm(this.$t('Save this behavior before'), this.$t('Alert'), {
...@@ -68,9 +84,11 @@ ...@@ -68,9 +84,11 @@
}, },
...mapMutations([ ...mapMutations([
'behavior_startEdit', 'behavior_startEdit',
'behavior_save',
'behavior_cancel', 'behavior_cancel',
]), ]),
...mapActions([
'behavior_save',
])
} }
} }
</script> </script>
......
<template>
<el-dialog :title="$t('CodeSyncServe')" width="70%" :visible.sync="visible"
:append-to-body="true"
custom-class=""
>
<div>
<el-form label-position="right" label-width="50px" size="mini">
<el-form-item label="IP">
<el-input v-model="config.ip"/>
</el-form-item>
<el-form-item label="Port">
<el-input-number v-model="config.port" controls-position="right" :max="65535"/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<div>
</div>
<div>
<el-button size="mini" @click="onClose">{{$t('Close')}}</el-button>
<el-button size="mini" @click="onSave" type="primary">{{$t('Save')}}</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import {mapMutations, mapState} from 'vuex'
import EnabledSetter from "../components/EnabledSetter";
import ElFormItem from "../behavior-editor/inputs/form-item";
import {clonePureObj} from "../../../utils";
import {startCodeSyncServe} from "../../../code-sync-serve";
export default {
name: "CodeSyncServeDialog",
components: {ElFormItem, EnabledSetter,},
data() {
return {
visible: false,
config: {},
}
},
mounted() {
},
computed: {
...mapState({
codeSyncServeConfig(state) {
return state.env.codeSyncServeConfig;
}
})
},
methods: {
show() {
this.config = clonePureObj(this.codeSyncServeConfig);
this.visible = true;
},
onSave() {
this.saveCodeSyncServeConfig(this.config);
startCodeSyncServe(this.config);
this.visible = false;
},
onClose() {
this.visible = false;
},
...mapMutations([
'saveCodeSyncServeConfig',
]),
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template> <template>
<el-dialog :title="$t('Project details')" width="70%" :visible.sync="visible" @open="onOpen" <el-dialog :title="$t('Project details')" width="70%" :visible.sync="visible" @opened="onOpen"
:close-on-click-modal="false" :close-on-click-modal="false"
:append-to-body="true" :append-to-body="true"
:show-close="false" :show-close="false"
custom-class="details-dialog" fullscreen
custom-class="flex-dialog details-dialog"
> >
<el-tabs v-model="activeName"> <el-tabs v-model="activeName" class="tabs">
<el-tab-pane :label="$t('Project')" name="project"> <el-tab-pane :label="$t('Project')" name="project">
<project-editor/> <project-editor ref="projectEditor"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('Env constant')" name="env"> <el-tab-pane :label="$t('Env constant')" name="env">
<env-editor/> <env-editor ref="envEditor"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('Data mapping')" name="data-mapping"> <el-tab-pane :label="$t('Data mapping')" name="data-mapping">
<data-mapping-editor/> <data-mapping-editor ref="dataMappingEditor"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('Custom module')" name="custom"> <el-tab-pane :label="$t('Custom module')" name="custom">
<custom-editor/> <custom-module-editor ref="customModuleEditor"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button size="mini" @click="onClose">{{$t('Close')}}</el-button> <el-button size="mini" @click="onClose">{{$t('Close')}}</el-button>
<el-button size="mini" @click="onSave" type="primary">{{$t('Save')}}</el-button>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import {mapMutations} from 'vuex';
import ProjectEditor from "./editors/ProjectEditor"; import ProjectEditor from "./editors/ProjectEditor";
import EnvEditor from "./editors/EnvEditor"; import EnvEditor from "./editors/EnvEditor";
import CustomEditor from "./editors/CustomEditor"; import CustomModuleEditor from "./editors/CustomModuleEditor";
import DataMappingEditor from "./editors/DataMappingEditor"; import DataMappingEditor from "./editors/DataMappingEditor";
const refs = [
'projectEditor',
'envEditor',
'dataMappingEditor',
'customModuleEditor',
];
export default { export default {
name: "DetailsDialog", name: "DetailsDialog",
components: {DataMappingEditor, CustomEditor, EnvEditor, ProjectEditor}, components: {DataMappingEditor, CustomModuleEditor, EnvEditor, ProjectEditor},
data() { data() {
return { return {
visible: false, visible: false,
...@@ -44,12 +54,24 @@ ...@@ -44,12 +54,24 @@
show() { show() {
this.visible = true; this.visible = true;
}, },
onSave() {
for (let ref of refs) {
this.$refs[ref].save();
}
this.visible = false;
this.modifyProjectDetails();
},
onClose() { onClose() {
this.visible = false; this.visible = false;
}, },
onOpen() { onOpen() {
for (let ref of refs) {
this.$refs[ref].edit();
}
}, },
...mapMutations([
'modifyProjectDetails',
]),
} }
} }
</script> </script>
......
<template>
<el-dialog :title="$t('Mock Editor')" width="70%" :visible.sync="visible"
:close-on-click-modal="false"
:append-to-body="true"
:show-close="false"
fullscreen
custom-class="flex-dialog mock-editor-dialog"
>
<div class="mock-editor-view">
<label class="enabled-switch">
<el-switch :value="mockServeEnabled" @input="onMockServeEnabledChange"/>
{{$t('Enable mock serve')}}
</label>
<el-table class="mock-table" :data="mocks" height="100%" stripe size="mini">
<el-table-column type="expand">
<template slot-scope="props">
<codemirror ref="codeEditor"
v-model="props.row.data"
:options="cmOptions"/>
</template>
</el-table-column>
<el-table-column
prop="disabled"
width="40">
<template slot-scope="scope">
<enabled-setter :target="scope.row"/>
</template>
</el-table-column>
<el-table-column
prop="path"
:label="$t('Path')"
width="200">
<template slot-scope="scope">
<el-input v-model="scope.row.path" size="mini"/>
</template>
</el-table-column>
<el-table-column
prop="timeout"
:label="$t('Timeout')"
width="100">
<template slot-scope="scope">
<el-input-number style="width:100%;" v-model="scope.row.timeout" size="mini" controls-position="right"/>
</template>
</el-table-column>
<el-table-column
prop="successRatio"
:label="$t('SuccessRatio')"
width="150">
<template slot-scope="scope">
<el-slider v-model="scope.row.successRatio" size="mini" :max="1" :min="0" :step="0.1"/>
</template>
</el-table-column>
<el-table-column
prop="data"
:label="$t('Data')">
<template slot-scope="scope">
<span style="white-space: nowrap;overflow: hidden;">{{scope.row.data}}</span>
</template>
</el-table-column>
<el-table-column
width="50">
<template slot-scope="scope">
<el-button circle size="mini" type="danger" icon="el-icon-delete" plain
@click="toDeleteItem(scope.$index)"/>
</template>
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer">
<div>
<el-button size="mini" @click="onAdd">{{$t('Add')}}</el-button>
</div>
<div>
<el-button size="mini" @click="onClose">{{$t('Close')}}</el-button>
<el-button size="mini" @click="onSave" type="primary">{{$t('Save')}}</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import {mapState, mapMutations, mapActions} from 'vuex'
import EnabledSetter from "../components/EnabledSetter";
import {clonePureObj} from "../../../utils";
import {codemirror} from "vue-codemirror";
import 'codemirror/addon/lint/lint.js'
import 'codemirror/addon/lint/lint.css'
import 'codemirror/addon/lint/json-lint.js'
export default {
name: "MockEditorDialog",
components: {EnabledSetter, codemirror},
data() {
return {
visible: false,
mocks: [],
cmOptions: {
tabSize: 2,
styleActiveLine: true,
theme: 'default',
line: true,
matchBrackets: true,
autoCloseBrackets: true,
lineNumbers: true,
mode: "application/json",
gutters: ["CodeMirror-lint-markers"],
lint: true
}
}
},
mounted() {
},
computed: {
...mapState({
mocksOrigin: state => state.project.data.mock,
mockServeEnabled: state => state.project.mockServeEnabled,
}),
},
watch: {
mockServeEnabled(v) {
}
},
methods: {
show() {
this.mocks = clonePureObj(this.mocksOrigin);
this.visible = true;
},
onSave() {
this.modifyMocks(this.mocks);
this.visible = false;
},
onClose() {
this.visible = false;
},
onAdd() {
this.mocks.push({
path: '',
data: '',
timeout: 0,
successRatio: 1,
});
},
onDelete(index) {
this.mocks.splice(index, 1);
},
toDeleteItem(index) {
this.$confirm(this.$t('Are you sure to delete this item'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this.onDelete(index);
}).catch((e) => {
});
},
onMockServeEnabledChange(v){
this.setMockServeEnabled(v);
},
...mapMutations([
'modifyMocks',
]),
...mapActions([
'setMockServeEnabled',
]),
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
...@@ -7,6 +7,11 @@ ...@@ -7,6 +7,11 @@
> >
<div> <div>
<p>{{$t('Pack project successfully')}}</p> <p>{{$t('Pack project successfully')}}</p>
<el-collapse>
<el-collapse-item :title="$t('Check template code')" name="1">
<el-input v-if="packResult" type="textarea" readonly v-model="packResult.tpl" :rows="10"/>
</el-collapse-item>
</el-collapse>
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button size="mini" @click="onClose">{{$t('Close')}}</el-button> <el-button size="mini" @click="onClose">{{$t('Close')}}</el-button>
...@@ -18,6 +23,7 @@ ...@@ -18,6 +23,7 @@
<script> <script>
import copy from 'copy-to-clipboard' import copy from 'copy-to-clipboard'
import {openPreview} from "../../../utils";
export default { export default {
name: "PackResultDialog", name: "PackResultDialog",
...@@ -40,9 +46,7 @@ ...@@ -40,9 +46,7 @@
openInNewTab() { openInNewTab() {
this.visible = false; this.visible = false;
setTimeout(()=>{ openPreview(this.packResult);
window.open(this.packResult.tplUrl, 'blank');
}, 500);
}, },
copy() { copy() {
this.visible = false; this.visible = false;
......
<template>
<el-dialog :title="$t('Project details')" width="70%" :visible.sync="visible" @opened="onOpen"
:close-on-click-modal="false"
:append-to-body="true"
custom-class="select-asset-dialog"
>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="view">
<asset-list @click-item="onClickItem"/>
</el-scrollbar>
<div slot="footer" class="dialog-footer">
<el-button size="mini" @click="onClose">{{$t('Close')}}</el-button>
</div>
</el-dialog>
</template>
<script>
import AssetList from "../Assets/AssetList";
export default {
name: "SelectAssetDialog",
components: {AssetList},
data() {
return {
visible: false,
activeName: 'project',
}
},
methods: {
show() {
this.$emit('cancel');
this.visible = true;
},
onClose() {
this.$emit('cancel');
this.visible = false;
},
onOpen() {
},
onClickItem(asset) {
this.$emit('select', asset);
this.visible = false;
},
}
}
</script>
<style lang="scss">
.select-asset-dialog {
.scrollbar {
height: 50vh;
}
}
</style>
\ No newline at end of file
<template>
<el-dialog :title="$t('Custom module asset mapping')" width="70%" :visible.sync="visible" @opened="onOpen"
:close-on-click-modal="false"
:append-to-body="true"
:show-close="false"
fullscreen
custom-class="flex-dialog asset-mapping-editor-dialog"
>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="view">
<div class="mapping-list">
<div class="list" v-if="customMeta">
<div class="item" v-for="(item, index) in customMeta.assets" :key="index">
<span class="name">{{item.name}}</span>
<div class="diff">
<div class="side left">
<span class="extname">
{{item.ext}}
</span>
<div class="thumbnail">
<img class="alpha-image-background" v-if="item.ext === '.png'" :src="item.url">
</div>
<el-input size="mini" v-model="item.url" readonly/>
</div>
<el-icon class="el-icon-connection"/>
<div class="side right">
<!--<span class="extname">
{{item.ext}}
</span>-->
<div class="thumbnail">
<img class="alpha-image-background" v-if="item.replace && showThumbnail(item.ext)"
:src="resolveImg(item)">
</div>
<el-input size="mini" v-model="item.replace" clearable>
<el-button slot="append" icon="el-icon-brush" @click="onClickReplace(item)"/>
</el-input>
</div>
</div>
</div>
</div>
</div>
</el-scrollbar>
<div slot="footer" class="dialog-footer">
<el-button size="mini" @click="onClose">{{$t('Close')}}</el-button>
<el-button size="mini" @click="onSave" type="primary">{{$t('Save')}}</el-button>
</div>
<select-asset-dialog ref="selectAssetDialog" @select="onSelectReplace"/>
</el-dialog>
</template>
<script>
import {mapMutations, mapState, mapGetters} from 'vuex';
import {fileTypeIcon} from "../../../../config";
import SelectAssetDialog from "../SelectAssetDialog";
import {clonePureObj} from "../../../../utils";
const linkScheme = 'link://';
export default {
name: "AssetMappingEditorDialog",
components: {SelectAssetDialog},
data() {
return {
visible: false,
mid: '',
customMeta: null,
}
},
computed: {
...mapState({
customs(state) {
console.log(state.env.customs);
return state.env.customs;
},
}),
...mapGetters([
'assets',
])
},
methods: {
showThumbnail(ext) {
return fileTypeIcon[ext] === 't';
},
resolveImg(item) {
let url;
const replace = item.replace;
if (replace.startsWith(linkScheme)) {
let uuid = replace.replace(linkScheme, '');
let asset = this.assets.find(asset => asset.uuid === uuid);
if (asset) {
url = asset.url;
}
} else {
url = replace;
}
return url;
},
onClickReplace(item) {
this.toReplaceItem = item;
this.$refs.selectAssetDialog.show();
},
onSelectReplace(asset) {
this.$delete(this.toReplaceItem, 'replace');
this.$set(this.toReplaceItem, 'replace', linkScheme + asset.uuid);
},
show(data) {
this.mid = data.id;
this.customMeta = clonePureObj(this.customs.find(item => item.id === this.mid));
for (let uuid in data.assetMapping) {
let asset = this.customMeta.assets.find(item => item.uuid === uuid);
if (asset) {
this.$set(asset, 'replace', data.assetMapping[uuid]);
}
}
this.visible = true;
},
onSave() {
let assetMapping = {};
for (let asset of this.customMeta.assets) {
if (asset.replace) {
assetMapping[asset.uuid] = asset.replace;
}
}
this.$emit('save', this.mid, assetMapping);
this.visible = false;
},
onClose() {
this.visible = false;
},
onOpen() {
},
...mapMutations([]),
}
}
</script>
<style lang="scss">
.asset-mapping-editor-dialog {
.view {
padding-right: 10px;
}
.scrollbar {
height: 100%;
.item {
display: flex;
flex-direction: column;
align-items: stretch;
.name{
text-align: center;
}
.diff{
position: relative;
border: 1px solid lightgray;
border-radius: 5px;
min-height: 70px;
align-items: stretch;
display: flex;
.el-icon-connection {
margin-bottom: 6px;
align-self: flex-end;
}
.side {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
.extname {
position: absolute;
background-color: rgba(0, 0, 0, 0.3);
color: white;
padding: 2px;
}
.thumbnail {
margin: 5px;
font-size: 0;
width: 110px;
height: 110px;
display: flex;
justify-content: center;
align-items: center;
img {
max-width: 100px;
max-height: 100px;
}
}
&.left {
.extname {
left: 0;
border-top-left-radius: 4px;
border-bottom-right-radius: 4px;
}
}
&.right {
.extname {
right: 0;
border-top-right-radius: 4px;
border-bottom-left-radius: 4px;
}
}
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="view">
<el-checkbox-group :value="customs" class="custom-module-list" @input="onChange">
<el-checkbox class="item" v-for="customMeta in customMetas" :key="customMeta.id" :label="customMeta.id">
{{customMeta.alias || customMeta.name}}
</el-checkbox>
</el-checkbox-group>
</el-scrollbar>
</template>
<script>
import {mapState, mapMutations} from 'vuex'
export default {
name: "CustomEditor",
data() {
return {
visible: false,
}
},
computed: {
...mapState({
customMetas: state => state.env.customs,
customs: state => state.project.data.customs,
}),
},
methods: {
...mapMutations([
'modifyCustoms',
]),
onChange(v){
this.modifyCustoms(v);
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<el-table v-if="editData" :data="customMetas" stripe height="100%">
<el-table-column
width="40"
>
<template slot-scope="scope">
<el-checkbox v-model="scope.row.selected"/>
</template>
</el-table-column>
<el-table-column
prop="id"
label="ID"
width="200">
</el-table-column>
<el-table-column
prop="name"
:label="$t('Name')"
width="200">
</el-table-column>
<el-table-column
prop="assets"
:label="$t('Assets config')"
width="100">
<template slot-scope="scope">
<el-button size="mini" icon="el-icon-edit" circle type="primary" plain @click="editAssetMapping(scope.row)"/>
</template>
</el-table-column>
<!--<el-table-column
label="Version"
width="100"
>
<template slot-scope="scope">
<el-select size="mini"/>
</template>
</el-table-column>-->
<el-table-column>
</el-table-column>
<asset-mapping-editor-dialog ref="assetMappingEditorDialog" @save="onSaveAssetMapping"/>
</el-table>
</template>
<script>
import {mapState, mapMutations} from 'vuex'
import {clonePureObj} from "../../../../utils";
import AssetMappingEditorDialog from "./AssetMappingEditorDialog";
export default {
name: "CustomModuleEditor",
components: {AssetMappingEditorDialog},
data() {
return {
visible: false,
customMetas: [],
editData: null,
}
},
computed: {
...mapState({
customs: state => state.project.data.customs,
}),
},
methods: {
edit() {
this.editData = clonePureObj(this.customs);
this.customMetas.splice(0);
for (let meta of this.$store.state.env.customs) {
let data = this.editData.find(item => item.id === meta.id);
this.customMetas.push({
id: meta.id,
name: meta.name,
assetMapping: data && (data.assetMapping || {}),
selected: !!data,
})
}
},
save() {
const editData = this.editData;
editData.splice(0);
for (let meta of this.customMetas) {
if (meta.selected) {
editData.push({
id: meta.id,
assetMapping: meta.assetMapping || {},
});
}
}
this.modifyCustoms(editData);
},
editAssetMapping(data) {
this.$refs.assetMappingEditorDialog.show(data);
},
onSaveAssetMapping(mid, assetMapping) {
this.customMetas.find(item=>item.id === mid).assetMapping = assetMapping;
},
...mapMutations([
'modifyCustoms',
]),
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template> <template>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="view"> <el-scrollbar v-if="editData" class="scrollbar" wrap-class="wrap-x-hidden" view-class="view">
<div class="mapping-list"> <div class="mapping-list">
<el-button class="add-button" icon="el-icon-plus" size="mini" circle @click="onAdd"/> <el-button class="add-button" icon="el-icon-plus" size="mini" circle @click="onAdd"/>
<div class="list"> <div class="list">
<div class="item" v-for="(link, index) in mapping"> <div class="item" v-for="(link, index) in editData">
<el-input size="mini" style="width: 30%" v-model="link.name" @change="onChange"/> <el-input size="mini" style="width: 30%" v-model="link.name"/>
<el-icon class="el-icon-connection"/> <el-icon class="el-icon-connection"/>
<el-input size="mini" v-model="link.path" @change="onChange"/> <el-input size="mini" v-model="link.path"/>
<el-button class="delete-button" icon="el-icon-minus" circle size="mini" plain type="danger" @click="toDeleteItem(link, index)"/> <el-button class="delete-button" icon="el-icon-minus" circle size="mini" plain type="danger" @click="toDeleteItem(link, index)"/>
</div> </div>
</div> </div>
...@@ -16,12 +16,14 @@ ...@@ -16,12 +16,14 @@
<script> <script>
import {mapState, mapMutations} from 'vuex' import {mapState, mapMutations} from 'vuex'
import {clonePureObj} from "../../../../utils";
export default { export default {
name: "DataMappingEditor", name: "DataMappingEditor",
data() { data() {
return { return {
visible: false, visible: false,
editData: null,
} }
}, },
computed: { computed: {
...@@ -30,11 +32,17 @@ ...@@ -30,11 +32,17 @@
}), }),
}, },
methods: { methods: {
onAdd() { edit() {
this.addDataMapping(); this.editData = clonePureObj(this.mapping);
},
save() {
this.modifyDataMapping(this.editData);
}, },
onChange() { onAdd() {
this.modifyDataMapping(); this.editData.push({
name: '',
path: '',
});
}, },
toDeleteItem(link, index) { toDeleteItem(link, index) {
this.$confirm(this.$t('Are you sure to delete this item'), this.$t('Alert'), { this.$confirm(this.$t('Are you sure to delete this item'), this.$t('Alert'), {
...@@ -42,13 +50,11 @@ ...@@ -42,13 +50,11 @@
cancelButtonText: this.$t('Cancel'), cancelButtonText: this.$t('Cancel'),
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
this.deleteDataMapping(index); this.editData.splice(index, 1);
}).catch((e) => { }).catch((e) => {
}); });
}, },
...mapMutations([ ...mapMutations([
'addDataMapping',
'deleteDataMapping',
'modifyDataMapping', 'modifyDataMapping',
]), ]),
} }
......
<template> <template>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="view"> <el-scrollbar v-if="editData" class="scrollbar" wrap-class="wrap-x-hidden" view-class="view">
<div class="mapping-list"> <div class="mapping-list">
<el-button class="add-button" icon="el-icon-plus" size="mini" circle @click="onAdd"/> <el-button class="add-button" icon="el-icon-plus" size="mini" circle @click="onAdd"/>
<div class="list"> <div class="list">
<div class="item" v-for="(link, index) in env"> <div class="item" v-for="(link, index) in editData">
<el-input size="mini" style="width: 30%" v-model="link.name" @change="onChange"/> <el-input size="mini" style="width: 30%" v-model="link.name"/>
<el-icon class="el-icon-connection"/> <el-icon class="el-icon-connection"/>
<el-input size="mini" v-model="link.value" @change="onChange"/> <el-input size="mini" v-model="link.value"/>
<el-button class="delete-button" icon="el-icon-minus" circle size="mini" plain type="danger" @click="toDeleteItem(link, index)"/> <el-button class="delete-button" icon="el-icon-minus" circle size="mini" plain type="danger"
@click="toDeleteItem(link, index)"/>
</div> </div>
</div> </div>
</div> </div>
...@@ -16,12 +17,14 @@ ...@@ -16,12 +17,14 @@
<script> <script>
import {mapState, mapMutations} from 'vuex' import {mapState, mapMutations} from 'vuex'
import {clonePureObj} from "../../../../utils";
export default { export default {
name: "EnvEditor", name: "EnvEditor",
data() { data() {
return { return {
visible: false, visible: false,
editData: null,
} }
}, },
computed: { computed: {
...@@ -30,11 +33,17 @@ ...@@ -30,11 +33,17 @@
}), }),
}, },
methods: { methods: {
onAdd() { edit() {
this.addEnvMapping(); this.editData = clonePureObj(this.env);
},
save() {
this.modifyEnv(this.editData);
}, },
onChange() { onAdd() {
this.modifyEnvMapping(); this.editData.push({
name: '',
value: '',
});
}, },
toDeleteItem(link, index) { toDeleteItem(link, index) {
this.$confirm(this.$t('Are you sure to delete this item'), this.$t('Alert'), { this.$confirm(this.$t('Are you sure to delete this item'), this.$t('Alert'), {
...@@ -42,14 +51,12 @@ ...@@ -42,14 +51,12 @@
cancelButtonText: this.$t('Cancel'), cancelButtonText: this.$t('Cancel'),
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
this.deleteEnvMapping(index); this.editData.splice(index, 1);
}).catch((e) => { }).catch((e) => {
}); });
}, },
...mapMutations([ ...mapMutations([
'addEnvMapping', 'modifyEnv',
'deleteEnvMapping',
'modifyEnvMapping',
]), ]),
} }
} }
......
<template> <template>
<div> <div class="project-editor">
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="view"> <el-scrollbar v-if="editData" class="project-scrollbar" wrap-class="wrap-x-hidden" view-class="view">
<el-form @submit.native.prevent ref="form" :model="options" size="mini" label-position="right" <el-form @submit.native.prevent ref="form" :model="editData" size="mini" label-position="right"
label-width="150px"> label-width="150px">
<el-form-item prop="pageTitle" :label="$t('Page title')"> <el-form-item prop="pageTitle" :label="$t('Page title')">
<el-input v-model="options.pageTitle"/> <el-input v-model="editData.pageTitle"/>
</el-form-item> </el-form-item>
<el-form-item prop="entrySceneView" :label="$t('Entry scene view')"> <el-form-item prop="entrySceneView" :label="$t('Entry scene view')">
<el-select v-model="options.entrySceneView"> <el-select v-model="editData.entrySceneView">
<el-option v-for="(view, index) in project.data.views" <el-option v-for="(view, index) in project.data.views"
:key="index" :key="index"
:value="view.name" :value="view.name"
...@@ -16,19 +16,19 @@ ...@@ -16,19 +16,19 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item prop="containerId" :label="$t('Container ID')"> <el-form-item prop="containerId" :label="$t('Container ID')">
<el-input v-model="options.containerId"/> <el-input v-model="editData.containerId"/>
</el-form-item> </el-form-item>
<el-form-item prop="designWidth" :label="$t('Design width')"> <el-form-item prop="designWidth" :label="$t('Design width')">
<el-input-number v-model="options.designWidth" controls-position="right"/> <el-input-number v-model="editData.designWidth" controls-position="right"/>
</el-form-item> </el-form-item>
<el-form-item prop="designHeight" :label="$t('Design height')"> <el-form-item prop="designHeight" :label="$t('Design height')">
<el-input-number v-model="options.designHeight" controls-position="right"/> <el-input-number v-model="editData.designHeight" controls-position="right"/>
</el-form-item> </el-form-item>
<el-form-item prop="frameRate" :label="$t('Frame Rate')"> <el-form-item prop="frameRate" :label="$t('Frame Rate')">
<el-input-number v-model="options.frameRate" :max="60" :min="0" controls-position="right"/> <el-input-number v-model="editData.frameRate" :max="60" :min="0" controls-position="right"/>
</el-form-item> </el-form-item>
<el-form-item prop="scaleMode" :label="$t('Scale Mode')"> <el-form-item prop="scaleMode" :label="$t('Scale Mode')">
<el-select v-model="options.scaleMode"> <el-select v-model="editData.scaleMode">
<el-option v-for="(label, key) in scaleMode" <el-option v-for="(label, key) in scaleMode"
:key="key" :key="key"
:value="key" :value="key"
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item prop="rendererType" :label="$t('Renderer Type')"> <el-form-item prop="rendererType" :label="$t('Renderer Type')">
<el-select v-model="options.rendererType"> <el-select v-model="editData.rendererType">
<el-option v-for="(label, key) in rendererType" <el-option v-for="(label, key) in rendererType"
:key="key" :key="key"
:value="key" :value="key"
...@@ -46,30 +46,55 @@ ...@@ -46,30 +46,55 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item prop="tpl" :label="$t('Template')"> <el-form-item prop="tpl" :label="$t('Template')">
<el-input type="textarea" v-model="options.tpl" :rows="10"/> <!-- <el-input type="textarea" v-model="options.tpl" :rows="10"/> -->
<codemirror ref="codeEditor"
v-model="editData.tpl"
:options="cmOptions"
@cursorActivity="onCodeChange"
>
</codemirror>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-scrollbar> </el-scrollbar>
<div class="operate-bar"> <!--<div class="operate-bar">
<el-button size="mini" @click="onReset">{{$t('Reset')}}</el-button> <el-button size="mini" @click="onReset">{{$t('Reset')}}</el-button>
<el-button size="mini" type="primary" @click="onSave">{{$t('Save')}}</el-button> <el-button size="mini" type="primary" @click="onSave">{{$t('Save')}}</el-button>
</div> </div>-->
</div> </div>
</template> </template>
<script> <script>
import {mapState, mapGetters, mapMutations} from 'vuex'; import {mapState, mapGetters, mapMutations} from 'vuex';
import {codemirror} from "vue-codemirror";
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/monokai.css'
import 'codemirror/addon/edit/closebrackets.js'
import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/show-hint.css'
import {clonePureObj} from "../../../../utils";
export default { export default {
name: "ProjectEditor", name: "ProjectEditor",
components: {}, components: {codemirror},
data() { data() {
const scaleMode = this.$t('scaleMode'); const scaleMode = this.$t('scaleMode');
const rendererType = this.$t('rendererType'); const rendererType = this.$t('rendererType');
return { return {
visible: false, visible: false,
editData: null,
scaleMode, scaleMode,
rendererType, rendererType,
cmOptions: {
tabSize: 2,
mode: 'text/javascript',
styleActiveLine: true,
theme: 'default',
lineNumbers: true,
line: true,
matchBrackets: true,
autoCloseBrackets: true,
}
} }
}, },
computed: { computed: {
...@@ -82,14 +107,24 @@ ...@@ -82,14 +107,24 @@
}, },
methods: { methods: {
...mapMutations([ ...mapMutations([
'modifyProject', 'modifyOptions',
]), ]),
onReset() { edit() {
this.editData = clonePureObj(this.options);
},
save() {
this.modifyOptions(this.editData);
},
/*onReset() {
this.$refs.form.resetFields(); this.$refs.form.resetFields();
}, },
onSave() { onSave() {
this.modifyProject(); this.modifyProject();
}, },*/
onCodeChange(codemirror) {
//codemirror.showHint();
//console.log(code);
}
} }
} }
</script> </script>
......
<template> <template>
<div class="home"> <div class="home">
<p class="editor-name" style="font-weight: bold">{{env.name}}<span <div class="editor-name" style="font-weight: bold">{{env.name}}
class="editor-version">{{env.version}}</span></p> <span class="editor-version">{{env.version}}</span>
</div>
<main> <main>
<div class="project-list"> <div class="project-list">
<el-table <el-table
:data="projects" :data="projects.projects"
:empty-text="$t('No projects')" :empty-text="$t('No projects')"
height="100%" height="100%"
> >
...@@ -21,32 +22,60 @@ ...@@ -21,32 +22,60 @@
</el-table-column> </el-table-column>
<el-table-column <el-table-column
fixed="right" fixed="right"
width="140"> width="180">
<template slot="header" slot-scope="scope">
<el-checkbox
v-model="onlyMine"
size="mini">{{$t('Only mine')}}
</el-checkbox>
</template>
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
@click.native.prevent="selectProject(scope.row)" @click.native.prevent="selectProject(scope.row)"
type="primary" icon="el-icon-edit" type="primary" icon="el-icon-edit"
size="small" circle plain> size="small" circle plain>
</el-button> </el-button>
<el-button
@click.native.prevent="selectFromHistory(scope.row)"
type="primary" icon="el-icon-coin"
size="small" circle plain>
</el-button>
<el-button <el-button
@click.native.prevent="showDuplicateProjectDialog(scope.row)" @click.native.prevent="showDuplicateProjectDialog(scope.row)"
type="success" icon="el-icon-document-copy" type="success" icon="el-icon-document-copy"
size="small" circle plain> size="small" circle plain>
</el-button> </el-button>
<!--<el-button <el-button
@click.native.prevent="exportProject(scope.row)"
type="warning" icon="icon-download"
size="small" circle plain>
</el-button>
<el-button
v-if="showDeleteButton"
@click.native.prevent="onDeleteProject(scope.row)" @click.native.prevent="onDeleteProject(scope.row)"
type="danger" icon="el-icon-delete" type="danger" icon="el-icon-delete"
size="small" circle plain> size="small" circle plain>
</el-button>--> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<div> <div class="bottom-bar">
<el-button @click="showCreateProjectDialog()">{{$t('Create')}}</el-button> <div>
<el-button type="primary" @click="showCreateProjectDialog()">{{$t('Create')}}</el-button>
<el-button @click="importProject()">{{$t('Import')}}</el-button>
</div>
<el-pagination
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-size="pageSize"
layout="prev, pager, next, jumper"
:total="projects.projectCount">
</el-pagination>
</div> </div>
<create-project-dialog ref="createProjectDialog" @success="onCreateProject"/> <create-project-dialog ref="createProjectDialog" @success="onCreateProject"/>
<duplicate-project-dialog ref="duplicateProjectDialog" @success="onDuplicateProject"/> <duplicate-project-dialog ref="duplicateProjectDialog" @success="onDuplicateProject"/>
<project-history-dialog ref="projectHistoryDialog" @select-history-project="onSelectProject"/>
</main> </main>
</div> </div>
</template> </template>
...@@ -54,32 +83,46 @@ ...@@ -54,32 +83,46 @@
<script> <script>
import {mapState, mapActions} from 'vuex' import {mapState, mapActions} from 'vuex'
import CreateProjectDialog from "./Home/CreateProjectDialog"; import CreateProjectDialog from "./Home/CreateProjectDialog";
import {playWaiting} from "../utils"; import {playWaiting, readTextFile, saveAs, selectFile} from "../utils";
import moment from "moment"; import moment from "moment";
import DuplicateProjectDialog from "./Home/DuplicateProjectDialog"; import DuplicateProjectDialog from "./Home/DuplicateProjectDialog";
import {PROJECT_PAGE_SIZE} from "../config";
import ProjectHistoryDialog from "./Home/ProjectHistoryDialog";
export default { export default {
name: "Home", name: "Home",
components: {DuplicateProjectDialog, CreateProjectDialog}, components: {ProjectHistoryDialog, DuplicateProjectDialog, CreateProjectDialog},
data() { data() {
return { return {
appVersion: 'v1.0.0', appVersion: 'v1.0.0',
currentPage: 1,
pageSize: PROJECT_PAGE_SIZE,
onlyMine: true,
} }
}, },
mounted() { mounted() {
playWaiting(this.prepare(), this.$t('Preparing')).catch(e => {}); playWaiting(this.prepare(), this.$t('Preparing')).catch(e => {
});
}, },
computed: { computed: {
showDeleteButton() {
return localStorage.getItem('---zeroing-editor-delete-button');
},
...mapState([ ...mapState([
'projects', 'projects',
'env', 'env',
]), ]),
}, },
watch: {
onlyMine() {
this.handleCurrentChange(1);
}
},
methods: { methods: {
prepare(){ prepare() {
return Promise.all([ return Promise.all([
this.updateEnv(), this.updateEnv(),
this.updateProjects(), this.handleCurrentChange(1),
]) ])
}, },
moment(time) { moment(time) {
...@@ -88,22 +131,42 @@ ...@@ -88,22 +131,42 @@
showCreateProjectDialog() { showCreateProjectDialog() {
this.$refs.createProjectDialog.show(); this.$refs.createProjectDialog.show();
}, },
importProject() {
selectFile((files) => {
readTextFile(files[0])
.then(
txt => {
let data = JSON.parse(txt);
return playWaiting(this.createProject(data), this.$t('Importing project'));
}
)
});
},
async exportProject({id}) {
const project = await playWaiting(this.fetchProject(id), this.$t('Exporting project'));
saveAs(new Blob([JSON.stringify(project)]), project.name + '.json');
},
showDuplicateProjectDialog(project) { showDuplicateProjectDialog(project) {
this.$refs.duplicateProjectDialog.show(project); this.$refs.duplicateProjectDialog.show(project);
}, },
selectProject(row, event) { selectProject(row, event) {
this.editProject(row.id); this.editProject(row.id);
}, },
selectFromHistory(row, event) {
this.$refs.projectHistoryDialog.show(row)
},
async onDeleteProject(project, event) { async onDeleteProject(project, event) {
this.$confirm(this.$t('This action will permanently delete project', {projectName: project.name}), this.$t('Alert'), { this.$confirm(this.$t('This action will permanently delete project', {projectName: project.name}), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'), confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'), cancelButtonText: this.$t('Cancel'),
type: 'warning' type: 'warning'
}).then( }).then(
()=>{ () => {
playWaiting(this.deleteProject(project.id), this.$t('Deleting')).catch(e => {}); playWaiting(this.deleteProject(project.id), this.$t('Deleting')).catch(e => {
});
}, },
()=>{} () => {
}
) )
}, },
onCreateProject(projectID) { onCreateProject(projectID) {
...@@ -112,11 +175,26 @@ ...@@ -112,11 +175,26 @@
onDuplicateProject(projectID) { onDuplicateProject(projectID) {
this.editProject(projectID); this.editProject(projectID);
}, },
onSelectProject(project, dataUrl) {
this.$router.push({name: 'editor', params: {projectID: project.id, project, dataUrl}});
},
editProject(projectID) { editProject(projectID) {
this.$router.push({name: 'editor', params: {projectID}}); this.$router.push({name: 'editor', params: {projectID}});
}, },
async handleCurrentChange(page) {
const loading = this.$loading({
lock: true,
text: this.$t('In processing'),
});
this.currentPage = page;
await this.fetchProjects({currentPage: page, pageSize: this.pageSize, onlyMine: this.onlyMine});
loading.close();
},
...mapActions([ ...mapActions([
'updateProjects', 'fetchProjects',
'fetchProject',
'fetchHistory',
'createProject',
'deleteProject', 'deleteProject',
'updateEnv', 'updateEnv',
]), ]),
......
<template>
<el-dialog :title="$t('Select from history')" width="70%" :visible.sync="visible" @open="onOpen"
:append-to-body="true">
<div class="project-history-table" v-if="project">
<el-table :data="historyProjects" stripe height="100%">
<el-table-column
label="时间"
width="200">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span style="margin-left: 10px">{{ moment(scope.row.operator_time) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作人"
width="100"
prop="operator"
>
</el-table-column>
<el-table-column
label="版本备注"
prop="remark"
>
<template slot-scope="scope">
<el-popover
class="popover-remark"
placement="top"
width="200"
title="版本备注"
:open-delay="500"
trigger="hover"
:content="scope.row.remark">
<span slot="reference" class="short-remark">{{scope.row.remark}}</span>
</el-popover>
</template>
</el-table-column>
<el-table-column
width="60">
<template slot-scope="scope">
<el-button
@click.native.prevent="selectProject(scope.row)"
type="primary" icon="el-icon-edit"
size="small" circle plain>
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
class="pagination"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-size="pageSize"
layout="prev, pager, next, jumper"
:total="historyProjectCount">
</el-pagination>
</div>
<div slot="footer" class="dialog-footer">
<el-button size="mini" @click="visible=false">{{$t('Cancel')}}</el-button>
</div>
</el-dialog>
</template>
<script>
import {mapState, mapActions} from 'vuex'
import {HISTORY_PAGE_SIZE} from "../../config";
import moment from "moment";
export default {
name: "ProjectHistoryDialog",
data() {
return {
visible: false,
project: null,
currentPage: 1,
historyProjectCount: 0,
pageSize: HISTORY_PAGE_SIZE,
historyProjects: [],
}
},
computed: {
...mapState([
'env',
]),
},
watch: {},
methods: {
async show(project) {
this.project = project;
this.visible = true;
this.handleCurrentChange(1);
},
onOpen() {
},
moment(time) {
return moment(time).format('YYYY-MM-DD HH:mm:ss');
},
async handleCurrentChange(page) {
const loading = this.$loading({
lock: true,
text: this.$t('In processing'),
});
this.currentPage = page;
const {data, total} = await this.fetchHistory({
projectID: this.project.id,
currentPage: page,
pageSize: this.pageSize
});
this.historyProjects = data;
this.historyProjectCount = total;
loading.close();
},
selectProject(item){
this.$emit('select-history-project', this.project, 'http:' + item.url);
this.visible = false;
},
...mapActions([
'fetchHistory'
]),
}
}
</script>
<style lang="scss">
</style>
\ No newline at end of file
<template>
<div class="wrapper">
<iframe v-if="flag" ref="iframe" class="player-wrapper"></iframe>
</div>
</template>
<script>
import {applyMock} from "./Preview/mock-serve";
import db from "../utils/db-storage";
import {newScriptEl} from "../utils";
export default {
name: "Preview",
components: {},
data() {
return {
ts: '',
flag: false,
}
},
async mounted() {
this.codes = {};
this.urls = {};
if (!this.ts) {
this.ts = localStorage.getItem('preview-ts');
}
document.addEventListener("visibilitychange", this.onVisibilityChange);
await db.open();
this.reload();
},
destroyed() {
document.removeEventListener("visibilitychange", this.onVisibilityChange);
},
methods: {
reload() {
this.flag = false;
setTimeout(() => {
this.flag = true;
this.buildPage();
}, 300);
},
onVisibilityChange(e) {
if (!document.hidden) {
let ts = localStorage.getItem('preview-ts');
if (this.ts !== ts) {
//this.$refs.iframe.contentDocument.location.reload();
//document.location.reload();
this.ts = ts;
this.reload();
}
}
},
async buildPage() {
const {projectID} = this.$route.params;
const {data, codes} = await db.get('preview', projectID);
console.log(data);
let {options: {tpl, pageTitle, containerId}} = data;
//const dataName = projectID + "_data";
//const launchCode = `engine.launchWithWindowVariable("${dataName}");`;
const scripts = [];
for (let key in codes) {
let oldCode = this.codes[key];
let code = codes[key];
let url;
if (oldCode !== code) {
url = this.urls[key] = URL.createObjectURL(new Blob([code]));
} else {
url = this.urls[key];
}
scripts.push(newScriptEl(url));
}
this.codes = codes;
const dataUrl = URL.createObjectURL(new Blob([JSON.stringify(data)]));
document.title = pageTitle;
tpl = tpl
.replace('$PAGE_TITLE$', pageTitle)
.replace('$CONTAINER_ID$', containerId)
.replace('$SCRIPTS$', scripts.join('\n'))
.replace('//yun.duiba.com.cn/aurora/$VERSION$-data.json', dataUrl);
const doc = this.$refs.iframe.contentDocument;
const win = this.$refs.iframe.contentWindow;
doc.write(tpl);
applyMock(projectID, win);
},
}
}
</script>
<style scoped>
.wrapper {
width: 100%;
height: 100%;
}
.player-wrapper {
border: 0;
width: 100%;
height: 100%;
}
.mock-button {
position: absolute;
right: 10px;
bottom: 10px;
}
</style>
\ No newline at end of file
/**
* Created by rockyl on 2019-12-18.
*/
import db from "../../utils/db-storage";
import {getMockServeEnabled} from "../../utils";
db.open();
const logStyle = "color: rgb(63, 172, 203)";
export function applyMock(projectID, win) {
win.mock = function ({url, method, params = {}, type}, callback, resolve, reject) {
const mockServeEnabled = getMockServeEnabled(projectID);
if (!mockServeEnabled) {
callback();
return;
}
if(url.includes('projectx')){
url = url.replace(/projectx\/\w+\//, '');
}
db.get('mock', url)
.then(
rule => {
if (rule && !rule.disabled) {
const {timeout = 0, successRatio = 1, data} = rule;
setTimeout(function () {
if (Math.random() < successRatio) {
try {
console.log(`%c[mock] ${url} ${method} ${JSON.stringify(params)}`, logStyle, JSON.parse(data));
resolve(JSON.parse(data));
} catch (e) {
reject(e);
}
} else {
reject(url + ' 404 (Not Found)')
}
}, timeout);
callback(rule);
} else {
callback();
}
},
e => {
callback();
}
);
};
}
\ No newline at end of file
/**
* Created by rockyl on 2019-12-20.
*/
import {clonePureObj} from "../../utils";
import {divideCode} from "zeroing-code-divider";
import db from "../../utils/db-storage";
const storeName = 'preview';
function getPack(packs) {
return function (ids) {
return packs.filter(pack => ids.includes(pack.id))
}
}
export async function preprocess(project, data, processes, scripts, customs) {
let newData = clonePureObj(data);
const codes = await divideCode(newData, {
debug: true,
getProcesses: getPack(processes),
getScripts: getPack(scripts),
getCustoms: getPack(customs),
});
db.set(storeName, {
id: project.id,
data: newData,
codes,
});
localStorage.setItem('preview-ts', Date.now().toString());
}
const serverHost = 'http://10.10.95.74:7777'; const serverHost = 'http://10.10.95.74:7777'
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
module.exports = { module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '//yun.duiba.com.cn/editor/zeroing/v1/' : '', publicPath: process.env.NODE_ENV === 'production' ? '//yun.duiba.com.cn/editor/zeroing/v1/' : '',
...@@ -10,7 +12,7 @@ module.exports = { ...@@ -10,7 +12,7 @@ module.exports = {
enableInSFC: true enableInSFC: true
} }
}, },
devServer: { /*devServer: {
host: '0.0.0.0', host: '0.0.0.0',
port: '8080', port: '8080',
proxy: { proxy: {
...@@ -21,8 +23,13 @@ module.exports = { ...@@ -21,8 +23,13 @@ module.exports = {
target: serverHost, target: serverHost,
}, },
} }
}, },*/
configureWebpack: { configureWebpack: {
plugins: [
new MonacoWebpackPlugin({
languages: ['javascript', 'typescript', 'json'],
publicPath: "/monaco"
})
]
} }
}; }
...@@ -1292,6 +1292,11 @@ address@>=0.0.1, address@^1.0.0, address@^1.0.3: ...@@ -1292,6 +1292,11 @@ address@>=0.0.1, address@^1.0.0, address@^1.0.3:
resolved "https://registry.npm.taobao.org/address/download/address-1.1.2.tgz?cache=0&sync_timestamp=1566806470420&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faddress%2Fdownload%2Faddress-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" resolved "https://registry.npm.taobao.org/address/download/address-1.1.2.tgz?cache=0&sync_timestamp=1566806470420&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faddress%2Fdownload%2Faddress-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
integrity sha1-vxEWycdYxRt6kz0pa3LCIe2UKLY= integrity sha1-vxEWycdYxRt6kz0pa3LCIe2UKLY=
after@0.8.2:
version "0.8.2"
resolved "https://registry.npm.taobao.org/after/download/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=
agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0: agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0:
version "4.3.0" version "4.3.0"
resolved "https://registry.npm.taobao.org/agent-base/download/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" resolved "https://registry.npm.taobao.org/agent-base/download/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
...@@ -1535,6 +1540,11 @@ array-unique@^0.3.2: ...@@ -1535,6 +1540,11 @@ array-unique@^0.3.2:
resolved "https://registry.npm.taobao.org/array-unique/download/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" resolved "https://registry.npm.taobao.org/array-unique/download/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
arraybuffer.slice@~0.0.7:
version "0.0.7"
resolved "https://registry.npm.taobao.org/arraybuffer.slice/download/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
integrity sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=
asap@~2.0.3: asap@~2.0.3:
version "2.0.6" version "2.0.6"
resolved "https://registry.npm.taobao.org/asap/download/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" resolved "https://registry.npm.taobao.org/asap/download/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
...@@ -1689,11 +1699,21 @@ babel-runtime@6.x: ...@@ -1689,11 +1699,21 @@ babel-runtime@6.x:
core-js "^2.4.0" core-js "^2.4.0"
regenerator-runtime "^0.11.0" regenerator-runtime "^0.11.0"
backo2@1.0.2:
version "1.0.2"
resolved "https://registry.npm.taobao.org/backo2/download/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
integrity sha1-MasayLEpNjRj41s+u2n038+6eUc=
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" resolved "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base64-arraybuffer@0.1.5:
version "0.1.5"
resolved "https://registry.npm.taobao.org/base64-arraybuffer/download/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
base64-js@^1.0.2: base64-js@^1.0.2:
version "1.3.1" version "1.3.1"
resolved "https://registry.npm.taobao.org/base64-js/download/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" resolved "https://registry.npm.taobao.org/base64-js/download/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
...@@ -1724,6 +1744,13 @@ bcrypt-pbkdf@^1.0.0: ...@@ -1724,6 +1744,13 @@ bcrypt-pbkdf@^1.0.0:
dependencies: dependencies:
tweetnacl "^0.14.3" tweetnacl "^0.14.3"
better-assert@~1.0.0:
version "1.0.2"
resolved "https://registry.npm.taobao.org/better-assert/download/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=
dependencies:
callsite "1.0.0"
bfj@^6.1.1: bfj@^6.1.1:
version "6.1.2" version "6.1.2"
resolved "https://registry.npm.taobao.org/bfj/download/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" resolved "https://registry.npm.taobao.org/bfj/download/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f"
...@@ -1761,6 +1788,11 @@ bl@^3.0.0: ...@@ -1761,6 +1788,11 @@ bl@^3.0.0:
dependencies: dependencies:
readable-stream "^3.0.1" readable-stream "^3.0.1"
blob@0.0.5:
version "0.0.5"
resolved "https://registry.npm.taobao.org/blob/download/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
integrity sha1-1oDu7yX4zZGtUz9bAe7UjmTK9oM=
block-stream@*: block-stream@*:
version "0.0.9" version "0.0.9"
resolved "https://registry.npm.taobao.org/block-stream/download/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" resolved "https://registry.npm.taobao.org/block-stream/download/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
...@@ -2057,6 +2089,11 @@ caller-path@^2.0.0: ...@@ -2057,6 +2089,11 @@ caller-path@^2.0.0:
dependencies: dependencies:
caller-callsite "^2.0.0" caller-callsite "^2.0.0"
callsite@1.0.0:
version "1.0.0"
resolved "https://registry.npm.taobao.org/callsite/download/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA=
callsites@^2.0.0: callsites@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npm.taobao.org/callsites/download/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" resolved "https://registry.npm.taobao.org/callsites/download/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
...@@ -2365,6 +2402,11 @@ code-point-at@^1.0.0: ...@@ -2365,6 +2402,11 @@ code-point-at@^1.0.0:
resolved "https://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" resolved "https://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codemirror@^5.41.0, codemirror@^5.50.0:
version "5.50.0"
resolved "https://registry.npm.taobao.org/codemirror/download/codemirror-5.50.0.tgz#aeacd18f225735b17cbab98908edace87fedcdab"
integrity sha1-rqzRjyJXNbF8urmJCO2s6H/tzas=
collection-visit@^1.0.0: collection-visit@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npm.taobao.org/collection-visit/download/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" resolved "https://registry.npm.taobao.org/collection-visit/download/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
...@@ -2443,11 +2485,26 @@ commondir@^1.0.1: ...@@ -2443,11 +2485,26 @@ commondir@^1.0.1:
resolved "https://registry.npm.taobao.org/commondir/download/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" resolved "https://registry.npm.taobao.org/commondir/download/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
component-bind@1.0.0:
version "1.0.0"
resolved "https://registry.npm.taobao.org/component-bind/download/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=
component-emitter@1.2.1:
version "1.2.1"
resolved "https://registry.npm.taobao.org/component-emitter/download/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
component-emitter@^1.2.1: component-emitter@^1.2.1:
version "1.3.0" version "1.3.0"
resolved "https://registry.npm.taobao.org/component-emitter/download/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" resolved "https://registry.npm.taobao.org/component-emitter/download/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
integrity sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A= integrity sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=
component-inherit@0.0.3:
version "0.0.3"
resolved "https://registry.npm.taobao.org/component-inherit/download/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=
compressible@~2.0.16: compressible@~2.0.16:
version "2.0.17" version "2.0.17"
resolved "https://registry.npm.taobao.org/compressible/download/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" resolved "https://registry.npm.taobao.org/compressible/download/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
...@@ -2936,14 +2993,14 @@ debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: ...@@ -2936,14 +2993,14 @@ debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@3.1.0: debug@3.1.0, debug@~3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" resolved "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE= integrity sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@4, debug@^4.1.0, debug@^4.1.1: debug@4, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0:
version "4.1.1" version "4.1.1"
resolved "https://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" resolved "https://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E= integrity sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=
...@@ -3117,6 +3174,11 @@ detect-node@^2.0.4: ...@@ -3117,6 +3174,11 @@ detect-node@^2.0.4:
resolved "https://registry.npm.taobao.org/detect-node/download/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" resolved "https://registry.npm.taobao.org/detect-node/download/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha1-AU7o+PZpxcWAI9pkuBecCDooxGw= integrity sha1-AU7o+PZpxcWAI9pkuBecCDooxGw=
diff-match-patch@^1.0.0:
version "1.0.4"
resolved "https://registry.npm.taobao.org/diff-match-patch/download/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1"
integrity sha1-asS1UjdGN2HE2vDcYD64aRJHRLE=
diff@^4.0.1: diff@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.npm.taobao.org/diff/download/diff-4.0.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdiff%2Fdownload%2Fdiff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" resolved "https://registry.npm.taobao.org/diff/download/diff-4.0.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdiff%2Fdownload%2Fdiff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff"
...@@ -3383,6 +3445,34 @@ end-or-error@^1.0.1: ...@@ -3383,6 +3445,34 @@ end-or-error@^1.0.1:
resolved "https://registry.npm.taobao.org/end-or-error/download/end-or-error-1.0.1.tgz#dc7a6210fe78d372fee24a8b4899dbd155414dcb" resolved "https://registry.npm.taobao.org/end-or-error/download/end-or-error-1.0.1.tgz#dc7a6210fe78d372fee24a8b4899dbd155414dcb"
integrity sha1-3HpiEP5403L+4kqLSJnb0VVBTcs= integrity sha1-3HpiEP5403L+4kqLSJnb0VVBTcs=
engine.io-client@~3.4.0:
version "3.4.0"
resolved "https://registry.npm.taobao.org/engine.io-client/download/engine.io-client-3.4.0.tgz#82a642b42862a9b3f7a188f41776b2deab643700"
integrity sha1-gqZCtChiqbP3oYj0F3ay3qtkNwA=
dependencies:
component-emitter "1.2.1"
component-inherit "0.0.3"
debug "~4.1.0"
engine.io-parser "~2.2.0"
has-cors "1.1.0"
indexof "0.0.1"
parseqs "0.0.5"
parseuri "0.0.5"
ws "~6.1.0"
xmlhttprequest-ssl "~1.5.4"
yeast "0.1.2"
engine.io-parser@~2.2.0:
version "2.2.0"
resolved "https://registry.npm.taobao.org/engine.io-parser/download/engine.io-parser-2.2.0.tgz#312c4894f57d52a02b420868da7b5c1c84af80ed"
integrity sha1-MSxIlPV9UqArQgho2ntcHISvgO0=
dependencies:
after "0.8.2"
arraybuffer.slice "~0.0.7"
base64-arraybuffer "0.1.5"
blob "0.0.5"
has-binary2 "~1.0.2"
enhanced-resolve@^4.1.0: enhanced-resolve@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.npm.taobao.org/enhanced-resolve/download/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" resolved "https://registry.npm.taobao.org/enhanced-resolve/download/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f"
...@@ -4260,6 +4350,18 @@ has-ansi@^2.0.0: ...@@ -4260,6 +4350,18 @@ has-ansi@^2.0.0:
dependencies: dependencies:
ansi-regex "^2.0.0" ansi-regex "^2.0.0"
has-binary2@~1.0.2:
version "1.0.3"
resolved "https://registry.npm.taobao.org/has-binary2/download/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d"
integrity sha1-d3asYn8+p3JQz8My2rfd9eT10R0=
dependencies:
isarray "2.0.1"
has-cors@1.1.0:
version "1.1.0"
resolved "https://registry.npm.taobao.org/has-cors/download/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=
has-flag@^3.0.0: has-flag@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" resolved "https://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
...@@ -4647,11 +4749,21 @@ indent-string@^2.1.0: ...@@ -4647,11 +4749,21 @@ indent-string@^2.1.0:
dependencies: dependencies:
repeating "^2.0.0" repeating "^2.0.0"
indexdbwrapper@^1.0.4:
version "1.0.4"
resolved "https://registry.npm.taobao.org/indexdbwrapper/download/indexdbwrapper-1.0.4.tgz#a2dce8f7e67d89331e87726636fa67fb472f0b3e"
integrity sha1-otzo9+Z9iTMeh3JmNvpn+0cvCz4=
indexes-of@^1.0.1: indexes-of@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npm.taobao.org/indexes-of/download/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" resolved "https://registry.npm.taobao.org/indexes-of/download/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
indexof@0.0.1:
version "0.0.1"
resolved "https://registry.npm.taobao.org/indexof/download/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=
infer-owner@^1.0.3: infer-owner@^1.0.3:
version "1.0.4" version "1.0.4"
resolved "https://registry.npm.taobao.org/infer-owner/download/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" resolved "https://registry.npm.taobao.org/infer-owner/download/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
...@@ -5108,6 +5220,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: ...@@ -5108,6 +5220,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
resolved "https://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz?cache=0&sync_timestamp=1562592096220&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fisarray%2Fdownload%2Fisarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" resolved "https://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz?cache=0&sync_timestamp=1562592096220&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fisarray%2Fdownload%2Fisarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isarray@2.0.1:
version "2.0.1"
resolved "https://registry.npm.taobao.org/isarray/download/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=
isexe@^2.0.0: isexe@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" resolved "https://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
...@@ -5852,6 +5969,18 @@ moment@^2.24.0: ...@@ -5852,6 +5969,18 @@ moment@^2.24.0:
resolved "https://registry.npm.taobao.org/moment/download/moment-2.24.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmoment%2Fdownload%2Fmoment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" resolved "https://registry.npm.taobao.org/moment/download/moment-2.24.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmoment%2Fdownload%2Fmoment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s= integrity sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s=
monaco-editor-webpack-plugin@^1.8.1:
version "1.8.1"
resolved "https://registry.npm.taobao.org/monaco-editor-webpack-plugin/download/monaco-editor-webpack-plugin-1.8.1.tgz#5701bfce16c14c51503a607726d97fd92784af26"
integrity sha1-VwG/zhbBTFFQOmB3Jtl/2SeEryY=
dependencies:
loader-utils "^1.2.3"
monaco-editor@^0.19.0:
version "0.19.0"
resolved "https://registry.npm.taobao.org/monaco-editor/download/monaco-editor-0.19.0.tgz#c6e774210f3b292ff739e96f45a1cc78bf5ac2e7"
integrity sha1-xud0IQ87KS/3OelvRaHMeL9awuc=
move-concurrently@^1.0.1: move-concurrently@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" resolved "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
...@@ -6217,6 +6346,11 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: ...@@ -6217,6 +6346,11 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
resolved "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" resolved "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-component@0.0.3:
version "0.0.3"
resolved "https://registry.npm.taobao.org/object-component/download/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=
object-copy@^0.1.0: object-copy@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.npm.taobao.org/object-copy/download/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" resolved "https://registry.npm.taobao.org/object-copy/download/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
...@@ -6619,6 +6753,20 @@ parse5@^4.0.0: ...@@ -6619,6 +6753,20 @@ parse5@^4.0.0:
resolved "https://registry.npm.taobao.org/parse5/download/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" resolved "https://registry.npm.taobao.org/parse5/download/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
integrity sha1-bXhlbj2o14tOwLkG98CO8d/j9gg= integrity sha1-bXhlbj2o14tOwLkG98CO8d/j9gg=
parseqs@0.0.5:
version "0.0.5"
resolved "https://registry.npm.taobao.org/parseqs/download/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=
dependencies:
better-assert "~1.0.0"
parseuri@0.0.5:
version "0.0.5"
resolved "https://registry.npm.taobao.org/parseuri/download/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=
dependencies:
better-assert "~1.0.0"
parseurl@~1.3.2, parseurl@~1.3.3: parseurl@~1.3.2, parseurl@~1.3.3:
version "1.3.3" version "1.3.3"
resolved "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" resolved "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
...@@ -7894,6 +8042,11 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.3.0: ...@@ -7894,6 +8042,11 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" resolved "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0= integrity sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=
semver@^7.1.1:
version "7.1.1"
resolved "https://registry.npm.taobao.org/semver/download/semver-7.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.1.1.tgz#29104598a197d6cbe4733eeecbe968f7b43a9667"
integrity sha1-KRBFmKGX1svkcz7uy+lo97Q6lmc=
semver@~5.3.0: semver@~5.3.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.npm.taobao.org/semver/download/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" resolved "https://registry.npm.taobao.org/semver/download/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
...@@ -8322,6 +8475,35 @@ snyk@^1.231.0: ...@@ -8322,6 +8475,35 @@ snyk@^1.231.0:
uuid "^3.3.2" uuid "^3.3.2"
wrap-ansi "^5.1.0" wrap-ansi "^5.1.0"
socket.io-client@^2.3.0:
version "2.3.0"
resolved "https://registry.npm.taobao.org/socket.io-client/download/socket.io-client-2.3.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsocket.io-client%2Fdownload%2Fsocket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4"
integrity sha1-FNW6LgC5vNFFrkQ6uWs/hsvMG7Q=
dependencies:
backo2 "1.0.2"
base64-arraybuffer "0.1.5"
component-bind "1.0.0"
component-emitter "1.2.1"
debug "~4.1.0"
engine.io-client "~3.4.0"
has-binary2 "~1.0.2"
has-cors "1.1.0"
indexof "0.0.1"
object-component "0.0.3"
parseqs "0.0.5"
parseuri "0.0.5"
socket.io-parser "~3.3.0"
to-array "0.1.4"
socket.io-parser@~3.3.0:
version "3.3.0"
resolved "https://registry.npm.taobao.org/socket.io-parser/download/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f"
integrity sha1-K1KpalCf3zFEC6QP7WCUx9TxJi8=
dependencies:
component-emitter "1.2.1"
debug "~3.1.0"
isarray "2.0.1"
sockjs-client@1.3.0: sockjs-client@1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.npm.taobao.org/sockjs-client/download/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177" resolved "https://registry.npm.taobao.org/sockjs-client/download/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177"
...@@ -8947,6 +9129,11 @@ tmp@^0.1.0: ...@@ -8947,6 +9129,11 @@ tmp@^0.1.0:
dependencies: dependencies:
rimraf "^2.6.3" rimraf "^2.6.3"
to-array@0.1.4:
version "0.1.4"
resolved "https://registry.npm.taobao.org/to-array/download/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA=
to-arraybuffer@^1.0.0: to-arraybuffer@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.npm.taobao.org/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" resolved "https://registry.npm.taobao.org/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
...@@ -9430,6 +9617,14 @@ vue-cli-plugin-i18n@^0.6.0: ...@@ -9430,6 +9617,14 @@ vue-cli-plugin-i18n@^0.6.0:
vue-i18n "^8.0.0" vue-i18n "^8.0.0"
vue-i18n-extract "^0.4.13" vue-i18n-extract "^0.4.13"
vue-codemirror@^4.0.6:
version "4.0.6"
resolved "https://registry.npm.taobao.org/vue-codemirror/download/vue-codemirror-4.0.6.tgz#b786bb80d8d762a93aab8e46f79a81006f0437c4"
integrity sha1-t4a7gNjXYqk6q45G95qBAG8EN8Q=
dependencies:
codemirror "^5.41.0"
diff-match-patch "^1.0.0"
vue-draggable-resizable@^2.0.1: vue-draggable-resizable@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.npm.taobao.org/vue-draggable-resizable/download/vue-draggable-resizable-2.0.1.tgz#fb98d0997b1cfa8e3ba90f723cf8b605012cd96d" resolved "https://registry.npm.taobao.org/vue-draggable-resizable/download/vue-draggable-resizable-2.0.1.tgz#fb98d0997b1cfa8e3ba90f723cf8b605012cd96d"
...@@ -9769,6 +9964,13 @@ ws@^6.0.0, ws@^6.2.1: ...@@ -9769,6 +9964,13 @@ ws@^6.0.0, ws@^6.2.1:
dependencies: dependencies:
async-limiter "~1.0.0" async-limiter "~1.0.0"
ws@~6.1.0:
version "6.1.4"
resolved "https://registry.npm.taobao.org/ws/download/ws-6.1.4.tgz?cache=0&sync_timestamp=1576314828024&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fws%2Fdownload%2Fws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
integrity sha1-W1yIAK+rkl6UzLKdFTyNAsF3bvk=
dependencies:
async-limiter "~1.0.0"
xdg-basedir@^3.0.0: xdg-basedir@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npm.taobao.org/xdg-basedir/download/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" resolved "https://registry.npm.taobao.org/xdg-basedir/download/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
...@@ -9801,6 +10003,11 @@ xmlbuilder@~9.0.1: ...@@ -9801,6 +10003,11 @@ xmlbuilder@~9.0.1:
resolved "https://registry.npm.taobao.org/xmlbuilder/download/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" resolved "https://registry.npm.taobao.org/xmlbuilder/download/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
resolved "https://registry.npm.taobao.org/xmlhttprequest-ssl/download/xmlhttprequest-ssl-1.5.5.tgz?cache=0&sync_timestamp=1564594466893&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fxmlhttprequest-ssl%2Fdownload%2Fxmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
xregexp@2.0.0: xregexp@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npm.taobao.org/xregexp/download/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" resolved "https://registry.npm.taobao.org/xregexp/download/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
...@@ -9919,3 +10126,12 @@ yargs@^7.0.0: ...@@ -9919,3 +10126,12 @@ yargs@^7.0.0:
which-module "^1.0.0" which-module "^1.0.0"
y18n "^3.2.1" y18n "^3.2.1"
yargs-parser "^5.0.0" yargs-parser "^5.0.0"
yeast@0.1.2:
version "0.1.2"
resolved "https://registry.npm.taobao.org/yeast/download/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=
"zeroing-code-divider@http://gitlab2.dui88.com/laoqifeng/zeroing-code-divider.git":
version "1.0.0"
resolved "http://gitlab2.dui88.com/laoqifeng/zeroing-code-divider.git#15af443267c46e32e84af786e3f01ef06e7da237"
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
<module version="4"> <module version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/dist" />
</content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="jshint" level="application" />
</component> </component>
</module> </module>
\ No newline at end of file
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