Commit 6456ad0f authored by rockyl's avatar rockyl

提交一下

parent 39f9e6a6
......@@ -62,7 +62,11 @@ function execute(document) {
}
return 1;
} else if (layer.name.match(excludeFlagReg)) {
try {
layer.remove();
}catch (e) {
alert('移除图层' + layer.name + '失败,可能含有锁的层');
}
return 2;
}
return 0;
......
......@@ -4,7 +4,7 @@
"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'"
},
......@@ -13,10 +13,12 @@
"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",
"path": "^0.12.7",
"querystringify": "^2.1.1",
"socket.io-client": "^2.3.0",
"splitpanes": "^1.14.5",
"string-width": "^4.1.0",
"uuid": "^3.3.3",
......@@ -24,7 +26,8 @@
"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>
......
/**
* 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);
/**
* 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) {
socket.emit('edit-open', code);
}
function onConnect() {
codeSyncServeEnabled = true;
events.$emit('code-sync-start');
}
function onDisconnect() {
socket = null;
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
......@@ -30,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}`
}
......
......@@ -18,6 +18,7 @@
"Add": "Add",
"Delete": "Delete",
"Delete all": "Delete all",
"Not delete": "Not delete",
"Import": "Import",
"Export": "Export",
"Importing project": "Importing project",
......@@ -28,13 +29,20 @@
"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",
......@@ -147,9 +155,9 @@
"menu": {
"save": "Save",
"details": "Details",
"preview": "Preview",
"preview2": "Fast-Preview",
"preview-fast": "Preview",
"publish": "Publish",
"mock": "Mock",
"exit": "Exit",
"undo": "Undo",
"redo": "Redo"
......@@ -159,7 +167,8 @@
"image": "Image",
"label": "Label",
"rect": "Rect",
"scrollView": "ScrollView"
"scrollView": "ScrollView",
"svga": "SVGA"
},
"panes": {
"Assets": "Assets",
......
......@@ -18,6 +18,7 @@
"Add": "添加",
"Delete": "删除",
"Delete all": "删除全部",
"Not delete": "不删除",
"Import": "导入",
"Export": "导出",
"Importing project": "项目导入中",
......@@ -28,13 +29,20 @@
"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": "事件",
......@@ -151,9 +159,9 @@
"menu": {
"save": "保存",
"details": "详情",
"preview": "预览",
"preview2": "快速预览",
"preview-fast": "预览",
"publish": "发布",
"mock": "Mock",
"exit": "退出",
"undo": "撤销",
"redo": "重做"
......@@ -165,7 +173,8 @@
"rect": "矩形",
"circle": "圆形",
"textinput": "输入框",
"scrollView": "滚动视图"
"scrollView": "滚动视图",
"svga": "SVGA"
},
"panes": {
"Assets": "素材",
......
......@@ -39,15 +39,10 @@ export default new Vuex.Store({
'modifyAsset',
'importView',
'importAssets',
'addDataMapping',
'deleteDataMapping',
'modifyDataMapping',
'addEnvMapping',
'deleteEnvMapping',
'modifyEnvMapping',
'modifyActiveView',
'behavior_save',
'modifyCustoms',
'modifyProjectDetails',
'modifyMocks',
]
})
]
......
......@@ -64,7 +64,6 @@ export const behaviorStore = {
state.processStack.push(process);
updatePropsEditable(state);
state.currentProcess = process;
console.log(state.currentProcess);
},
popProcessStack(state, index) {
state.processStack.splice(index);
......
......@@ -7,6 +7,8 @@
import {envApi} from "../../api";
import i18n from "../../i18n";
const storeKey = 'code-sync-serve-config';
export const envStore = {
state: {
initialized: false,
......@@ -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: {
......@@ -77,7 +92,7 @@ export const envStore = {
const env = await envApi.fetchEnv();
commit('updateEnv', env);
}
}
},
}
};
......
This diff is collapsed.
......@@ -16,14 +16,16 @@ export const projectsStore = {
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;
}
}
......
......@@ -3,7 +3,7 @@
*/
export const template =
`<!DOCTYPE html>
`<!DOCTYPE html>
<html lang="en">
<head>
......@@ -28,13 +28,13 @@ 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="${process.env.NODE_ENV === 'development' ? 'http://10.10.94.134:4002/dist/index.js' : 'http://yun.duiba.com.cn/editor/zeroing/libs/engine.d20665eae76962ad21c153fe7a719130b08e292c.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');
......
......@@ -2,16 +2,7 @@
$dock-pin-width: 9px;
.behavior-editor-dialog {
display: flex;
flex-direction: column;
.el-dialog__body {
flex: 1;
height: 0;
padding: 5px;
}
.process-pane {
......
@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;
......@@ -57,11 +54,11 @@
align-self: flex-start;
}
.bottom-bar{
.bottom-bar {
align-self: flex-end;
}
.list{
.list {
display: flex;
flex-direction: column;
flex: 1;
......@@ -101,28 +98,76 @@
}
.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;
}
}
}
}
.full-scrollbar{
height: 68vh;
}
.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;
}
}
/**
* 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";
.flex-dialog {
display: flex;
flex-direction: column;
.el-dialog__body {
flex: 1;
height: 0;
padding: 5px;
}
}
.el-tree {
background: transparent;
}
......@@ -23,15 +32,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 +55,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 {
......
......@@ -13,6 +13,7 @@
.el-collapse-item__header{
height: 25px;
line-height: 25px;
}
.el-tabs__item {
......@@ -67,7 +68,7 @@
}
.el-button + .el-button {
margin-left: 2px;
margin-left: 5px;
}
.trigger-list{
......
......@@ -30,6 +30,10 @@ export const componentsMap = [
label: '滚动视图',
value: 'scrollView'
},
{
label: 'SVGA',
value: 'svga'
},
];
// 属性的计算方法
......
/**
* Created by rockyl on 2019-12-18.
*/
const version = 3;
const storeConfigs = [
{name: 'project', key: 'id'},
{name: 'mock', key: 'path'},
{name: 'preview', 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
......@@ -24,7 +24,7 @@ export function playWaiting(promise, text, closeLoading = true) {
messageError(e);
throw e;
}).finally(() => {
if(closeLoading){
if (closeLoading) {
loading.close();
}
})
......@@ -59,7 +59,7 @@ export function updateProcesses(processes, targetMetaID, replaceMetaID) {
subProcess.meta = replaceMetaID;
}
}
if(process.metas){
if (process.metas) {
updateProcesses(process.metas, targetMetaID, replaceMetaID)
}
}
......@@ -116,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;
}
......@@ -131,14 +158,26 @@ export function selectFile(callback, {accept, multiple} = {}) {
input.click();
}
export function openPreview(packResult){
export function openPreview(packResult) {
setTimeout(() => {
let url;
if(location.host.startsWith('localhost')){
if (location.host.startsWith('localhost')) {
url = packResult.tplUrl;
}else{
} 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;
}
......@@ -203,6 +203,20 @@ export default {
type: 'textArea',
value: ''
},
type: {
title: '输入类型',
type: 'select',
options: [
{ label: '文本', value: 'text' },
{ label: '密码', value: 'password' },
],
value: 'text'
},
pattern: {
title: '输入模式',
type: 'input',
value: ''
},
fillColor: {
title: '颜色',
type: 'colorPicker',
......@@ -234,7 +248,7 @@ export default {
},
},
image: {
groupName: '来源',
groupName: '图片',
source: {
title: '来源',
type: 'source',
......@@ -259,6 +273,30 @@ export default {
}
}
},
svga: {
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
},
},
rect: {
groupName: '矩形',
fillColor: {
......
......@@ -3,6 +3,7 @@
*/
import MaxRectsBinPack from "./MaxRectsBinPack";
import {clonePureObj} from "./index";
const packExts = ['.png']; //, '.jpg', '.jpeg', '.bmp'
......@@ -83,7 +84,7 @@ export async function packImages(asssts, options = {}) {
};
for (let asset of rect.assets) {
frames[asset.uuid] = sprite;
frames[asset.uuid] = Object.assign({}, sprite, {name: asset.name});
i++;
}
}
......
......@@ -11,13 +11,16 @@
</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";
......@@ -29,10 +32,19 @@
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,
......@@ -59,29 +71,39 @@
}
},
computed: {
...mapGetters([]),
...mapState({
codeSyncServeConfig(state) {
return state.env.codeSyncServeConfig;
},
}),
},
async mounted() {
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 => {
});
setTimeout(() => {
startCodeSyncServe(this.codeSyncServeConfig);
}, 100);
this.loadProject();
},
destroyed() {
document.removeEventListener('keydown', this.onKeyPress)
},
created() {
events.$on('saveAndPreview', () => {
this.clickMenu("preview");
});
},
methods: {
prepare() {
return Promise.all([
this.updateEnv(),
db.open('store'),
])
},
onKeyPress(e) {
......@@ -166,14 +188,18 @@
this.$refs.dialogsDialog.show();
break;
case 'preview':
this.preview();
await this.pack(true);
break;
case 'preview2':
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);
break;
......@@ -202,7 +228,7 @@
break;
}
},
async publish(){
async publish() {
this.pack();
},
async pack(debug = false) {
......@@ -211,7 +237,7 @@
text: this.$t('Packing'),
});
try {
if(!debug){
if (!debug) {
await this.saveProject(false);
}
const packResult = await this.packProject(debug);
......@@ -257,7 +283,7 @@
let previewUrl = new URL(location.href);
previewUrl.hash = '#/preview/' + projectID;
setTimeout(()=>{
setTimeout(() => {
window.open(previewUrl.href, 'blank');
}, 300);
},
......
......@@ -23,7 +23,7 @@
<el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view">
<div class="file-list">
<div class="file-uploader" @click="toUploadFile">
<div 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"
......@@ -42,7 +42,7 @@
import FileItem from "./Assets/FileItem";
import AssetsShow from "./Assets/AssetsShow";
import SplitPanes from 'splitpanes'
import {selectFile} from "../../utils";
import {scanFiles, selectFile} from "../../utils";
export default {
name: "Assets",
......@@ -68,6 +68,23 @@
this.uploadFiles(files);
}, {multiple: true})
},
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(file) {
this.$refs.assetsShow.show(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>
<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" :src="thumbnailUrl"
alt="thumb" @dblclick="onDbclick()">
<div class="operate-bar">
......
<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'
......
......@@ -147,28 +147,32 @@
},
handleBehaviorsChange(isPreview) {
if (isPreview) {
events.$emit('saveAndPreview')
events.$emit('save-and-preview')
}
},
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'), {
showClose: false,
closeOnClickModal: false,
closeOnPressEscape: false,
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
confirmButtonText: this.$t('Delete'),
cancelButtonText: this.$t('Not delete'),
distinguishCancelAndClose: true,
type: 'warning'
}).then(() => {
deleteMeta = true;
}).catch((e) => {
}).catch(action => {
if(action === 'close'){
close = true;
}
});
if(!close){
this.deleteBehaviorDirect({
behaviors,
index,
deleteMeta,
})
}
},
...mapActions([
'modifyActiveView',
......
......@@ -2,8 +2,6 @@
<div class="zero-inspector-props-form" v-if="activeComponent.uuid">
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden">
<el-form ref="form" size="mini" :model="form" label-width="80px" @submit.native.prevent>
<el-collapse v-model="configColl">
<el-collapse-item :title="$t('Props')" name="properties">
<el-form-item label="名称">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
</el-form-item>
......@@ -23,32 +21,6 @@
</el-tooltip>
</el-form-item>
</template>
</el-collapse-item>
<el-collapse-item :title="$t('Script')" 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-dropdown trigger="click" @command="handleAddScript" placement="top" size="small">
<el-button size="mini">{{$t('Add')}}</el-button>
<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-collapse-item>
</el-collapse>
</el-form>
</el-scrollbar>
</div>
......@@ -110,36 +82,6 @@ const componentMapper = {
}
};
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 {
name: 'PropsTab',
components: { 'dynamic-component': dynamicComponent },
......@@ -150,18 +92,11 @@ export default {
name: '',
type: '',
properties: {},
scripts: []
},
configColl: ['properties']
};
},
computed: {
...mapGetters(['activeComponent', 'activeComponentCopy', 'componentList']),
...mapState({
scripts(state){
return state.env.scripts;
}
}),
cmpProps: function() {
// 获取properties.js中的默认配置
return getCmpProps(this.activeComponent.type);
......@@ -183,13 +118,6 @@ export default {
_view[label] = v;
this.$store.dispatch('modifyActiveView', _view);
},
/**
* 脚本预设对象选中
*/
handleAddScript(command) {
console.log('handleAddScript', command);
this.$store.dispatch('addNodeScript', command);
},
/**
* 基础属性发生改变
*/
......@@ -200,20 +128,6 @@ export default {
_prop[key] = v;
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
});
},
/**
* 获取动态组件的类型
*/
......@@ -239,61 +153,6 @@ export default {
let _properties = this.activeComponentCopy.properties;
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>
......
<template>
<div class="zero-inspector-script-form" v-if="activeComponent.uuid">
<el-collapse v-model="configColl">
<el-collapse-item title="配置" name="properties">
<div class="zero-inspector-props-form" v-if="activeComponent.uuid">
<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-item label="名称">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="form.type" @change="v => handleChange('type', v)" placeholder="请选择类型">
<el-option v-for="cmp in componentsMap" :key="cmp.value" :label="cmp.label" :value="cmp.value"></el-option>
</el-select>
</el-form-item>
<template v-for="(p, key) in cmpProps">
<el-form-item v-if="key !== 'groupName'" :id="activeComponent.uuid + '-' + key" :key="activeComponent.uuid + key" :label="p.title">
<!-- {{key}} -->
<dynamic-component :label="key" :item="p" :properties="activeComponent.properties"></dynamic-component>
<el-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>
</el-collapse-item>
<el-collapse-item title="脚本" name="scripts">
<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-dropdown trigger="click" @command="handleAddScript" placement="top" size="small">
<el-button size="mini">{{$t('Add')}}</el-button>
<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>
</template>
<script>
import { mapGetters } from 'vuex';
import { mapGetters, mapState } from 'vuex';
import _ from 'lodash';
import { componentsMap, getCmpProps } from '../../../utils/common';
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 {
name: 'PropsTab',
name: 'ScriptsTab',
components: { 'dynamic-component': dynamicComponent },
data() {
return {
componentsMap,
form: {
name: '',
type: '',
properties: {}
scripts: []
},
configColl: ['properties']
};
},
computed: {
...mapGetters(['activeComponent', 'componentList']),
cmpProps: function() {
return getCmpProps(this.activeComponent.type);
...mapGetters(['activeComponent', 'activeComponentCopy', 'componentList']),
...mapState({
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: {
/**
* 脚本预设对象选中
*/
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;
},
methods: {
handleChange(label, v) {
this.$store.dispatch('modifyComponent', {
label: label,
value: v
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 +167,17 @@ export default {
</script>
<style lang="scss">
.zero-inspector-script-form {
.zero-inspector-props-form {
width: 100%;
padding-right: 10px;
.el-form-item--mini.el-form-item {
margin-bottom: 10px;
}
.el-divider__text {
background-color: #e9e9e9;
}
.zero-inspector-props-group {
max-height: 500px;
}
}
.script-config-dialog {
height: 350px;
}
</style>
\ No newline at end of file
<template>
<div class="tool-bar">
<sample-menu :data="menu" @click-menu="clickMenu"/>
<upload-indicator/>
<div style="flex: 1"></div>
<div class="right-part">
<span>
......@@ -15,11 +14,10 @@
<script>
import {mapState, mapActions, mapMutations} from 'vuex'
import SampleMenu from "../../components/SampleMenu";
import UploadIndicator from "./ToolBar/UploadIndicator";
export default {
name: "ToolBar",
components: {UploadIndicator, SampleMenu},
components: {SampleMenu},
data() {
return {}
},
......@@ -45,7 +43,7 @@
async mounted() {
//监听键盘事件
document.addEventListener('keydown', this.onKeyPress);
//document.addEventListener('keydown', this.onKeyPress);
},
methods: {
clickMenu(menuItem) {
......
......@@ -5,10 +5,11 @@
:show-close="false"
fullscreen
:append-to-body="true"
custom-class="behavior-editor-dialog"
custom-class="flex-dialog behavior-editor-dialog"
>
<div class="meta-editor-wrapper">
<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>
<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>
<template>
<el-form-item prop="id" :label="$t('ID')">
<el-input v-model="meta.id" :placeholder="$t('ID')" :readonly="!editable"/>
......@@ -42,6 +43,7 @@
</template>
</el-form>
<div style="margin-top: 5px;">
<code-sync-indicator/>
<el-tag v-for="(item, key) in exposeVariables" :key="key" size="mini">{{item}}</el-tag>
</div>
<!--<el-input v-if="meta" class="script-editor" :readonly=" !editable" type="textarea" :placeholder="$t('Code')"
......@@ -49,10 +51,7 @@
<codemirror ref="codeEditor" v-if="meta"
v-model="meta.script"
:options="cmOptions"
@cursorActivity="onCodeChange"
>
</codemirror>
@cursorActivity="onCodeChange"/>
</div>
<div slot="footer" class="dialog-footer">
<div class="button-bar">
......@@ -64,7 +63,7 @@
</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" 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>
......@@ -85,14 +84,17 @@
import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/hint/javascript-hint.js'
import events from "@/global-events.js"
import 'codemirror/addon/lint/lint.js'
import 'codemirror/addon/lint/lint.css'
import 'codemirror/addon/lint/javascript-lint.js'
import events from '../../../global-events';
import CodeSyncIndicator from "../BottomBar/CodeSyncIndicator";
const exposeVariables = ['args', 'props', 'target', 'global', 'vm', 'engine'];
export default {
name: "MetaEditorDialog",
components: {PropsEditorDialog, ElFormItem, codemirror},
components: {CodeSyncIndicator, PropsEditorDialog, ElFormItem, codemirror},
data() {
return {
visible: false,
......@@ -102,26 +104,35 @@
rules: {
id: [
{ required: true, trigger: 'blur' },
{required: true, trigger: 'blur'},
],
name: [
{ required: true, trigger: 'blur' }
{required: true, trigger: 'blur'}
],
},
cmOptions: {
tabSize: 2,
mode: 'text/javascript',
styleActiveLine: true,
theme: 'default',
lineNumbers: true,
line: true,
matchBrackets: true,
autoCloseBrackets: true,
extraKeys: {
"Alt-Space": "autocomplete",
},
lineNumbers: true,
mode: {name: 'javascript', globalVars: true},
gutters: ["CodeMirror-lint-markers"],
lint: true
}
}
},
mounted(){
mounted() {
events.$on('edit-save', this.onEditSave);
},
destroyed(){
events.$off('edit-save', this.onEditSave);
},
computed: {
editable() {
......@@ -136,6 +147,11 @@
this.visible = true;
this.meta = clonePureObj(meta);
this.oldMetaID = this.meta.id;
events.$emit('edit-open', this.meta.script);
},
onEditSave(code){
this.$set(this.meta, 'script', code);
},
onClickEditProps() {
this.$refs.propsEditorDialog.edit(this.meta.props);
......@@ -148,8 +164,8 @@
.catch((e) => {
});
} else {
this.$emit('input',this.meta,isPreview);
this.visible=false;
this.$emit('input', this.meta, isPreview);
this.visible = false;
}
} else {
return false;
......@@ -167,19 +183,19 @@
},
onPaste(e) {
let metaStr = e.clipboardData.getData("Text");
if(metaStr){
if (metaStr) {
try {
let meta = JSON.parse(metaStr);
this.meta = meta;
}catch (e) {
} catch (e) {
}
}
},
focusPasteBoard(){
focusPasteBoard() {
this.$refs.pasteBoard.focus();
},
onCodeChange(codemirror){
onCodeChange(codemirror) {
//codemirror.showHint();
//console.log(code);
}
......
<template>
<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"
: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>
<div slot="footer" class="dialog-footer">
<el-button size="mini" @click="onSave(true)">{{$t('Save And Preview')}}</el-button>
......
<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>
<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"
:append-to-body="true"
:show-close="false"
fullscreen
custom-class="details-dialog"
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">
<project-editor/>
<project-editor ref="projectEditor"/>
</el-tab-pane>
<el-tab-pane :label="$t('Env constant')" name="env">
<env-editor/>
<env-editor ref="envEditor"/>
</el-tab-pane>
<el-tab-pane :label="$t('Data mapping')" name="data-mapping">
<data-mapping-editor/>
<data-mapping-editor ref="dataMappingEditor"/>
</el-tab-pane>
<el-tab-pane :label="$t('Custom module')" name="custom">
<custom-editor/>
<custom-editor ref="customEditor"/>
</el-tab-pane>
</el-tabs>
<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>
</el-dialog>
</template>
<script>
import {mapMutations} from 'vuex';
import ProjectEditor from "./editors/ProjectEditor";
import EnvEditor from "./editors/EnvEditor";
import CustomEditor from "./editors/CustomEditor";
import DataMappingEditor from "./editors/DataMappingEditor";
const refs = [
'projectEditor',
'envEditor',
'dataMappingEditor',
'customEditor',
];
export default {
name: "DetailsDialog",
components: {DataMappingEditor, CustomEditor, EnvEditor, ProjectEditor},
......@@ -45,12 +54,24 @@
show() {
this.visible = true;
},
onSave() {
for (let ref of refs) {
this.$refs[ref].save();
}
this.visible = false;
this.modifyProjectDetails();
},
onClose() {
this.visible = false;
},
onOpen() {
for (let ref of refs) {
this.$refs[ref].edit();
}
},
...mapMutations([
'modifyProjectDetails',
]),
}
}
</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
<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>
<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
label="Version"
width="100"
>
<template slot-scope="scope">
<el-select size="mini"/>
</template>
</el-table-column>-->
<el-table-column>
</el-table-column>
</el-table>
</template>
<script>
import {mapState, mapMutations} from 'vuex'
import {clonePureObj} from "../../../../utils";
export default {
name: "CustomEditor",
data() {
return {
visible: false,
customMetas: [],
editData: null,
}
},
computed: {
...mapState({
customMetas: state => state.env.customs,
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) {
this.customMetas.push({
id: meta.id,
name: meta.name,
selected: this.editData.includes(meta.id),
})
}
},
save() {
const editData = this.editData;
editData.splice(0);
for (let meta of this.customMetas) {
if(meta.selected){
editData.push(meta.id);
}
}
this.modifyCustoms(editData);
},
...mapMutations([
'modifyCustoms',
]),
onChange(v){
this.modifyCustoms(v);
}
}
}
</script>
......
<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">
<el-button class="add-button" icon="el-icon-plus" size="mini" circle @click="onAdd"/>
<div class="list">
<div class="item" v-for="(link, index) in mapping">
<el-input size="mini" style="width: 30%" v-model="link.name" @change="onChange"/>
<div class="item" v-for="(link, index) in editData">
<el-input size="mini" style="width: 30%" v-model="link.name"/>
<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)"/>
</div>
</div>
......@@ -16,12 +16,14 @@
<script>
import {mapState, mapMutations} from 'vuex'
import {clonePureObj} from "../../../../utils";
export default {
name: "DataMappingEditor",
data() {
return {
visible: false,
editData: null,
}
},
computed: {
......@@ -30,11 +32,17 @@
}),
},
methods: {
onAdd() {
this.addDataMapping();
edit() {
this.editData = clonePureObj(this.mapping);
},
save() {
this.modifyDataMapping(this.editData);
},
onChange() {
this.modifyDataMapping();
onAdd() {
this.editData.push({
name: '',
path: '',
});
},
toDeleteItem(link, index) {
this.$confirm(this.$t('Are you sure to delete this item'), this.$t('Alert'), {
......@@ -42,13 +50,11 @@
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this.deleteDataMapping(index);
this.editData.splice(index, 1);
}).catch((e) => {
});
},
...mapMutations([
'addDataMapping',
'deleteDataMapping',
'modifyDataMapping',
]),
}
......
<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">
<el-button class="add-button" icon="el-icon-plus" size="mini" circle @click="onAdd"/>
<div class="list">
<div class="item" v-for="(link, index) in env">
<el-input size="mini" style="width: 30%" v-model="link.name" @change="onChange"/>
<div class="item" v-for="(link, index) in editData">
<el-input size="mini" style="width: 30%" v-model="link.name"/>
<el-icon class="el-icon-connection"/>
<el-input size="mini" v-model="link.value" @change="onChange"/>
<el-button class="delete-button" icon="el-icon-minus" circle size="mini" plain type="danger" @click="toDeleteItem(link, index)"/>
<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)"/>
</div>
</div>
</div>
......@@ -16,12 +17,14 @@
<script>
import {mapState, mapMutations} from 'vuex'
import {clonePureObj} from "../../../../utils";
export default {
name: "EnvEditor",
data() {
return {
visible: false,
editData: null,
}
},
computed: {
......@@ -30,11 +33,17 @@
}),
},
methods: {
onAdd() {
this.addEnvMapping();
edit() {
this.editData = clonePureObj(this.env);
},
save() {
this.modifyEnv(this.editData);
},
onChange() {
this.modifyEnvMapping();
onAdd() {
this.editData.push({
name: '',
value: '',
});
},
toDeleteItem(link, index) {
this.$confirm(this.$t('Are you sure to delete this item'), this.$t('Alert'), {
......@@ -42,14 +51,12 @@
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this.deleteEnvMapping(index);
this.editData.splice(index, 1);
}).catch((e) => {
});
},
...mapMutations([
'addEnvMapping',
'deleteEnvMapping',
'modifyEnvMapping',
'modifyEnv',
]),
}
}
......
<template>
<div>
<el-scrollbar class="scrollbar full-scrollbar" wrap-class="wrap-x-hidden" view-class="view">
<el-form @submit.native.prevent ref="form" :model="options" size="mini" label-position="right"
<div class="project-editor">
<el-scrollbar v-if="editData" class="project-scrollbar" wrap-class="wrap-x-hidden" view-class="view">
<el-form @submit.native.prevent ref="form" :model="editData" size="mini" label-position="right"
label-width="150px">
<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 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"
:key="index"
:value="view.name"
......@@ -16,19 +16,19 @@
</el-select>
</el-form-item>
<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 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 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 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 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"
:key="key"
:value="key"
......@@ -37,7 +37,7 @@
</el-select>
</el-form-item>
<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"
:key="key"
:value="key"
......@@ -48,7 +48,7 @@
<el-form-item prop="tpl" :label="$t('Template')">
<!-- <el-input type="textarea" v-model="options.tpl" :rows="10"/> -->
<codemirror ref="codeEditor"
v-model="options.tpl"
v-model="editData.tpl"
:options="cmOptions"
@cursorActivity="onCodeChange"
>
......@@ -56,10 +56,10 @@
</el-form-item>
</el-form>
</el-scrollbar>
<div class="operate-bar">
<!--<div class="operate-bar">
<el-button size="mini" @click="onReset">{{$t('Reset')}}</el-button>
<el-button size="mini" type="primary" @click="onSave">{{$t('Save')}}</el-button>
</div>
</div>-->
</div>
</template>
......@@ -73,6 +73,7 @@
import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/hint/javascript-hint.js'
import {clonePureObj} from "../../../../utils";
export default {
name: "ProjectEditor",
......@@ -82,6 +83,7 @@
const rendererType = this.$t('rendererType');
return {
visible: false,
editData: null,
scaleMode,
rendererType,
cmOptions: {
......@@ -106,15 +108,21 @@
},
methods: {
...mapMutations([
'modifyProject',
'modifyOptions',
]),
onReset() {
edit() {
this.editData = clonePureObj(this.options);
},
save() {
this.modifyOptions(this.editData);
},
/*onReset() {
this.$refs.form.resetFields();
},
onSave() {
this.modifyProject();
},
onCodeChange(codemirror){
},*/
onCodeChange(codemirror) {
//codemirror.showHint();
//console.log(code);
}
......
......@@ -26,7 +26,8 @@
<template slot="header" slot-scope="scope">
<el-checkbox
v-model="onlyMine"
size="mini">{{$t('Only mine')}}</el-checkbox>
size="mini">{{$t('Only mine')}}
</el-checkbox>
</template>
<template slot-scope="scope">
<el-button
......@@ -44,11 +45,12 @@
type="warning" icon="icon-download"
size="small" circle plain>
</el-button>
<!--<el-button
<el-button
v-if="showDeleteButton"
@click.native.prevent="onDeleteProject(scope.row)"
type="danger" icon="el-icon-delete"
size="small" circle plain>
</el-button>-->
</el-button>
</template>
</el-table-column>
</el-table>
......@@ -96,13 +98,16 @@
});
},
computed: {
showDeleteButton() {
return location.host.startsWith('localhost');
},
...mapState([
'projects',
'env',
]),
},
watch: {
onlyMine(){
onlyMine() {
this.handleCurrentChange(1);
}
},
......
<template>
<iframe ref="iframe" class="player-wrapper">
</iframe>
<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,
}
},
mounted() {
if(!this.ts){
async mounted() {
this.codes = {};
this.urls = {};
if (!this.ts) {
this.ts = localStorage.getItem('preview-ts');
}
document.addEventListener("visibilitychange", this.onVisibilityChange);
setTimeout(()=>{
this.buildPage();
}, 100);
await db.open();
this.reload();
},
destroyed(){
destroyed() {
document.removeEventListener("visibilitychange", this.onVisibilityChange);
},
methods: {
onVisibilityChange(e){
if(!document.hidden){
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();
if (this.ts !== ts) {
//this.$refs.iframe.contentDocument.location.reload();
//document.location.reload();
this.ts = ts;
this.reload();
}
}
},
buildPage() {
async buildPage() {
const {projectID} = this.$route.params;
const storeKey = 'preview-project-' + projectID;
let data = localStorage.getItem(storeKey);
let dataObj = JSON.parse(data);
const {data, codes} = await db.get('preview', projectID);
let {options: {tpl, pageTitle, containerId}} = data;
//const dataName = projectID + "_data";
//const launchCode = `engine.launchWithWindowVariable("${dataName}");`;
let {options: {tpl, pageTitle, containerId}} = dataObj.data;
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)]));
const launchCode = `engine.launchWithLocalStorage("${projectID}");`;
document.title = pageTitle;
tpl = tpl
.replace('$PAGE_TITLE$', pageTitle)
.replace('$CONTAINER_ID$', containerId)
.replace('$SCRIPTS$', '')
.replace('engine.launch(\'//yun.duiba.com.cn/aurora/$VERSION$-data.json\');', launchCode);
.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>
.player-wrapper{
.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 MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '//yun.duiba.com.cn/editor/zeroing/v1/' : '',
pluginOptions: {
......@@ -23,6 +25,10 @@ module.exports = {
}
},*/
configureWebpack: {
plugins: [
/*new MonacoWebpackPlugin({
languages: ['html', 'javascript', 'json']
})*/
]
}
};
This diff is collapsed.
......@@ -2,7 +2,9 @@
<module version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/dist" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
......
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