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) {
var newName = layer.name.replace(mergeFlagReg, '');
layer.name = newName;
try {
layer.merge();
mergeLayers.push(newName);
}catch (e) {
layer.merge();
}
return 1;
} else if (layer.name.match(excludeFlagReg)) {
try {
layer.remove();
}catch (e) {
alert('移除图层' + layer.name + '失败,可能含有锁的层');
}
return 2;
}
return 0;
......
......@@ -4,26 +4,34 @@
"scripts": {
"serve": "vue-cli-service serve",
"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",
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'"
},
"dependencies": {
"codemirror": "^5.50.0",
"cookie": "^0.4.0",
"copy-to-clipboard": "^3.2.0",
"core-js": "^2.6.5",
"element-ui": "^2.4.5",
"indexdbwrapper": "^1.0.4",
"jszip": "^3.2.2",
"moment": "^2.24.0",
"monaco-editor": "^0.19.0",
"monaco-editor-webpack-plugin": "^1.8.1",
"path": "^0.12.7",
"querystringify": "^2.1.1",
"semver": "^7.1.1",
"socket.io-client": "^2.3.0",
"splitpanes": "^1.14.5",
"string-width": "^4.1.0",
"uuid": "^3.3.3",
"vue": "^2.6.10",
"vue-codemirror": "^4.0.6",
"vue-i18n": "^8.0.0",
"vue-router": "^3.0.3",
"vuex": "^3.0.1"
"vuex": "^3.0.1",
"zeroing-code-divider": "http://gitlab2.dui88.com/laoqifeng/zeroing-code-divider.git"
},
"devDependencies": {
"@kazupon/vue-i18n-loader": "^0.3.0",
......
......@@ -6,6 +6,9 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<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>
<body>
<noscript>
......@@ -14,8 +17,9 @@
</noscript>
<script>
window.__data = {
token : "<%= process.env.NODE_ENV === 'development' ? 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MjA4NSwibmFtZSI6IuWKs-eQquWzsCIsImFjY291bnQiOiJsYW9xaWZlbmciLCJlbWFpbCI6Imxhb3FpZmVuZ0BkdWliYS5jb20uY24iLCJtb2JpbGUiOiIxMzQ1Njc3NDE1MyIsImRpbmdVc2VySWQiOiI1MDY5MDYyOTIxMjkxMDAxIiwibW9kZWxBdXRob3JpemUiOmZhbHNlLCJsb2dpblRpbWVvdXQiOjc5MTUwLCJnbXRDcmVhdGUiOjE1MzQ0MTIwMTUwMDAsImdtdE1vZGlmaWVkIjoxNTczODgyMDk2MDAwLCJzeXN0ZW1JZFNldCI6WzAsMSwzNCwyMjcsMzYsMzcsMjMxLDIwMywxOSwyMSwyNDYsMjMsMjUsMjQ5LDI3LDMwXSwiaWF0IjoxNTc0NzU4OTk5fQ.tlRIqNZ3t1-5ZjWBuB7sASQuDBQ03gjfv0aDC_Ba_3k' : '$TOKEN$' %>",
apiHost : "<%= process.env.NODE_ENV === 'development' ? 'http://beacon.duibadev.com.cn' : '$API_HOST$' %>"
token : "<%= process.env.NODE_ENV === 'development' ? 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MjU0NywibmFtZSI6IuWNnum-meS6rSIsImFjY291bnQiOiJiaWFubG9uZ3RpbmciLCJlbWFpbCI6ImJpYW5sb25ndGluZ0BkdWliYS5jb20uY24iLCJtb2JpbGUiOiIxNTg2OTE3NDMxMSIsImRpbmdVc2VySWQiOiIwMTE1MTgyMTEyMjE3OTYzNzAiLCJtb2RlbEF1dGhvcml6ZSI6ZmFsc2UsImxvZ2luVGltZW91dCI6NzczNjEsImdtdENyZWF0ZSI6MTU1OTUyNjcxNDAwMCwiZ210TW9kaWZpZWQiOjE1NzM3NDk1OTEwMDAsInN5c3RlbUlkU2V0IjpbMCwxLDM0LDM2LDM3LDIzMSwyMDMsNDQsMTksMjEsMjQ2LDIzLDI0OSwyNSwyNywzMF0sImlhdCI6MTU3Nzk1MjgxNH0.GNeP25ZZK5ca7rvwtj2BaQ3GZatGNSR3CthSN98PSD4' : '$TOKEN$' %>",
apiHost : "<%= process.env.NODE_ENV === 'development' ? 'http://beacon.duibadev.com.cn' : '$API_HOST$' %>",
types: `$TYPES$`
}
</script>
<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 {
}
}
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',
contentType: 'json'
}) {
let url = API_HOST + uri;
let url = uri.startsWith('http') || uri.startsWith('//') ? uri : API_HOST + uri;
const options = {
method,
headers: {
authorization: 'Bearer ' + window['zeroing_token'],
},
headers: {},
//credentials: 'include',
};
if (auth) {
options.headers.authorization = 'Bearer ' + window['zeroing_token'];
}
if (params) {
if (method.toLowerCase() === 'post') {
switch(contentType){
switch (contentType) {
case 'form-data':
let formData = new FormData();
for (let key in params) {
let value = params[key];
if(value instanceof File){
if (value instanceof File) {
formData.append(key, value, value.name);
}else{
} else {
formData.append(key, value);
}
}
......@@ -59,7 +60,8 @@ export async function fetchApi(uri, {params, method = 'get', contentType = 'json
break;
}
} else {
url += (url.indexOf('?') < 0 ? '?' : '') + (url.endsWith('&') ? '' : '&') + stringify(params);
url += (url.indexOf('?') < 0 ? '?' : '');
url += (url.endsWith('?') ? '' : '&') + stringify(params);
}
}
......@@ -70,9 +72,13 @@ export async function fetchApi(uri, {params, method = 'get', contentType = 'json
const jsonObj = await response.json();
//console.log(jsonObj);
if (judgeSuccess) {
if (jsonObj.success) {
return jsonObj.data;
}
} else {
return jsonObj;
}
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 @@
import {fetchApi} from "./common";
export async function fetchAll() {
export async function fetchAll(currentPage, pageSize, onlyMine) {
return await fetchApi('/api/project/query', {
params: {currentPage, pageSize, isAll: onlyMine ? 0 : 1},
errMessage: 'Failed to fetch projects',
})
}
......@@ -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', {
params: project,
method: 'post',
......@@ -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', {
params: {id},
params: {id, debug, packedAssets},
method: 'post',
errMessage: 'Failed to pack project',
})
......@@ -73,11 +91,13 @@ export async function importView(file) {
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', {
params: {
file,
},
params,
method: 'post',
contentType: 'form-data',
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 @@
<div class="menu">
<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">
{{item.text}}
{{item.label || ''}}
</el-link>
</el-badge>
</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>
<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>
<slot></slot>
</div>
......@@ -30,12 +30,21 @@ export default {
properties: {
type: Object,
default: () => {}
},
view: {
type: Object,
default: () => {}
}
},
computed: {
selfText() {
return this.properties ? this.properties.text || '' : '';
}
},
methods: {
clickViewHandle(data) {
this.$store.dispatch('activeComponent', {data, fromPlayground: true});
}
}
};
</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 @@
placement="top"
trigger="hover"
width="auto"
:disabled="!legalUrl"
:content="url"
>
<img style="max-width: 200px;" v-if="url" :src="url" alt="" />
......@@ -29,7 +30,6 @@ export default {
methods: {
drop(e) {
if (this.$store.state.project.dragUUID) {
// debugger;
console.log('native drop', this.$store.state);
this.swvalue = `asset://${this.$store.state.project.dragUUID}`
}
......@@ -51,6 +51,9 @@ export default {
} else {
return '';
}
},
legalUrl: function() {
return (this.swvalue + '').indexOf('//') > -1;
}
}
};
......
......@@ -5,7 +5,9 @@
export let API_HOST;
if (process.env.NODE_ENV === 'development') {
//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 = window.__data.apiHost;
//API_HOST = '';
......@@ -21,6 +23,9 @@ export const SSO_VERIFY_PAGE_URL = '/sso/logout';
export const DOCK_POINT_OFFSET = 4;
export const PROJECT_PAGE_SIZE = 20;
export const HISTORY_PAGE_SIZE = 20;
//文件类型图标 t表示展示缩略图
export const fileTypeIcon = {
'': 'file-empty',
......@@ -28,6 +33,7 @@ export const fileTypeIcon = {
'.json': 'file-text',
'.zip': 'file-zip',
'.fnt': 'file-font',
'.mp3': 'file-music',
'.jpg': 't',
'.jpeg': 't',
......
......@@ -5,28 +5,45 @@
"Close": "Close",
"Still Close": "Still Close",
"Save": "Save",
"Save And Preview": "Save And Preview",
"Save And Close": "Save And Close",
"Reset": "Reset",
"Copy": "Copy",
"Assets config": "Assets config",
"Paste same level":"Paste(same level)",
"Paste child":"Paste(child)",
"Exit": "Exit",
"Props": "Props",
"Behavior": "Behavior",
"Only mine": "Only mine",
"Add": "Add",
"Delete": "Delete",
"Delete all": "Delete all",
"Not delete": "Not delete",
"Import": "Import",
"Export": "Export",
"Importing project": "Importing project",
"Exporting project": "Exporting project",
"Upload": "Upload",
"Uploading": "Uploading",
"Edit": "Edit",
"EditEnv": "EditEnv",
"EditCustomModule": "EditCustomModule",
"Script": "Script",
"CodeSyncServe": "CodeSyncServe",
"ID": "ID",
"Mock Editor": "Mock Editor",
"Enable mock serve": "Enable mock serve",
"Name": "Name",
"Alias": "Alias",
"Output": "Output",
"Code": "Code",
"Desc": "Desc",
"Empty": "Empty",
"Path": "Path",
"Timeout": "Timeout",
"SuccessRatio": "SuccessRatio",
"Data": "Data",
"Key": "Key",
"Default": "Default",
"Event": "Event",
......@@ -53,6 +70,8 @@
"Packing": "Packing",
"Type": "Type",
"Group": "Group",
"Select from history": "Select from history",
"Meta Search": "Meta Search",
"Access denied": "Access denied",
"Invalid router": "Invalid router",
"Jump after": "Jump after {cd}s",
......@@ -75,9 +94,18 @@
"Env editor": "Env editor",
"As inline": "As inline",
"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",
"Custom module": "Custom module",
"Custom module asset mapping": "Custom module asset mapping",
"Copy template to clipboard": "Copy template to clipboard",
"Copied process 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 +119,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 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 +127,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 version remark",
"Input view name": "Input view name",
"Invalid view name": "Invalid view name",
"Unsaved version found locally": "Unsaved version found locally, please select version to open",
......@@ -105,6 +135,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 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 +158,40 @@
"Import multi": "Import multi",
"Import view success": "Import view success",
"menu": {
"save": "Save",
"details": "Details",
"preview": "Preview",
"pack": "Pack",
"exit": "Exit",
"undo": "Undo",
"redo": "Redo"
"save": {
"label": "Save"
},
"details": {
"label": "Details"
},
"preview-fast": {
"label": "Preview"
},
"publish": {
"label": "Publish"
},
"mock": {
"label": "Mock"
},
"exit": {
"label": "Exit"
},
"undo": {
"icon": "icon-undo"
},
"redo": {
"icon": "icon-redo"
}
},
"view_node_menu": {
"node": "Node",
"image": "Image",
"label": "Label",
"bitmapText": "BitmapText",
"rect": "Rect",
"scrollView": "ScrollView"
"scrollView": "ScrollView",
"scrollList": "ScrollList",
"svga": "SVGA"
},
"panes": {
"Assets": "Assets",
......@@ -165,11 +216,16 @@
"dynamic": "Dynamic",
"map": "Map"
},
"nodeFilterPresets": {
"trigger": "Behavior Trigger",
"type": "Node Type"
},
"events": {
"init": "Init",
"awake": "Awake",
"sleep": "Sleep",
"data-center": "DataCenter",
"update-data": "UpdateData",
"click": "Click",
"touchstart": "Touchstart",
"touchend": "Touchend",
......
......@@ -5,28 +5,45 @@
"Close": "关闭",
"Still Close": "仍然关闭",
"Save": "保存",
"Save And Preview": "保存并预览",
"Save And Close": "保存并关闭",
"Reset": "重置",
"Copy": "复制",
"Assets config": "素材配置",
"Paste same level":"粘贴(同级)",
"Paste child":"粘贴(子级)",
"Exit": "退出",
"Props": "属性",
"Behavior": "行为",
"Only mine": "仅显示我的",
"Add": "添加",
"Delete": "删除",
"Delete all": "删除全部",
"Not delete": "不删除",
"Import": "导入",
"Export": "导出",
"Importing project": "项目导入中",
"Exporting project": "项目导出中",
"Upload": "上传",
"Uploading": "上传中",
"Edit": "编辑",
"EditEnv": "编辑环境",
"EditCustomModule": "编辑自定义模块",
"Script": "脚本",
"CodeSyncServe": "代码同步服务",
"ID": "ID",
"Mock Editor": "Mock编辑器",
"Enable mock serve": "启用Mock服务",
"Name": "名字",
"Alias": "别名",
"Output": "输出",
"Code": "代码",
"Desc": "描述",
"Empty": "空",
"Path": "路径",
"Timeout": "超时(ms)",
"SuccessRatio": "成功率",
"Data": "数据",
"Key": "属性名",
"Default": "默认值",
"Event": "事件",
......@@ -49,10 +66,13 @@
"Saving": "保存中…",
"Divider": "分流节点",
"Custom": "自定义节点",
"Custom event": "自定义事件",
"Entry": "入口",
"Packing": "打包",
"Type": "类型",
"Group": "分组",
"Select from history": "打开历史版本",
"Meta Search": "过程元查找",
"Access denied": "无权限",
"Invalid router": "无效的页面",
"Jump after": "{cd}秒后跳转",
......@@ -75,9 +95,21 @@
"Env editor": "环境编辑器",
"As inline": "作为内联",
"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": "自定义常量",
"Custom module": "自定义模块",
"Custom module asset mapping": "自定义模块素材映射",
"Copy template to clipboard": "复制模板到粘贴板",
"Copied process to clipboard": "复制过程到粘贴板",
"Link to parent": "连接到父节点",
"Input project name": "输入项目名",
"Invalid project name": "无效的项目名",
......@@ -91,6 +123,7 @@
"Fetching projects": "获取项目列表…",
"Failed to fetch env": "获取环境失败",
"Failed to fetch projects": "获取项目列表失败",
"Failed to fetch history": "获取历史记录失败",
"Failed to fetch project": "获取项目失败",
"Failed to create project": "创建项目失败",
"Failed to duplicate project": "复制项目失败",
......@@ -98,6 +131,7 @@
"Failed to get project": "获取项目失败",
"Failed to save project": "保存项目失败",
"Save project successfully": "保存项目成功",
"Input version remark": "输入版本备注",
"Input view name": "输入视图名",
"Invalid view name": "无效的视图名",
"Unsaved version found locally": "本地发现了未保存保本,请选择版本打开",
......@@ -105,6 +139,7 @@
"Remote Version": "远程版本",
"Confirm to exit the editor": "确定退出编辑器吗?",
"Confirm to publish": "确定发布吗?",
"Are you sure to delete it's process": "确定删除这对应的过程吗",
"Are you sure to delete this asset": "确定删除这个素材吗",
"Are you sure to delete all assets": "确定删除全部素材吗",
"Are you sure to delete this item": "确定删除这一项吗",
......@@ -127,20 +162,42 @@
"Import multi": "导入多",
"Import view success": "视图导入成功",
"menu": {
"save": "保存",
"details": "详情",
"preview": "预览",
"pack": "发布",
"exit": "退出",
"undo": "撤销",
"redo": "重做"
"save": {
"label": "保存"
},
"details": {
"label": "详情"
},
"preview-fast": {
"label": "预览"
},
"publish": {
"label": "发布"
},
"mock": {
"label": "Mock"
},
"exit": {
"label": "退出"
},
"undo": {
"icon": "icon-undo"
},
"redo": {
"icon": "icon-redo"
}
},
"view_node_menu": {
"node": "节点",
"image": "图片",
"label": "标签",
"label": "文本",
"bitmapText": "位图文本",
"rect": "矩形",
"scrollView": "滚动视图"
"circle": "圆形",
"textinput": "输入框",
"scrollView": "滚动视图",
"scrollList": "滚动列表",
"svga": "SVGA"
},
"panes": {
"Assets": "素材",
......@@ -165,11 +222,16 @@
"dynamic": "动态",
"map": "字典"
},
"nodeFilterPresets": {
"trigger": "行为触发",
"type": "节点类型"
},
"events": {
"init": "初始化",
"awake": "激活",
"sleep": "入眠",
"data-center": "数据中心",
"update-data": "更新数据",
"click": "触摸点击",
"touchstart": "触摸按下",
"touchend": "触摸弹起",
......
......@@ -18,5 +18,10 @@ export default new Router({
name: 'editor',
component: () => import('./views/Editor.vue')
},
{
path: '/preview/:projectID',
name: 'preview',
component: () => import('./views/Preview.vue')
},
]
})
......@@ -29,24 +29,21 @@ export default new Vuex.Store({
plugins: [
SaveToLocalPlugin({
mutationTypes: [
'makeDirty',
'modifyProject',
'addNode',
'deleteNode',
'copyNode',
'addAsset',
'deleteAsset',
'deleteAllAssets',
'modifyAsset',
'importView',
'importAssets',
'addDataMapping',
'deleteDataMapping',
'modifyDataMapping',
'addEnvMapping',
'deleteEnvMapping',
'modifyEnvMapping',
'modifyActiveView',
'behavior_save',
'modifyCustoms',
'modifyProjectDetails',
'modifyMocks',
]
})
]
......
......@@ -8,6 +8,7 @@ import Vue from "vue";
import i18n from "../../i18n";
import generateUUID from "uuid/v4";
import {clonePureObj, metaInUse, updateProcesses} from "../../utils";
import {arrayFind} from "element-ui/src/utils/util";
export const behaviorStore = {
state: {
......@@ -15,8 +16,7 @@ export const behaviorStore = {
currentBehavior: null,
processContext: [],
originData: null,
originBehaviors: null,
behaviors: null,
currentProcess: null,
processStack: [],
editable: false,
......@@ -29,53 +29,20 @@ export const behaviorStore = {
},
},
mutations: {
behavior_startEdit(state, {originData, behaviors, event}) {
behavior_startEdit(state, {originData, behavior}) {
state.originData = originData;
state.originBehaviors = behaviors;
state.behaviors = clonePureObj(behaviors);
state.data = clonePureObj(originData);
if (state.behaviors.length > 0) {
state.currentBehavior = state.behaviors[0];
} else {
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,
}
},
state.currentBehavior = clonePureObj(behavior);
},
});
}
behavior_save(state) {
state.originData.processes = state.data.processes;
},
addProcessMeta(state, meta) {
state.data.processes.push(meta);
return process;
},
behavior_save(state) {
state.originData.processes = state.data.processes;
state.originBehaviors[0] = state.currentBehavior;
},
updateProcesses(state, {targetMetaID, replaceMetaID}) {
for (let process of state.data.processes) {
updateProcesses(process, targetMetaID, replaceMetaID);
}
updateProcesses(state.data.processes, targetMetaID, replaceMetaID);
},
deleteProcessMeta(state, {meta, process}) {
let container;
......@@ -85,27 +52,23 @@ export const behaviorStore = {
container = state.data.processes;
}
if (container) {
for (let i = 0, li = container.length; i < li; i++) {
const process = container[i];
if (process.id === meta.id) {
container.splice(i, 1);
break;
}
}
deleteProcessMeta(meta.id, container);
}
},
clearProcessStack(state) {
state.processStack.splice(0);
updatePropsEditable(state);
state.currentProcess = null;
},
pushProcessStack(state, process) {
state.processStack.push(process);
updatePropsEditable(state);
state.currentProcess = process;
},
popProcessStack(state, index) {
state.processStack.splice(index);
updatePropsEditable(state);
state.currentProcess = state.processStack[state.processStack.length - 1];
},
setScale(state, value) {
let scale = state.drawState.boardScale;
......@@ -153,6 +116,10 @@ export const behaviorStore = {
}
},
actions: {
behavior_save({commit, state}) {
commit('behavior_save');
return state.currentBehavior;
},
addCustomProcessMeta({commit, state}, {masterProcess, isInline, processId}) {
let meta = {
id: generateUUID(),
......@@ -175,7 +142,7 @@ export const behaviorStore = {
addProcessMeta(commit, isInline, masterProcess, meta);
return meta;
},
addProcessFromPrefab({commit, state}, {masterProcess, isInline, meta: pMeta}){
addProcessFromPrefab({commit, state}, {masterProcess, isInline, meta: pMeta}) {
let meta = clonePureObj(pMeta);
meta.id = generateUUID();
meta.isInline = isInline;
......@@ -183,7 +150,10 @@ export const behaviorStore = {
addProcessMeta(commit, isInline, masterProcess, meta);
return meta;
}
},
searchMeta({state}, {keyword}) {
searchMeta(state.data.processes, keyword);
},
}
};
......@@ -191,7 +161,7 @@ function updatePropsEditable(state) {
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 (!masterProcess.meta.metas) {
Vue.set(masterProcess.meta, 'metas', []);
......@@ -201,3 +171,53 @@ function addProcessMeta(commit, isInline, masterProcess, 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 @@
import {envApi} from "../../api";
import i18n from "../../i18n";
const storeKey = 'code-sync-serve-config';
export const envStore = {
state: {
initialized: false,
name: 'Zeroing Editor',
version: '1.0.1',
version: '1.0.3',
templates: {
builtin: ['blank'],
custom: [],
......@@ -19,6 +21,10 @@ export const envStore = {
processes: [],
scripts: [],
customs: [],
codeSyncServeConfig: {
ip: 'localhost',
port: 7788,
}
},
mutations: {
updateEnv(state, env) {
......@@ -28,6 +34,15 @@ export const envStore = {
state.customs = parseItem(state.customs);
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: {
......@@ -35,8 +50,14 @@ export const envStore = {
return groupProcesses(state.processes, process => process.isPrefab);
},
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 = {
id: 'divider',
name: i18n.t('Divider'),
......@@ -71,7 +92,7 @@ export const envStore = {
const env = await envApi.fetchEnv();
commit('updateEnv', env);
}
}
},
}
};
......
This diff is collapsed.
......@@ -4,30 +4,43 @@
import {projectApi} from "../../api";
export const projectsStore = {
state: [],
state: {
projectCount: 0,
projects: [],
},
mutations: {
updateProjects(state, projects) {
state.splice(0);
state.push(...projects);
updateProjects(state, {projects, projectCount}) {
state.projects.splice(0);
state.projects.push(...projects);
state.projectCount = projectCount;
},
addProject(state, project) {
state.unshift(project);
state.projects.unshift(project);
},
deleteProject(state, projectID) {
for (let i = 0, li = state.length; i < li; i++) {
const item = state[i];
const {projects} = state;
for (let i = 0, li = projects.length; i < li; i++) {
const item = projects[i];
if (item.id === projectID) {
state.splice(i, 1);
projects.splice(i, 1);
break;
}
}
},
},
actions: {
async updateProjects({commit}) {
const projects = await projectApi.fetchAll();
commit('updateProjects', projects);
async fetchProjects({commit, state}, {currentPage, pageSize, onlyMine}) {
const data = await projectApi.fetchAll(currentPage, pageSize, onlyMine);
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) {
const project = await projectApi.createOne(data);
......
......@@ -3,7 +3,7 @@
*/
export const template =
`<!DOCTYPE html>
`<!DOCTYPE html>
<html lang="en">
<head>
......@@ -28,16 +28,17 @@ content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, u
overflow: hidden;
position: absolute;
background-color: white;
}
</style>
}
</style>
</head>
<body>
<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>
engine.launch('//yun.duiba.com.cn/aurora/$VERSION$-data.json');
</script>
</script>
</body>
</html>
`;
......@@ -54,7 +54,18 @@
.assets-scrollbar {
flex: 1;
.file-list {
}
}
.scrollbar-view{
padding-bottom: 5px;
}
}
}
.file-list {
display: flex;
flex: 1;
flex-direction: row;
......@@ -67,11 +78,11 @@
&:hover {
& > .icon > .operate-bar {
display: block;
visibility: visible;
}
& > .name > .full-name {
display: block;
visibility: visible;
}
}
......@@ -93,14 +104,18 @@
}
.operate-bar {
display: none;
visibility: hidden;
position: absolute;
right: 1px;
bottom: 1px;
display: flex;
.el-button + .el-button{
margin-left: 2px;
}
.el-button {
padding: 2px;
margin-left: 2px;
}
}
}
......@@ -121,7 +136,7 @@
}
.full-name {
display: none;
visibility: hidden;
position: absolute;
top: 0;
left: 0;
......@@ -155,15 +170,6 @@
text-align: center;
}
}
}
}
}
.scrollbar-view{
padding-bottom: 5px;
}
}
}
.assets-show {
......
......@@ -2,15 +2,7 @@
$dock-pin-width: 9px;
.behavior-editor-dialog {
display: flex;
flex-direction: column;
.el-dialog__body {
flex: 1;
padding: 5px;
}
.process-pane {
......@@ -35,6 +27,13 @@ $dock-pin-width: 9px;
text-overflow: ellipsis;
overflow: hidden;
font-size: 14px;
.current-node {
background-color: $--color-text-secondary;
color: $--color-white;
border-radius: 5px;
padding: 0 2px;
}
}
.edit-button {
......@@ -56,14 +55,28 @@ $dock-pin-width: 9px;
display: flex;
flex-direction: column;
.el-button {
padding: 3px;
margin-top: 2px;
margin-right: 2px;
}
.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;
border-bottom: 1px solid $--border-color-light;
......@@ -100,6 +113,7 @@ $dock-pin-width: 9px;
visibility: visible;
}
}
/*&:hover {
border-color: $block-border-hover-background-color;
......@@ -126,6 +140,10 @@ $dock-pin-width: 9px;
align-self: flex-end;
visibility: hidden;
.el-link {
padding: 3px 2px 2px 2px;
}
.el-link + .el-link {
margin-left: 5px;
}
......@@ -141,7 +159,7 @@ $dock-pin-width: 9px;
color: white;
display: flex;
i{
i {
display: block;
//color: $--color-warning;
margin-right: 3px;
......@@ -299,8 +317,31 @@ $dock-pin-width: 9px;
.scrollbar-view {
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 {
display: flex;
justify-content: flex-end;
......@@ -320,8 +361,9 @@ $dock-pin-width: 9px;
}
.meta-editor-wrapper {
height: 40vh;
//height: 40vh;
display: flex;
height: 100%;
padding: 5px;
flex-direction: column;
......@@ -332,6 +374,15 @@ $dock-pin-width: 9px;
.script-editor {
flex: 1;
}
.vue-codemirror {
flex: 1;
height: 0;
}
.CodeMirror {
height: 100%;
}
}
.props-editor-dialog {
......@@ -389,3 +440,23 @@ $dock-pin-width: 9px;
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 @@
@import "views";
@import "assets";
@import "behavior";
@import "bottom-bar";
.editor {
display: flex;
......@@ -25,10 +26,6 @@
}
}
.file-upload-indicator{
color: $--color-text-secondary;
}
.right-part {
color: $--color-text-regular;
line-height: 15px;
......@@ -41,6 +38,7 @@
.pane-container {
flex: 1;
height: 0;
}
}
......@@ -56,11 +54,11 @@
align-self: flex-start;
}
.bottom-bar{
.bottom-bar {
align-self: flex-end;
}
.list{
.list {
display: flex;
flex-direction: column;
flex: 1;
......@@ -100,21 +98,46 @@
}
.details-dialog {
.operate-bar{
.operate-bar {
display: flex;
justify-content: flex-end;
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 {
height: 40vh;
height: 100%;
.view {
padding-right: 10px;
.custom-module-list{
.custom-module-list {
display: flex;
flex-direction: column;
.item + .item {
margin-top: 5px;
}
......@@ -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 @@
.editor-name {
font-size: 50px;
color: $--color-primary;
margin: 10px 0;
}
.editor-version {
......@@ -25,6 +26,7 @@
.project-list {
flex: 1;
margin-bottom: 10px;
height: 0;
}
.project-info {
......@@ -43,4 +45,32 @@
.project-detail {
}
.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.
*/
@import "./var.scss";
@import "./base.scss";
@import "./home.scss";
@import "./editor.scss";
@import "./playground.scss";
@import "./inspector.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 {
background: transparent;
}
......@@ -23,15 +36,18 @@
overflow-y: hidden;
}
.el-tree-node__content > .el-tree-node__expand-icon{
.el-tree-node__content > .el-tree-node__expand-icon {
padding: 6px 0;
}
.el-input-number.is-without-controls .el-input__inner{
padding: 0 5px;
}
.el-input__inner {
padding: 0 5px;
}
.el-input--suffix .el-input__inner{
.el-input--suffix .el-input__inner {
padding-right: 5px;
}
......@@ -43,13 +59,13 @@
height: 100%;
}
.el-tabs--border-card > .el-tabs__content{
.el-tabs--border-card > .el-tabs__content {
padding: 5px 0 5px 5px;
}
.el-input-number.is-controls-right .el-input__inner {
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 {
......@@ -60,3 +76,12 @@
padding-left: 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 {
flex: 1;
border: 0 !important;
......@@ -6,6 +8,12 @@
&>:last-child{
flex: 1;
height: 0;
}
.el-collapse-item__header{
height: 25px;
line-height: 25px;
}
.el-tabs__item {
......@@ -45,12 +53,93 @@
}
}
.zero-inspector-behavior-form {
.zero-inspector-behavior {
height: 100%;
display: flex;
flex-direction: column;
.add-trigger{
align-self: flex-start;
}
.scrollbar{
margin-top: 5px;
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 @@
&::-webkit-scrollbar {
display: none;
}
.active {
border: 2px dashed rgb(20, 100, 206);
}
}
// .zero-playground-draw-panel{
// min-height: 1200px;
......@@ -60,6 +64,3 @@
cursor: default;
user-select: none;
}
.active {
border: 2px dashed rgb(20, 100, 206);
}
This diff is collapsed.
import _ from 'lodash';
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 = {
x: 'add',
......@@ -66,7 +39,7 @@ const attrShortMapper = {
// 编辑时想拖拽组件需要生成的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'];
// 属性单位 对照表, 如果是数值的时候需要添加单位
......@@ -107,7 +80,7 @@ function getParentCmps(uuid, list) {
return _self.parent ? parentLoop(_self.parent, list) : [];
}
export { getParentCmps, completeSelfProps };
export {getParentCmps, completeSelfProps};
export const getCmpByUUID = function (uuid, views) {
if (!uuid) {
......@@ -207,10 +180,10 @@ export const styles = {
return ['transform', [`rotate(${value}deg)`]]; //`transform: rotate(${value}deg);`;
case 'background-image':
return ['background-image', [`url(${value})`]]; //`background-image: url(${value});background-position:center;background-size:contain;`;
// case 'scale-x':
// return ['transform', [`scaleX(${value})`]]; //`transform: scaleX(${value});`;
// case 'scale-y':
// return ['transform', [`scaleY(${value})`]]; //`transform: scaleY(${value});`;
case 'scale-x':
return ['transform', [`scaleX(${value})`]]; //`transform: scaleX(${value});`;
case 'scale-y':
return ['transform', [`scaleY(${value})`]]; //`transform: scaleY(${value});`;
case 'visible':
return ['display', [value ? 'block' : 'none']]; // `display: ${value ? 'block' : 'none'};`;
default:
......@@ -265,6 +238,11 @@ export const styles = {
});
}
if (component.type === 'image') {
cmpSelfProps.width = cmpSelfProps.width || cmpSelfProps.imageWidth;
cmpSelfProps.height = cmpSelfProps.height || cmpSelfProps.imageHeight;
}
// 获取拖拽组件的样式,只生成operatProps中的属性
if (onlyOpera) {
_.forIn(cmpSelfProps, (val, key) => {
......@@ -276,14 +254,16 @@ export const styles = {
// 如果节点的width/height未定义,则设置拖拽组件的width/height为0
cmpSelfProps.width = cmpSelfProps.width || 0;
cmpSelfProps.height = cmpSelfProps.height || 0;
}
if (cmpSelfProps.scaleX && cmpSelfProps.width) {
cmpSelfProps.width *= cmpSelfProps.scaleX;
delete cmpSelfProps.scaleX;
}
if (cmpSelfProps.scaleY && cmpSelfProps.height) {
cmpSelfProps.height *= cmpSelfProps.scaleY;
delete cmpSelfProps.scaleY;
}
}
// 把不需要参与样式计算的属性干掉
......@@ -291,10 +271,20 @@ export const styles = {
delete cmpSelfProps[prop];
});
if (component.type === 'label') {
if (component.type === 'label' || component.type === 'textinput') {
// 如果是label类型,把fillColor转换为color
cmpSelfProps.color = 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);
......@@ -305,38 +295,47 @@ export const styles = {
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) {
// 如果是circle类型,加圆角
result += 'border-radius: 50%;'
}
if (component.type === 'label' && !onlyOpera) {
// if (component.type === 'label' && !onlyOpera) {
// 如果是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);
return result;
}
}
export const getCmpProps = function (type) {
const cmpPropsCache = {};
export function getCmpProps(type) {
if (!type) {
return {}
} else {
let _nodeProps = _.cloneDeep(properties.node);
let _typeProps = {};
if (type !== 'node') {
_typeProps = _.cloneDeep(properties[type]);
let cmpProps = cmpPropsCache[type];
if (cmpProps) {
cmpProps = _.cloneDeep(cmpProps);
} 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 = {
..._nodeProps,
..._typeProps
};
let result = Object.assign({}, ...inherits);
cmpPropsCache[type] = result;
delete result.base;
delete result.groupName;
}
return result
return cmpProps;
}
}
/**
* 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) {
})
}
export function playWaiting(promise, text) {
export function playWaiting(promise, text, closeLoading = true) {
const loading = Loading.service({
lock: true,
text: text || i18n.t('In processing'),
......@@ -24,7 +24,9 @@ export function playWaiting(promise, text) {
messageError(e);
throw e;
}).finally(() => {
if (closeLoading) {
loading.close();
}
})
}
......@@ -45,37 +47,22 @@ export function strEllipsis(str, maxLength = 0, rightOffset = 0) {
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) {
return property ? property.hasOwnProperty('default') ? property.default + '' : 'unset' : 'unset';
}
export function updateProcesses(process, targetMetaID, replaceMetaID) {
export function updateProcesses(processes, targetMetaID, replaceMetaID) {
for (let process of processes) {
for (let key in process.sub) {
let subProcess = process.sub[key];
if (subProcess.meta === targetMetaID) {
subProcess.meta = replaceMetaID;
}
}
if (process.metas) {
updateProcesses(process.metas, targetMetaID, replaceMetaID)
}
}
}
export function metaInUse(process, targetMetaID) {
......@@ -94,6 +81,26 @@ export function clonePureObj(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) {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
......@@ -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');
input.type = 'file';
input.onchange = function (e) {
callback(input.files);
};
if(acceptDirectory){
input.webkitdirectory = true;
}
if (accept) {
input.accept = accept;
}
......@@ -123,3 +157,27 @@ export function selectFile(callback, {accept, multiple} = {}) {
}
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 @@
export default {
node: {
groupName: '基础',
visible: {
title: '是否可见',
type: 'switch',
props: {
width: 40
},
value: true
},
x: {
title: 'x坐标',
type: 'inputNumber',
......@@ -47,13 +55,13 @@ export default {
title: '水平偏移',
type: 'inputNumber',
value: undefined,
desc: '相对于父元素中心点的水平偏移,0为正中心'
//desc: '相对于父元素中心点的水平偏移,0为正中心'
},
verticalCenter: {
title: '垂直偏移',
type: 'inputNumber',
value: undefined,
desc: '相对于父元素中心点的垂直偏移,0为正中心'
//desc: '相对于父元素中心点的垂直偏移,0为正中心'
},
rotation: {
title: '旋转',
......@@ -117,36 +125,57 @@ export default {
},
value: true
},
visible: {
title: '是否可见',
type: 'switch',
props: {
width: 40
},
value: true
}
},
label: {
base: 'node',
groupName: '文本',
text: {
title: '文本内容',
type: 'textArea',
value: ''
},
htmlText: {
title: 'HTML内容',
type: 'input',
value: ''
},
bold: {
title: '粗体',
type: 'switch',
props: {
width: 40
},
value: false,
},
fillColor: {
title: '颜色',
type: 'colorPicker',
value: '#000'
},
lineType: {
title: '自动换行',
type: 'select',
options: [
{ label: '单行', value: 'single' },
{ label: '多行', value: 'multi' }
],
value: 'single'
},
size: {
title: '字体大小',
type: 'swSelect',
type: 'inputNumber',
/*type: 'swSelect',
props: {
optionType: 'fontSize'
},
},*/
value: 12
},
lineHeight: {
title: '行高',
type: 'inputNumber',
value: 1
},
textAlign: {
title: '文本对齐',
type: 'select',
......@@ -161,23 +190,129 @@ export default {
title: '纵向对齐',
type: 'select',
options: [
{ label: '靠上', value: 'top' },
{ label: '靠上', value: 'up' },
{ label: '居中', value: 'middle' },
{ label: '靠下', value: 'bottom' }
{ label: '靠下', value: 'down' }
],
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: {
groupName: '来源',
base: 'node',
groupName: '图片',
source: {
title: '来源',
type: 'source',
value: ''
},
imageWidth: {
title: '原图宽度',
type: 'input',
value: '',
desc: '原图片的宽度',
props: {
disabled: true
}
},
rect: {
groupName: '矩形',
imageHeight: {
title: '原图高度',
type: 'input',
value: '',
desc: '原图片的高度',
props: {
disabled: true
}
}
},
svga: {
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: {
title: '填充色',
type: 'colorPicker',
......@@ -195,7 +330,11 @@ export default {
props: {
min: 0
}
}
},
rect: {
base: 'shape',
groupName: '矩形',
borderRadius: {
title: '圆角',
type: 'inputNumber',
......@@ -206,35 +345,20 @@ export default {
}
},
circle: {
base: 'shape',
groupName: '圆形',
fillColor: {
title: '填充色',
type: 'colorPicker',
value: '#fff'
},
strokeColor: {
title: '边框颜色',
type: 'colorPicker',
value: '#000'
},
strokeWidth: {
title: '边框宽度',
type: 'inputNumber',
value: 0,
props: {
min: 0
}
}
},
scrollView: {
base: 'node',
groupName: '滚动视图',
isVertical: {
title: '纵向锁定',
type: 'switch',
props: {
width: 40
},
value: true,
direction: {
title: '滚动方向',
type: 'select',
options: [
{ label: '纵向', value: 'vertical' },
{ label: '横向', value: 'horizontal' }
],
value: 'vertical',
},
isSpringBack: {
title: '回弹效果',
......@@ -244,11 +368,6 @@ export default {
},
value: true,
},
/*maxDistance: {
title: '最大距离',
type: 'inputNumber',
value: 1040,
},*/
maxSpeed: {
title: '最大速度',
type: 'inputNumber',
......@@ -259,5 +378,47 @@ export default {
type: 'inputNumber',
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 @@
<views splitpanes-min="20" :splitpanes-size="getSize(2, 0)"></views>
<playground splitpanes-min="20" :splitpanes-size="getSize(2, 1)"></playground>
</split-panes>
<assets splitpanes-min="20" :splitpanes-size="getSize(1, 1)"></assets>
<assets splitpanes-min="25" :splitpanes-size="getSize(1, 1)"></assets>
</split-panes>
<inspector splitpanes-min="20" :splitpanes-size="getSize(0, 1)"></inspector>
</split-panes>
<bottom-bar/>
<details-dialog ref="dialogsDialog"/>
<pack-result-dialog ref="packResultDialog"/>
<mock-editor-dialog ref="mockEditorDialog"/>
<code-sync-serve-dialog ref="codeSyncServeDialog"/>
</div>
</template>
<script>
import {mapGetters, mapActions} from 'vuex'
import {mapState, mapActions} from 'vuex'
import SplitPanes from 'splitpanes'
import ToolBar from "./Editor/ToolBar";
import Inspector from "./Editor/Inspector";
......@@ -25,13 +28,23 @@
import Playground from "./Editor/Playground";
import Assets from "./Editor/Assets";
import DetailsDialog from "./Editor/dialogs/DetailsDialog";
import {playWaiting} from "../utils";
import {openPreview, playWaiting} from "../utils";
import i18n from "../i18n";
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 {
name: 'Editor',
components: {
CodeSyncServeDialog,
BottomBar,
MockEditorDialog,
PackResultDialog,
DetailsDialog,
Assets,
......@@ -50,7 +63,7 @@
},
watch: {
$route: {
handler: function(val, oldVal){
handler: function (val, oldVal) {
console.log('router changed');
this.loadProject();
},
......@@ -58,26 +71,43 @@
}
},
computed: {
...mapGetters([]),
...mapState({
codeSyncServeConfig(state) {
return state.env.codeSyncServeConfig;
},
}),
},
async mounted() {
document.addEventListener('keydown', this.onKeyPress);
events.$on('save-and-preview', () => {
this.clickMenu("preview");
});
await playWaiting(this.prepare(), this.$t('Preparing')).catch(e => {});
events.$on('show-code-sync-serve-dialog', () => {
this.$refs.codeSyncServeDialog.show();
});
await playWaiting(this.prepare(), this.$t('Preparing')).catch(e => {
});
setTimeout(() => {
startCodeSyncServe(this.codeSyncServeConfig);
}, 100);
this.loadProject();
},
destroyed(){
destroyed() {
document.removeEventListener('keydown', this.onKeyPress)
},
methods: {
prepare(){
prepare() {
return Promise.all([
this.updateEnv(),
db.open('store'),
])
},
onKeyPress(e){
if(e.key === 's' && (e.ctrlKey||e.metaKey)){
onKeyPress(e) {
if (e.key === 's' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
this.saveProject();
......@@ -85,8 +115,8 @@
return false;
}
},
async loadProject(){
const {projectID} = this.$route.params;
async loadProject() {
const {projectID, project, dataUrl} = this.$route.params;
if (await this.localVersionExist(projectID)) {
this.$confirm(this.$t('Unsaved version found locally'), this.$t('Alert'), {
showClose: false,
......@@ -94,34 +124,35 @@
closeOnPressEscape: false,
confirmButtonText: this.$t('Local Version'),
cancelButtonText: this.$t('Remote Version'),
type: 'warning'
type: 'warning',
}).then(() => {
this.loadLocalVersion(projectID);
}).catch((e) => {
if (e === 'cancel') {
this.loadRemoteVersion(projectID);
this.loadRemoteVersion(projectID, project, dataUrl);
} else {
console.log(e);
}
});
} else {
this.loadRemoteVersion(projectID);
this.loadRemoteVersion(projectID, project, dataUrl);
}
},
loadLocalVersion(projectID) {
this.ready = false;
this.loadFromLocal(projectID);
this.$nextTick(()=>{
this.$nextTick(() => {
this.ready = true;
});
},
async loadRemoteVersion(projectID) {
async loadRemoteVersion(projectID, project, dataUrl) {
if (projectID) {
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'), {
confirmButtonText: this.$t('Confirm'),
callback: action=>{
callback: action => {
this.$router.replace({name: 'home'});
}
});
......@@ -139,12 +170,28 @@
this.panesConfig[id] = configs[0].width / 100;
localStorage.panesConfig = JSON.stringify(this.panesConfig);
},
async saveProject() {
await playWaiting(this.saveToRemote(), this.$t('Saving'));
async saveProject(closeLoading) {
let remark, cancel;
await this.$prompt(this.$t('Input version remark'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
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) {
switch (menuItem) {
......@@ -157,8 +204,18 @@
case 'details':
this.$refs.dialogsDialog.show();
break;
case 'pack':
await this.pack();
case 'preview':
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;
case 'undo':
this.$store.commit('undoRedo', 1);
......@@ -173,9 +230,9 @@
confirmButtonText: this.$t('Save'),
cancelButtonText: this.$t('Exit'),
type: 'warning'
}).then(() => {
}).then(async () => {
try {
this.saveProject();
await this.saveProject();
this.backToHome();
} catch (e) {
}
......@@ -188,20 +245,33 @@
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({
lock: true,
text: this.$t('Packing'),
});
try{
await this.saveProject();
const packResult = await this.packProject();
try {
const packResult = await this.packProject(debug);
this.$message({
message: this.$t('Pack project successfully'),
type: 'success',
duration: 1000,
duration: 500,
});
if (debug) {
openPreview(packResult);
} else {
this.$refs.packResultDialog.show(packResult);
}
/*this.$confirm(this.$t('Pack project successfully'), this.$t('Alert'), {
confirmButtonText: this.$t('Open in new tab'),
cancelButtonText: this.$t('Close'),
......@@ -213,7 +283,8 @@
}).catch(() => {
});*/
}catch (e) {
} catch (e) {
console.log(e);
this.$message({
message: this.$t('Pack project failed'),
type: 'error',
......@@ -225,14 +296,29 @@
backToHome() {
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([
'localVersionExist',
'loadFromLocal',
'loadFromRemote',
'loadFromDataUrl',
"saveToLocal",
"saveToRemote",
'updateEnv',
'packProject',
'savePreview',
'packImages',
])
}
}
......
......@@ -22,13 +22,11 @@
</div>
<el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view">
<div class="file-list">
<div class="file-uploader" @click="toUploadFile">
<asset-list editable @show-file-details="showFileDetails" @click-item="onItemClick">
<div slot="first" class="file-uploader" @click="toUploadFile" @dragover.prevent @drop="onDropFile">
<i class="el-icon-plus file-uploader-icon"></i>
</div>
<file-item v-for="(asset, index) in assets" :data="asset" :key="index" @show-file-details="showFileDetails"
@click="onItemClick(asset)"/>
</div>
</asset-list>
</el-scrollbar>
</div>
</div>
......@@ -37,16 +35,17 @@
</template>
<script>
import {mapState, mapMutations, mapActions} from 'vuex'
import {mapGetters, mapMutations, mapActions} from 'vuex'
import Pane from "../../components/Pane";
import FileItem from "./Assets/FileItem";
import AssetsShow from "./Assets/AssetsShow";
import SplitPanes from 'splitpanes'
import {selectFile} from "../../utils";
import {scanFiles, selectFile} from "../../utils";
import AssetList from "./Assets/AssetList";
export default {
name: "Assets",
components: {AssetsShow, FileItem, Pane, SplitPanes},
components: {AssetList, AssetsShow, FileItem, Pane, SplitPanes},
data() {
return {
showFields: ['url', 'uuid'],
......@@ -57,9 +56,7 @@
}
},
computed: {
...mapState({
assets: state => state.project.data.assets
}),
...mapGetters(['assets']),
},
mounted() {
this.currentItem = null;
......@@ -70,8 +67,25 @@
this.uploadFiles(files);
}, {multiple: true})
},
showFileDetails(file) {
this.$refs.assetsShow.show(file);
async onDropFile(e) {
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) {
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>
<div class="file-item" @click="$emit('click', $event)">
<div class="icon">
<i v-if="!showThumbnail" draggable="true" class="file-icon" :class="fileIcon"></i>
<img @dragstart="assetDragStart(data)" v-if="showThumbnail" draggable="true" class="thumbnail" :src="thumbnailUrl" alt="thumb" @dblclick="onDbclick()">
<div class="operate-bar">
<i @dragstart="assetDragStart(data)" v-if="!showThumbnail" draggable="true" class="file-icon"
:class="fileIcon"></i>
<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="danger" icon="el-icon-delete" @dblclick.native.stop
@click="onClickDelete"/>
......@@ -17,21 +22,20 @@
</template>
<script>
import {mapMutations} from "vuex";
import {mapMutations, mapActions} from "vuex";
import {fileTypeIcon} from "../../../config";
import {strEllipsis} from "../../../utils";
import {selectFile, strEllipsis} from "../../../utils";
export default {
name: "FileItem",
props: ['data'],
data() {
return {
}
props: {
data: {type: Object},
editable: {type: Boolean, default: false},
},
watch: {
data() {
return {}
},
watch: {},
computed: {
fileIcon() {
let icon = fileTypeIcon[this.data.ext];
......@@ -43,7 +47,7 @@
thumbnailUrl() {
return this.data.url;
},
ellipsisName(){
ellipsisName() {
return strEllipsis(this.data.name, 14, 5);
},
},
......@@ -78,11 +82,23 @@
}).catch(() => {
});
},
onClickReplace() {
selectFile((files) => {
let file = files[0];
this.replaceAsset({
file: file,
uuid: this.data.uuid,
});
})
},
...mapMutations([
'deleteAsset',
'modifyAsset',
'assetDragStart'
'assetDragStart',
]),
...mapActions([
'replaceAsset'
])
}
}
</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 @@
<el-tab-pane :label="$t('Props')" name="properties">
<props-tab/>
</el-tab-pane>
<el-tab-pane :label="$t('Script')" name="script">
<scripts-tab/>
</el-tab-pane>
<el-tab-pane :label="$t('Behavior')" name="behavior">
<behavior-tab/>
</el-tab-pane>
......@@ -15,10 +18,11 @@
import Pane from '../../components/Pane';
import BehaviorTab from "./Inspector/BehaviorTab";
import PropsTab from "./Inspector/PropsTab";
import ScriptsTab from "./Inspector/ScriptsTab";
export default {
name: 'Inspector',
components: {BehaviorTab, PropsTab, Pane},
components: {ScriptsTab, BehaviorTab, PropsTab, Pane},
data() {
return {
tab: 'properties'
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -18,7 +18,7 @@
},
methods: {
parseName(process){
return process.data.alias || process.meta.name;
return process.meta.name;
},
onClickItem(process, index){
this.$emit('pop', index);
......
This diff is collapsed.
This diff is collapsed.
......@@ -5,7 +5,7 @@
</el-input>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view">
<el-form v-model="process" size="mini" label-width="80px" label-position="left" @submit.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"
:is="getInput(property)"
v-model="process.data.props[key]"
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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