Commit 9c8bb573 authored by rockyl's avatar rockyl

Merge branch 'res-group' into dev

parents bff32687 c0992a69
......@@ -147,3 +147,17 @@ export async function updateOperators(id, operators) {
errMessage: 'Failed to update operator',
})
}
/**
* 修改备注
* @param id
* @param remark
* @return {Promise<*|any|>}
*/
export async function updateRemark(id, remark) {
return await fetchApi('/api/project/updateRemark', {
params: { id, remark },
method: 'post',
errMessage: 'Failed to update remark',
})
}
<template>
<div :draggable="draggable" @dragstart="onDragstart" ref="playerContainer" :style="style">
<div :draggable="draggable" @dragstart="onDragstart" ref="playerContainer" :style="style"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave"
>
</div>
</template>
......@@ -24,6 +27,10 @@
type: Boolean,
default: false,
},
autoplay: {
type: Boolean,
default: false,
},
},
data() {
return {
......@@ -75,10 +82,24 @@
this.videoSize.height = videoItem.videoSize.height;
this.$nextTick(() => {
player.setVideoItem(videoItem);
player.startAnimation();
if(this.autoplay){
player.startAnimation();
}else{
player.stepToFrame(0);
}
});
}
},
onMouseEnter(){
if(!this.autoplay){
this.player.stepToFrame(0, true);
}
},
onMouseLeave(){
if(!this.autoplay){
this.player.stepToFrame(0);
}
},
...mapActions([
'loadSvga'
]),
......
......@@ -6,7 +6,7 @@ export let API_HOST;
if (process.env.NODE_ENV === 'development') {
//API_HOST = '//10.10.95.74:7777';
//API_HOST = '//192.168.1.16:7777';
API_HOST = '//10.10.94.107:3000';
API_HOST = '//0.0.0.0:3000';
//API_HOST = '//192.168.0.105:7777';
//API_HOST = '//localhost:3002';
//API_HOST = window.__data.apiHost;
......
......@@ -74,6 +74,7 @@
"No projects": "无项目",
"Create": "创建",
"Project details": "项目详情",
"Project remark": "项目备注",
"Project does not exist": "项目不存在",
"Project name": "项目名",
"Data mapping": "数据映射",
......@@ -101,8 +102,10 @@
"Create project": "创建项目",
"Rename project": "重命名项目",
"Entry scene view": "入口场景视图",
"Loading view": "加载视图",
"Page title": "页面标题",
"Container ID": "容器ID",
"Lazy Load Delay": "懒加载延时",
"Design width": "设计宽度",
"Design height": "设计高度",
"Frame Rate": "帧率",
......@@ -163,10 +166,12 @@
"Are you sure to discard the local modification": "确定丢弃本地修改吗?",
"Input view name": "输入视图名",
"Invalid view name": "无效的视图名",
"Unsaved version found locally": "本地发现了未保存保本,请选择版本打开",
"Unsaved version found locally, can not open from history": "本地发现了未保存版本,不能从历史打开",
"Unsaved version found locally, choose one": "本地发现了未保存版本,请选择版本打开",
"Local Version": "本地版本",
"Remote Version": "远程版本",
"Confirm to exit the editor": "确定退出编辑器吗?",
"Modified, save before exit": "内容已修改,要保存后关闭吗?",
"Confirm to publish": "确定发布吗?",
"Are you sure to delete this behavior?": "确定删除这个的行为吗",
"Are you sure to delete this process?": "确定删除这个的过程吗",
......@@ -226,6 +231,7 @@
"Function cmd": "功能指令",
"Edit store": "编辑数据",
"Edit computed": "计算属性",
"Asset dependence": "素材依赖",
"Are you sure to delete this cmd?": "确定删除这个指令吗?",
"Store editor": "数据编辑器",
"Store": "数据",
......@@ -247,6 +253,10 @@
"Previous Conflict": "上一个冲突",
"Next Conflict": "下一个冲突",
"To Verify": "去验证",
"Transform options": "转换参数",
"Save behavior successfully": "保存行为成功",
"The main editor is closed and cannot be saved": "主编辑器已关闭,无法保存",
"A behavior is being edited. Please save it first": "有行为正在编辑,请先保存",
"eventGroup": {
"in": "接收",
"out": "派发"
......@@ -355,6 +365,10 @@
"webgl": "WEBGL",
"canvas": "CANVAS"
},
"behaviorEditors": {
"builtin": "内建",
"standalone": "独立"
},
"pxEnvs": {
"dev": "开发环境",
"test": "测试环境",
......@@ -391,7 +405,8 @@
"assetItemMenu": {
"rename": "重命名",
"replace": "替换",
"combine": "合并"
"combine": "合并",
"to-jpg": "转换为JPG"
},
"customCmds": {
"z-for": "循环",
......
......@@ -24,9 +24,9 @@ export default new Router({
component: () => import('./views/Preview.vue')
},
{
path: '/diff',
name: 'diff',
component: () => import('./views/ProjectDiff.vue')
path: '/process-editor',
name: 'process-editor',
component: () => import('./views/ProcessEditor.vue')
},
]
})
......@@ -7,8 +7,7 @@
import Vue from "vue";
import i18n from "../../i18n";
import generateUUID from "uuid/v4";
import {clonePureObj, getViewNodePath, findMetaDepById, searchViewNode, updateProcesses} from "../../utils";
import {arrayFind} from "element-ui/src/utils/util";
import {clonePureObj, findMetaDepById, searchViewNode, updateProcesses} from "../../utils";
export const behaviorStore = {
state: {
......@@ -35,13 +34,16 @@ export const behaviorStore = {
makeBehaviorDirty(state) {
state.dirty = true;
},
behavior_startEdit(state, {originData, behavior}) {
clearBehavior(state) {
state.dirty = false;
},
behavior_startEdit(state, {originData, behavior}) {
state.projectData = originData;
state.originProcesses = originData.processes;
state.processes = clonePureObj(originData.processes);
state.currentBehavior = clonePureObj(behavior);
this.commit('clearBehavior');
this.commit('updateCustomProcesses', state.processes);
},
behavior_save(state) {
......@@ -117,7 +119,9 @@ export const behaviorStore = {
newMeta.id = newUUID;
newMeta.isInline = true;
Vue.set(parentMeta, 'metas', []);
if(!parentMeta.metas){
Vue.set(parentMeta, 'metas', []);
}
parentMeta.metas.push(newMeta);
process.metaID = newUUID;
}
......@@ -162,13 +166,12 @@ export const behaviorStore = {
if (uuid) {
return searchViewNode(state.projectData.views, uuid);
}
}
},
processesInEditor(state) {
return state.processes;
},
},
actions: {
behavior_save({commit, state}) {
commit('behavior_save');
return state.currentBehavior;
},
addCustomProcessMeta({commit, state}, {masterProcess, isInline, processId, name}) {
let meta = {
id: generateUUID(),
......
......@@ -5,8 +5,10 @@
*/
import Vue from "vue";
import {envApi} from "../../api";
import {editorApi, envApi} from "../../api";
import {Parser} from "svgaplayerweb";
import db from "../../utils/db-storage";
import events from "@/global-events"
const storeKey = 'code-sync-serve-config';
const svgaParser = new Parser();
......@@ -15,7 +17,7 @@ export const editorStore = {
state: {
initialized: false,
name: 'Zeroing Editor',
version: '0.1.0',
version: '0.2.1',
templates: {
builtin: ['blank'],
custom: [],
......@@ -28,6 +30,7 @@ export const editorStore = {
svgaCache: {},
svgaLoadingQueue: {},
assetSizeCache: {},
processEditorWin: null,
},
mutations: {
updateEnv(state, env) {
......@@ -44,15 +47,22 @@ export const editorStore = {
state.codeSyncServeConfig = config;
localStorage.setItem(storeKey, JSON.stringify(state.codeSyncServeConfig));
},
updateProcessEditorWin(state, win) {
state.processEditorWin = win;
}
},
getters: {
assetSize: state => url => {
let size = state.assetSizeCache[url];
if(size){
if (size) {
return `${size.width}x${size.height}`
}
return '0x0';
}
},
behaviorEditable: state => () => {
let win = state.processEditorWin;
return !win || win.closed || !win.editable;
},
},
actions: {
async updateEnv({state, commit}) {
......@@ -125,3 +135,24 @@ export const editorStore = {
},
}
};
export async function uploadFiles(files) {
const failedList = [];
let ps = [];
for (let file of files) {
events.$emit('upload-indicator', true);
ps.push(
editorApi.uploadFile(file).catch(e => {
failedList.push(file);
}).finally(() => {
events.$emit('upload-indicator', false);
})
);
}
const result = await Promise.all(ps);
return {
failedList,
result,
}
}
......@@ -4,10 +4,11 @@
import db from "../../utils/db-storage";
import {packageApi} from "../../api";
import maxSatisfying from "semver/ranges/max-satisfying";
import semverMaxSatisfying from "semver/ranges/max-satisfying";
import semverRSort from "semver/functions/rsort";
import i18n from "../../i18n";
import events from "@/global-events.js"
import {timeFormat, typeMapping} from "../../utils";
import {clonePureObj, timeFormat, typeMapping} from "../../utils";
const storeName = 'packages';
......@@ -30,15 +31,39 @@ export const packageStore = {
customProcesses: [],
},
mutations: {
resetPackageStore(state, sourceState) {
state.packageInfos = sourceState.packageInfos;
state.packages = sourceState.packages;
let builtins = state.processTree.find(item=>item.name === 'normal').children.find(item=>item.name === 'builtin');
builtins.children = sourceState.processTree.find(item=>item.name === 'normal').children.find(item=>item.name === 'builtin').children;
},
updatePackageInfos(state, packageInfos) {
for (let i = 0, li = typeMapping.length; i < li; i++) {
state.packageInfos[typeMapping[i]].splice(0);
}
for (let packageInfo of packageInfos) {
packageInfo.versions = packageInfo.versions.split(',').reverse();
packageInfo.remarks = packageInfo.remarks.split(',').reverse();
packageInfo.update_times = packageInfo.update_times.split(',').map(time => timeFormat(time)).reverse();
packageInfo.update_time = timeFormat(packageInfo.update_time);
let {versions, remarks, update_times, update_time} = packageInfo;
versions = versions.split(',');
remarks = remarks.split(',');
update_times = update_times.split(',').map(time => timeFormat(time));
let sortedVersions = semverRSort(versions.concat());
let sortedRemarks = [];
let sortedUpdate_times = [];
for (let i = 0, li = sortedVersions.length; i < li; i++) {
const version = sortedVersions[i];
let index = versions.indexOf(version);
sortedRemarks[i] = remarks[index];
sortedUpdate_times[i] = update_times[index];
}
packageInfo.versions = sortedVersions;
packageInfo.remarks = sortedRemarks;
packageInfo.update_times = sortedUpdate_times;
packageInfo.update_time = timeFormat(update_time);
state.packageInfos[typeMapping[packageInfo.type]].push(packageInfo);
}
},
......@@ -185,7 +210,7 @@ export const packageStore = {
for (let packageInfo of state.packageInfos[group]) {
const {package_id, versions} = packageInfo;
if (!dependencies[package_id]) {
dependencies[package_id] = maxSatisfying(versions, '*');
dependencies[package_id] = semverMaxSatisfying(versions, '*');
}
}
}
......
......@@ -17,23 +17,25 @@ import {
traverseViewNode
} from "../../utils"
import {template} from "../../template"
import events from "@/global-events"
import {packImages} from "../../utils/sheet-pack"
import {packAssetsGroups} from "../../utils/assets-pack"
import {addBehavior, deleteProcessMeta,} from "./behavior"
import db from "../../utils/db-storage"
import {preprocess} from "../../views/Preview/preview-preprocess"
import {packageStore} from "./package"
import {fetchApi} from "../../api/common"
import {toZeroing} from "psd-parse-web"
import {arrayFind} from "element-ui/src/utils/util";
import {uploadFiles} from "./editor";
const storeName = 'project';
const psStoreName = 'pack-history';
const defaultOptions = {
behaviorEditor: 'builtin',
pageTitle: 'no title',
entrySceneView: '',
loadingView: '',
lazyLoadWait: 1000,
containerId: 'game-container',
designWidth: 750,
designHeight: 1334,
......@@ -65,6 +67,7 @@ export const projectStore = {
operator: '',
operators: '',
update_time: '',
remark: '',
data: {
options: {},
views: [],
......@@ -79,11 +82,11 @@ export const projectStore = {
activeComponent: {},
activeComponentCopy: {}, // 当前选中节点的镜像,用来处理拖拽时数据变化频繁的问题
activeIdList: [],
activeViews: '',
dragUUID: '',
dirty: false,
operateStack: [],
stackIndex: 0,
activeView: null,
},
mutations: {
setDirty(state, dirty = true) {
......@@ -92,26 +95,30 @@ export const projectStore = {
makeProjectDirty() {
},
updateProjectUpdateTime(state, updateTime) {
state.update_time = updateTime;
resetProjectData(state, data) {
state.data = data;
},
updateProjectUpdateTime(state, {time, dirty = true}) {
state.update_time = time;
if (dirty) {
this.commit('makeProjectDirty');
}
},
updateProject(state, project) {
const {id, name, creator, data, operators, operator, update_time} = project;
const {id, name, creator, data, operators, operator, update_time, remark} = project;
state.id = id;
state.name = name;
state.creator = creator;
state.operator = operator;
state.operators = operators || creator; //兼容未保存的本地存储
state.update_time = update_time;
state.remark = remark;
const localData = state.data;
if (data) {
const {views, assets, dataMapping, processes, options, customs, mock, dependencies} = typeof data === 'string' ? JSON.parse(data) : data;
if (!options.pxEnv) {
options.pxEnv = getDefaultOptions().pxEnv;
}
Vue.set(localData, 'options', options || getDefaultOptions());
Vue.set(localData, 'views', views || []);
Vue.set(localData, 'assets', assets || []);
......@@ -131,11 +138,56 @@ export const projectStore = {
Vue.set(localData, 'dependencies', {});
}
if (!localData.options.pxEnv) {
localData.options.pxEnv = getDefaultOptions().pxEnv;
}
if (!localData.options.behaviorEditor) {
localData.options.behaviorEditor = 'builtin';
}
this.dispatch('fillLastVersion');
this.commit('updateAssetDep');
state.mockServeEnabled = getMockServeEnabled(id);
updateMock(localData.mock);
},
updateRemark(state, remark) {
state.remark = remark;
},
updateAssetDep(state) {
let assets = state.data.assets;
let explicitDepAssets;
for (let view of state.data.views) {
if (!view.implicitDepAssets) {
Vue.set(view, 'implicitDepAssets', []);
}
if (view.explicitDepAssets) {
view.explicitDepAssets.splice(0);
} else {
Vue.set(view, 'explicitDepAssets', []);
}
explicitDepAssets = view.explicitDepAssets;
onHitViewNode(view);
traverseViewNode(view.children, onHitViewNode);
}
function onHitViewNode(node) {
for (let field in node.properties) {
let prop = node.properties[field];
if (prop && typeof prop === 'string' && prop.startsWith(assetScheme) && !prop.includes('${')) {
let uuid = prop.replace(assetScheme, '');
let asset = assets.find(asset => asset.uuid === uuid);
if (asset) {
if (!explicitDepAssets.includes(asset.uuid)) {
explicitDepAssets.push(asset.uuid);
}
}
}
}
}
},
modifyOptions(state, value) {
state.data.options = value;
},
......@@ -172,6 +224,14 @@ export const projectStore = {
state.activeComponentCopy = _.cloneDeep(state.activeComponent);
}
},
/**
* 设置当前视图
* @param state
* @param view
*/
setActiveView(state, view) {
state.activeView = view;
},
/**
* 修改当前选中的节点
* @param {*} state
......@@ -360,7 +420,7 @@ export const projectStore = {
let _node1 = node;
console.log(_node1)
copyNodeCatch = clonePureObj(_node1)
console.log("copyNodeCatch",copyNodeCatch)
console.log("copyNodeCatch", copyNodeCatch)
if (copyState == 1) {
//复制行为
copyNodeCatch.events
......@@ -369,19 +429,20 @@ export const projectStore = {
console.log(this)
deleteChildEvent(copyNodeCatch)
}
//copy(JSON.stringify(copyNodeCatch))
function deleteChildEvent(copyNodeCatch){
function deleteChildEvent(copyNodeCatch) {
console.log(123)
delete copyNodeCatch.events;
if(copyNodeCatch.children){
for(let item of copyNodeCatch.children){
deleteChildEvent(item)
}
if (copyNodeCatch.children) {
for (let item of copyNodeCatch.children) {
deleteChildEvent(item)
}
}
}
},
pasteNode(state, {node, parentNode, pasteState}) {
let _node1 = copyNodeCatch;
console.log(copyNodeCatch);
......@@ -438,9 +499,11 @@ export const projectStore = {
})
},
replaceAsset(state, {uuid, url}) {
const ext = path.extname(url);
for (let asset of state.data.assets) {
if (asset.uuid === uuid) {
asset.url = url;
asset.ext = ext;
}
}
},
......@@ -486,6 +549,20 @@ export const projectStore = {
Vue.delete(state.activeComponent.properties, cmd);
this.commit('makeProjectDirty');
},
modifyViewDepAssets(state, {view, explicitDepAssets, implicitDepAssets, depCustoms}) {
view.explicitDepAssets = explicitDepAssets;
view.implicitDepAssets = implicitDepAssets;
view.depCustoms = depCustoms;
this.commit('makeProjectDirty');
},
overwriteProcesses(state, {processes}) {
state.data.processes.splice(0);
state.data.processes.push(...processes);
this.commit('makeProjectDirty');
},
},
getters: {
project(state) {
......@@ -499,6 +576,12 @@ export const projectStore = {
data: JSON.stringify(newData),
};
},
projectRemark(state) {
return state.remark;
},
behaviorEditor(state) {
return state.data.options.behaviorEditor;
},
menuBadge: (state) => (key) => {
let result = false;
switch (key) {
......@@ -539,8 +622,8 @@ export const projectStore = {
// 如果有选中的节点,则展示对应的视图组
// 否则展示第一个视图组
let _view = state.data.views.length ? [].concat(state.data.views[0]) : [];
if (state.activeViews) {
_view = state.data.views.filter(v => v.uuid === state.activeViews)
if (state.activeView) {
_view = state.data.views.filter(v => v.uuid === state.activeView.data.uuid)
}
let result = flattenViews(_.cloneDeep(_view));
......@@ -554,8 +637,8 @@ export const projectStore = {
// 如果有选中的节点,则展示对应的视图组
// 否则展示第一个视图组
let _view = [];
if (state.activeViews) {
_view = state.data.views.filter(v => v.uuid === state.activeViews)
if (state.activeView) {
_view = state.data.views.filter(v => v.uuid === state.activeView.data.uuid)
} else {
_view = state.data.views.length ? [].concat(state.data.views[0]) : [];
}
......@@ -565,6 +648,14 @@ export const projectStore = {
assets(state) {
return state.data.assets;
},
assetDepFlag: state => uuid => {
return state.data.views.filter(view =>
view.explicitDepAssets && view.explicitDepAssets.includes(uuid) || view.implicitDepAssets && view.implicitDepAssets.includes(uuid)
).length > 0 ? 0 : -1;
},
customs(state) {
return state.data.customs;
},
getProcess: state => behavior => {
return arrayFind(state.data.processes, process => process.id === behavior.meta);
},
......@@ -630,7 +721,10 @@ export const projectStore = {
let json = await db.get(storeName, projectID);
//let json = localStorage.getItem('project-' + projectID);
if (json) {
const remoteProject = await projectApi.fetchOne(projectID, false);
const project = JSON.parse(json.data);
project.operators = remoteProject.operators;
project.remark = remoteProject.remark;
commit('updateProject', project);
commit('setDirty', true);
}
......@@ -666,17 +760,22 @@ export const projectStore = {
commit('updateProject', project);
},
async saveToRemote({state, dispatch, getters, commit}, {remark, data}) {
if (data) { //如果已经合并了冲突
commit('updateProjectUpdateTime', {time: state.base_time});
}
let project = Object.assign({}, getters.project);
if (data) {
project.data = data;
project.force = true;
}
let resp = await projectApi.saveOne(project, remark);
if (resp.result) {
commit('updateProjectUpdateTime', resp.project.update_time);
commit('updateProjectUpdateTime', {time: resp.project.update_time, dirty: false});
dispatch('deleteLocalVersion', state.id);
}
resp.localData = project.data;
resp.localData = data || project.data;
if (!resp.result) {
state.base_time = resp.updateTime;
}
return resp;
},
async updateProject({commit}, projectID) {
......@@ -689,7 +788,7 @@ export const projectStore = {
* @param {*} context
* @param {*} data
*/
activeComponent(context, data) {
activeComponent({state, commit}, data) {
// debugger;
let getTopView = node => {
if (node.parent && !node.parent.parent) {
......@@ -698,26 +797,27 @@ export const projectStore = {
return getTopView(node.parent);
}
};
let _view = getTopView(data.node);
if (!data.fromPlayground) {
// 点击视图区域选中节点
// 则需要切换当前节点所在的最顶层视图
if (_view && _view.data) {
context.state.activeViews = _view.data.uuid;
}
}
let viewRoot = getTopView(data.node);
if (_view.data === data.data) {
if (!data.data.hasOwnProperty('$isRootView')) {
Object.defineProperty(data.data, '$isRootView', {
if (state.activeView !== viewRoot) {
commit('setActiveView', viewRoot);
/*if (!data.fromPlayground) {
context.state.activeViews = viewRoot.data.uuid;
}*/
}
if (viewRoot.data === data.data) {
if (!data.data.hasOwnProperty('$isViewRoot')) {
Object.defineProperty(data.data, '$isViewRoot', {
get() {
return true;
}
},
enumerable: false,
})
}
}
context.commit('activeComponent', data.data);
commit('activeComponent', data.data);
},
/**
* 修改属性
......@@ -726,10 +826,20 @@ export const projectStore = {
// debugger;
// 如果当前修改的是“来源”属性,节点又没有高度宽度,则取图片的高度宽度
let _props = _.cloneDeep(state.activeComponent.properties);
let hasAssetsDep = false;
_.forIn(props, (value, key) => {
/*if (value && value.startsWith(assetScheme) ||
_props[key] && _props[key].startsWith(assetScheme)) {
hasAssetsDep = true;
}*/
_props[key] = value;
});
/*if (hasAssetsDep) {
commit('updateAssetDep');
}*/
commit('updateAssetDep');
if (_props.source) {
// imageWidth imageHeight
// 设置image类型节点的原图高宽度
......@@ -836,6 +946,7 @@ export const projectStore = {
return failedList;
},
async replaceAsset({commit}, {uuid, file}) {
console.log(file);
const {failedList, result} = await uploadFiles([file]);
commit('replaceAsset', {uuid, url: result[0].url,});
return failedList;
......@@ -844,8 +955,9 @@ export const projectStore = {
let debug = params.debug;
let packedAssets;
//if (!debug) {
packedAssets = await packAssets(state.data.assets);
packedAssets = await packAssetsGroups(state.data.views, state.data.assets);
//}
console.log(packedAssets);
const packResult = await projectApi.pack(state.id, debug, packedAssets);
console.log(packResult);
......@@ -895,111 +1007,14 @@ export const projectStore = {
localStorage.setItem('mock-enabled-' + state.id, JSON.stringify(enabled));
commit('setMockServeEnabled', enabled);
return enabled;
}
},
async saveRemark({state, commit}, {remark}) {
await projectApi.updateRemark(state.id, remark);
commit('updateRemark', remark);
},
},
};
async function uploadFiles(files) {
const failedList = [];
let ps = [];
for (let file of files) {
events.$emit('upload-indicator', true);
ps.push(
editorApi.uploadFile(file).catch(e => {
failedList.push(file);
}).finally(() => {
events.$emit('upload-indicator', false);
})
);
}
const result = await Promise.all(ps);
return {
failedList,
result,
}
}
async function getSheetUrlByUUID(uuid) {
const response = await fetchApi('/api/uuid2url', {
params: {uuid},
errMessage: 'Failed to get url',
});
return response.url
}
async function packAssets(assets) {
let failedList = [];
let newAssets = assets.concat();
await packImages(newAssets, {getSheetUrlByUUID});
await Promise.all(newAssets.map(asset=>{
return new Promise(async (resolve, reject)=>{
try {
if (asset.file) {
const {url} = await editorApi.uploadFile(asset.file, true).catch(e => {
failedList.push(asset);
});
let sheetConfig = {
file: url,
type: 'sheet',
frames: asset.frames,
};
let sheetConfigFile = new File([JSON.stringify(sheetConfig)], 'sheet.json', {type: 'plain/text'});
const {url: sheetConfigUrl} = await editorApi.uploadFile(sheetConfigFile, false, asset.sheetUUID).catch(e => {
failedList.push(asset)
});
asset.url = sheetConfigUrl;
asset.uuid = generateUUID();
delete asset.file;
delete asset.frames;
delete asset.sheetUUID;
}
resolve();
}catch (e) {
reject(e);
}
})
}));
/*for (let asset of newAssets) {
if (asset.file) {
const {url} = await editorApi.uploadFile(asset.file, true).catch(e => {
failedList.push(asset);
});
let sheetConfig = {
file: url,
type: 'sheet',
frames: asset.frames,
};
let sheetConfigFile = new File([JSON.stringify(sheetConfig)], 'sheet.json', {type: 'plain/text'});
const {url: sheetConfigUrl} = await editorApi.uploadFile(sheetConfigFile, false, asset.sheetUUID).catch(e => {
failedList.push(asset)
});
asset.url = sheetConfigUrl;
asset.uuid = generateUUID();
delete asset.file;
delete asset.frames;
delete asset.sheetUUID;
}
}*/
return newAssets;
}
export async function updateMock(mocks) {
await db.clear('mock');
for (let mock of mocks) {
db.set('mock', mock);
}
}
function getDefaultOptions() {
return clonePureObj(defaultOptions)
......@@ -1041,3 +1056,10 @@ function copyBaseRoot(node) {
}
return data
}
export async function updateMock(mocks) {
await db.clear('mock');
for (let mock of mocks) {
db.set('mock', mock);
}
}
......@@ -9,8 +9,7 @@ export const template =
<head>
<meta charset="UTF-8">
<title>$PAGE_TITLE$</title>
<meta name="viewport"
content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
<meta name="viewport" content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="full-screen" content="true"/>
<meta name="screen-orientation" content="portrait"/>
......@@ -29,11 +28,17 @@ content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, u
position: absolute;
background-color: white;
}
.game-container{
width: 100%;
height: 100%;
line-height:0;
font-size:0;
}
</style>
</head>
<body>
<div id="$CONTAINER_ID$" style="line-height:0;font-size:0"></div>
<div id="$CONTAINER_ID$" class="game-container"></div>
$LIBRARIES.engine$
$SCRIPTS$
<script>
......
......@@ -30,7 +30,7 @@
font-size: 12px;
line-height: 14px;
color: $--color-text-primary;
border-right: 1px solid $--border-color-base;
border-left: 1px solid $--border-color-base;
.item{
border-bottom: 1px solid $--border-color-base;
......@@ -67,11 +67,11 @@
.file-list {
display: flex;
flex: 1;
flex-direction: row;
flex-wrap: wrap;
.file-item {
position: relative;
width: 60px;
padding: 5px;
color: $--color-text-regular;
......@@ -147,6 +147,25 @@
line-height: 17px;
}
}
.dep-flag{
position: absolute;
left: 0;
top: 0;
width: 0;
height: 0;
border-top: 10px solid black;
border-right: 10px solid transparent;
border-top-color: $--color-primary;
&.explicit{
border-top-color: $--color-danger;
}
&.implicit{
border-top-color: $--color-success;
}
}
}
.file-uploader {
......@@ -197,6 +216,7 @@
}
.item{
padding: 3px 0;
.field{
width: 30px;
display: block;
......
......@@ -13,6 +13,12 @@ html, body, #app {
font-family: Arial, sans-serif;
}
.gap-5{
*:not(:first-child){
margin-left: 5px;
}
}
.modal-layer{
position: absolute;
top: 0;
......
......@@ -4,322 +4,344 @@ $dock-pin-width: 9px;
.behavior-editor-dialog {
.process-pane {
}
.process-editor-dialog {
flex: 1;
display: flex;
flex-direction: column;
.editor-wrapper{
flex: 1;
display: flex;
height: 0;
}
.behavior-editor {
flex: 1;
}
.process-tree {
.bottom-bar {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 10px;
}
}
.process-pane {
}
.process-tree {
display: flex;
flex-direction: column;
.scrollbar {
.scrollbar {
flex: 1;
.process-tree-node {
flex: 1;
flex-direction: row;
display: flex;
padding-right: 10px;
.process-tree-node {
.node-name {
flex: 1;
flex-direction: row;
display: flex;
padding-right: 10px;
.node-name {
flex: 1;
width: 0;
text-overflow: ellipsis;
overflow: hidden;
font-size: 14px;
.current-node {
background-color: $--color-text-secondary;
color: $--color-white;
border-radius: 5px;
padding: 0 2px;
}
}
width: 0;
text-overflow: ellipsis;
overflow: hidden;
font-size: 14px;
.edit-button {
visibility: hidden;
.current-node {
background-color: $--color-text-secondary;
color: $--color-white;
border-radius: 5px;
padding: 0 2px;
}
}
.edit-button {
visibility: hidden;
}
}
}
}
.behavior {
border: 1px solid $--border-color-base;
.behavior {
//border: 1px solid $--border-color-base;
.background {
background-color: $--background-color-base;
.background {
background-color: $--background-color-base;
}
.center {
display: flex;
flex-direction: column;
.el-button {
padding: 3px;
margin-top: 2px;
margin-right: 2px;
}
.center {
.top-bar {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
.edit-path {
flex: 1;
padding: 5px;
height: 14px;
.el-button {
padding: 3px;
margin-top: 2px;
margin-right: 2px;
border-bottom: 1px solid $--border-color-light;
}
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: flex-start;
.edit-path {
flex: 1;
padding: 5px;
height: 14px;
.operate-bar {
padding: 3px;
border-bottom: 1px solid $--border-color-light;
border-bottom: 1px solid $--border-color-light;
}
}
.board {
flex: 1;
.svg-board {
.line {
stroke: #979797;
&.hover, &:hover {
stroke: $--color-primary;
stroke-dasharray: 5, 1;
}
&.contact {
stroke: $--color-primary;
}
}
.node {
display: flex;
flex-direction: column;
min-width: 100px;
background-color: $--background-color-base;
border: 1px solid $block-border-blur-background-color;
position: relative;
border-radius: 5px;
outline: none;
user-select: none;
margin: 0 $dock-pin-width;
&:hover {
& > .top-bar {
visibility: visible;
}
}
.operate-bar {
padding: 3px;
/*&:hover {
border-color: $block-border-hover-background-color;
border-bottom: 1px solid $--border-color-light;
}
}
& > .header {
background-color: $block-border-hover-background-color;
}
}*/
.board {
flex: 1;
/*&:focus {
border-color: $block-border-focus-background-color;
.svg-board {
.line {
stroke: #979797;
& > .header {
background-color: $block-border-focus-background-color;
}
}*/
.top-bar {
height: 19px;
padding: 0 3px;
margin: -20px 5px 0;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
background-color: rgba(0, 0, 0, 0.1);
align-self: flex-end;
visibility: hidden;
&.hover, &:hover {
stroke: $--color-primary;
stroke-dasharray: 5, 1;
.el-link {
padding: 3px 2px 2px 2px;
}
&.contact {
stroke: $--color-primary;
.el-link + .el-link {
margin-left: 5px;
}
}
.node {
.header {
min-height: 12px;
background-color: $block-border-blur-background-color;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
padding: 3px;
font-size: 12px;
color: white;
display: flex;
flex-direction: column;
min-width: 100px;
background-color: $--background-color-base;
border: 1px solid $block-border-blur-background-color;
position: relative;
border-radius: 5px;
outline: none;
user-select: none;
margin: 0 $dock-pin-width;
&:hover {
& > .top-bar {
visibility: visible;
}
i {
display: block;
//color: $--color-warning;
margin-right: 3px;
}
/*&:hover {
border-color: $block-border-hover-background-color;
.title {
flex: 1;
}
}
& > .header {
background-color: $block-border-hover-background-color;
}
}*/
.body {
display: flex;
flex-direction: column;
padding: 3px;
font-size: 12px;
color: $--color-text-regular;
/*&:focus {
border-color: $block-border-focus-background-color;
.field-item {
display: flex;
align-items: center;
& > .header {
background-color: $block-border-focus-background-color;
}
}*/
.top-bar {
height: 19px;
padding: 0 3px;
margin: -20px 5px 0;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
background-color: rgba(0, 0, 0, 0.1);
align-self: flex-end;
visibility: hidden;
.el-link {
padding: 3px 2px 2px 2px;
}
& > * {
flex: 1;
.el-link + .el-link {
margin-left: 5px;
white-space: nowrap;
overflow: hidden;
}
}
.header {
min-height: 12px;
background-color: $block-border-blur-background-color;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
padding: 3px;
font-size: 12px;
color: white;
display: flex;
.key {
i {
display: block;
//color: $--color-warning;
margin-right: 3px;
}
.title {
flex: 1;
}
}
.body {
display: flex;
flex-direction: column;
padding: 3px;
font-size: 12px;
color: $--color-text-regular;
.field-item {
.linked-value {
display: flex;
align-items: center;
justify-content: flex-end;
}
& > * {
flex: 1;
.string-value {
text-align: right;
white-space: nowrap;
overflow: hidden;
.tag {
color: $--color-success;
}
}
.key {
.color-value {
width: 15px;
height: 15px;
border-radius: 3px;
float: right;
border: 1px solid $--border-color-base;
}
}
}
}
.linked-value {
display: flex;
align-items: center;
justify-content: flex-end;
}
.dock {
position: absolute;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
.string-value {
text-align: right;
.pin {
border: 1px solid $block-border-blur-background-color;
padding: 1px;
margin-bottom: 5px;
background-color: white;
.tag {
color: $--color-success;
}
}
.color-value {
width: 15px;
height: 15px;
border-radius: 3px;
float: right;
border: 1px solid $--border-color-base;
}
div {
width: 3px;
height: 3px;
border: 1px solid $block-border-blur-background-color;
border-radius: 3px;
}
}
.dock {
position: absolute;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
.pin {
border: 1px solid $block-border-blur-background-color;
padding: 1px;
margin-bottom: 5px;
background-color: white;
div {
width: 3px;
height: 3px;
border: 1px solid $block-border-blur-background-color;
border-radius: 3px;
}
&:hover {
border-color: $--color-primary;
&:hover {
& > div {
border-color: $--color-primary;
& > div {
border-color: $--color-primary;
}
}
}
&:last-child {
margin-bottom: 0;
}
&:last-child {
margin-bottom: 0;
}
}
}
.input {
@extend .dock;
left: -$dock-pin-width;
}
.input {
@extend .dock;
left: -$dock-pin-width;
}
.output {
@extend .dock;
right: -$dock-pin-width;
}
.output {
@extend .dock;
right: -$dock-pin-width;
}
}
.active {
border-color: $block-border-focus-background-color;
.active {
border-color: $block-border-focus-background-color;
& > .header {
background-color: $block-border-focus-background-color;
}
/*& > .top-bar {
visibility: visible;
}*/
& > .header {
background-color: $block-border-focus-background-color;
}
/*& > .top-bar {
visibility: visible;
}*/
}
}
}
.properties {
display: flex;
flex-direction: column;
.properties {
display: flex;
flex-direction: column;
.wrapper {
padding: 5px;
flex: 1;
display: flex;
flex-direction: column;
.wrapper {
padding: 5px;
flex: 1;
display: flex;
flex-direction: column;
.name-input {
.name-input {
}
}
.scrollbar {
margin-right: -5px;
padding-top: 5px;
flex: 1;
.scrollbar {
margin-right: -5px;
padding-top: 5px;
flex: 1;
.scrollbar-view {
padding-right: 10px;
}
.scrollbar-view {
padding-right: 10px;
}
}
.inside-code {
flex: 1;
height: 0;
border-top: 1px solid $--border-color-base;
.inside-code {
flex: 1;
height: 0;
border-top: 1px solid $--border-color-base;
.scrollbar {
height: 100%;
.scrollbar {
height: 100%;
.code-body {
tab-size: 1.5em;
width: 100%;
margin: 0;
font-size: 12px;
white-space: pre-wrap;
}
.code-body {
tab-size: 1.5em;
width: 100%;
margin: 0;
font-size: 12px;
white-space: pre-wrap;
}
}
}
......
......@@ -31,10 +31,6 @@
.right-part {
color: $--color-text-regular;
line-height: 15px;
.el-button {
padding: 2px;
}
}
}
......@@ -347,45 +343,6 @@
}
}
.store-editor-dialog {
.wrapper {
height: 50vh;
.tabs {
height: 100%;
display: flex;
flex-direction: column;
.el-tabs__content {
flex: 1;
& > div {
height: 100%;
}
.editor {
width: 100%;
height: 100%;
}
.computed-editor{
.editor{
width: 100%;
height: 200px;
}
}
}
}
}
.dialog-footer{
.save-button{
margin-left: 10px;
}
}
}
.px-publish-view {
display: flex;
flex-direction: column;
......@@ -581,3 +538,21 @@
.local-change-marker-class {
background-color: rgba(64, 166, 255, 0.5);
}
.project-remark-dialog{
.wrapper{
height: 100%;
.editor{
height: 100%;
}
.project-scrollbar{
height: 100%;
.project-remark{
padding: 5px;
}
}
}
}
......@@ -133,3 +133,100 @@
}
}
}
.store-editor-dialog {
.wrapper {
height: 50vh;
.tabs {
height: 100%;
display: flex;
flex-direction: column;
.el-tabs__content {
flex: 1;
& > div {
height: 100%;
}
.editor {
width: 100%;
height: 100%;
}
.computed-editor {
.editor {
width: 100%;
height: 200px;
}
}
}
}
}
.dialog-footer {
.save-button {
margin-left: 10px;
}
}
}
.asset-dependence-dialog {
.wrapper {
display: flex;
flex-direction: column;
height: 50vh;
.assets-dep {
flex: 1;
height: 0;
display: flex;
flex-direction: column;
margin-bottom: 5px;
.explicit-tags {
display: flex;
div + div {
margin-left: 10px;
}
div {
display: flex;
align-items: center;
div {
width: 10px;
height: 10px;
margin-right: 2px;
}
.explicit {
background-color: $--color-danger;
}
.implicit {
background-color: $--color-success;
}
}
}
.assets-scrollbar {
flex: 1;
border: 1px solid $--border-color-light;
}
}
.custom-dep {
display: flex;
align-items: center;
.select{
flex: 1;
}
}
}
}
/**
* Created by rockyl on 2020-04-29.
*/
import {packImages} from "./sheet-pack";
import {editorApi} from "../api";
import generateUUID from "uuid/v4";
import {fetchApi} from "../api/common";
export async function packAssetsGroups(views, assets) {
let depCountingMap = {};
for (let view of views) {
const {explicitDepAssets, implicitDepAssets} = view;
let depAssets = [...explicitDepAssets, ...implicitDepAssets];
for (let depAsset of depAssets) {
if (!depCountingMap.hasOwnProperty(depAsset)) {
depCountingMap[depAsset] = [];
}
depCountingMap[depAsset].push(view.uuid);
}
}
let assetsGroups = {
common: [],
};
for (let asset of assets) {
let depCountingViews = depCountingMap[asset.uuid];
if (!depCountingViews) {
continue;
}
let depCounting = depCountingViews.length;
if (depCounting > 0) {
if (depCounting > 1) { //依赖引用数大于1则放入通用分组
assetsGroups.common.push(asset);
} else {
let depView = depCountingViews[0];
if (!assetsGroups.hasOwnProperty(depView)) {
assetsGroups[depView] = [];
}
assetsGroups[depView].push(asset);
}
}
}
let ps = [];
for (let key in assetsGroups) {
let group = assetsGroups[key];
ps.push(packAssets(key, group));
}
const groups = await Promise.all(ps);
for (let group of groups) {
assetsGroups[group.key] = group.assets;
}
return assetsGroups;
}
async function getSheetUrlByUUID(uuid) {
const response = await fetchApi('/api/uuid2url', {
params: {uuid},
errMessage: 'Failed to get url',
});
return response.url
}
async function packAssets(key, assets) {
let newAssets = assets.concat();
if (assets.length === 0) {
return {
key,
assets,
};
}
let failedList = [];
await packImages(newAssets, {getSheetUrlByUUID});
await Promise.all(newAssets.map(asset => {
return new Promise(async (resolve, reject) => {
try {
if (asset.file) {
await uploadSheet(asset).catch(e => {
failedList.push(asset);
});
}
resolve();
} catch (e) {
reject(e);
}
})
}));
return {
key,
assets: newAssets,
};
}
async function uploadSheet(asset) {
const {url} = await editorApi.uploadFile(asset.file, true);
let sheetConfig = {
file: url,
type: 'sheet',
frames: asset.frames,
};
let sheetConfigFile = new File([JSON.stringify(sheetConfig)], 'sheet.json', {type: 'plain/text'});
const {url: sheetConfigUrl} = await editorApi.uploadFile(sheetConfigFile, false, asset.sheetUUID);
asset.url = sheetConfigUrl;
asset.uuid = generateUUID();
delete asset.file;
delete asset.frames;
delete asset.sheetUUID;
}
......@@ -426,6 +426,61 @@ export function deleteAssetsDepConfig(data) {
}
}
export function formatJson(source){
export function formatJson(source) {
return source ? JSON.stringify(JSON.parse(source), null, '\t') : '';
}
let transCanvas = document.createElement('canvas');
export async function transPngToJpg(img, type = 'blob', option) {
const quality = option ? option.quality : 70;
const bgColor = option ? option.bgColor : '#FFFFFF';
if (!img) {
return;
}
let imgOrigin;
if (typeof img === 'string') {
imgOrigin = new Image();
imgOrigin.crossOrigin = 'anonymous';
imgOrigin.src = img;
await new Promise((resolve, reject) => {
imgOrigin.onload = resolve;
imgOrigin.onerror = reject;
})
} else {
imgOrigin = img;
}
const {width, height} = imgOrigin;
transCanvas.width = width;
transCanvas.height = height;
let ctx = transCanvas.getContext('2d');
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, width, height);
ctx.drawImage(imgOrigin, 0, 0);
let dataUrl = transCanvas.toDataURL('image/jpeg', quality / 100);
let result;
switch (type) {
case 'img':
let imgR = new Image();
imgR.src = dataUrl;
result = imgR;
break;
case 'blob':
result = dataURLtoBlob(dataUrl);
break;
}
return result;
}
export function dataURLtoBlob(dataUrl) {
var arr = dataUrl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type: mime});
}
......@@ -131,6 +131,26 @@ export default {
type: 'color',
default: '#000'
},
shadowBlur: {
alias: '阴影模糊度',
type: 'string',
default: 0
},
shadowColor: {
alias: '阴影颜色',
type: 'color',
default: '#000'
},
shadowOffsetX: {
alias: '阴影偏移X',
type: 'number',
default: 0
},
shadowOffsetY: {
alias: '阴影偏移Y',
type: 'number',
default: 0
},
stroke: {
alias: '描边宽度',
type: 'number',
......
......@@ -10,6 +10,10 @@ import generateUUID from "uuid/v4";
const packExts = ['.png']; //, '.jpg', '.jpeg', '.bmp'
let canvas = document.createElement('canvas');
let canvasFinal = document.createElement('canvas');
let context = canvas.getContext('2d');
let contextFinal = canvasFinal.getContext('2d');
export async function packImages(assets, options = {}) {
const padding = options.padding || 1;
......@@ -60,16 +64,24 @@ export async function packImages(assets, options = {}) {
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);
let maxWidth = 0, maxHeight = 0;
for (let rect of packedRects) {
if (rect.x + rect.width > maxWidth) {
maxWidth = rect.x + rect.width;
}
if (rect.y + rect.height > maxHeight) {
maxHeight = rect.y + rect.height;
}
context.drawImage(rect.image, rect.x + padding, rect.y + padding);
}
canvasFinal.width = maxWidth;
canvasFinal.height = maxHeight;
contextFinal.drawImage(canvas, 0, 0);
let blob = await new Promise(resolve => {
canvas.toBlob(function (blob) {
canvasFinal.toBlob(function (blob) {
resolve(blob);
}, 'image/png');
});
......
......@@ -25,7 +25,7 @@
</template>
<script>
import {mapState, mapActions, mapMutations} from 'vuex'
import {mapState, mapActions, mapMutations, mapGetters} from 'vuex'
import SplitPanes from 'splitpanes'
import ToolBar from "./Editor/ToolBar";
import Inspector from "./Editor/Inspector";
......@@ -33,7 +33,7 @@
import Playground from "./Editor/Playground";
import Assets from "./Editor/Assets";
import DetailsDialog from "./Editor/dialogs/DetailsDialog";
import {openPreview, playWaiting} from "../utils";
import {clonePureObj, openPreview, playWaiting} from "../utils";
import i18n from "../i18n";
import events from "@/global-events.js"
import db from "../utils/db-storage";
......@@ -92,8 +92,16 @@
operators: (state) => state.project.operators,
currentOperator: (state) => state.editor.operator,
}),
...mapGetters(['behaviorEditor'])
},
async mounted() {
if (!window.saveProcesses) {
window.saveProcesses = (processes) => {
this.$nextTick(()=>{
this.saveProcesses(processes);
});
}
}
document.addEventListener('keydown', this.onKeyPress);
events.$on('save-and-preview', () => {
this.clickMenu("preview");
......@@ -143,7 +151,7 @@
async loadProject(forceRemote) {
const {projectID, project, dataUrl} = this.$route.params;
if (!forceRemote && await this.localVersionExist(projectID)) {
/*this.$confirm(this.$t('Unsaved version found locally'), this.$t('Alert'), {
/*this.$confirm(this.$t('Unsaved version found locally, choose one'), this.$t('Alert'), {
showClose: false,
closeOnClickModal: false,
closeOnPressEscape: false,
......@@ -220,6 +228,7 @@
type: 'success'
});
} else {
cancel = true;
await this.$confirm(i18n.t('There are conflicts in the project'), i18n.t('Alert'), {
confirmButtonText: i18n.t('Confirm'),
cancelButtonText: i18n.t('Cancel'),
......@@ -227,6 +236,7 @@
}).then(() => {
this.$refs.projectConflictResolveDialog.show(resp.remoteData, resp.localData);
}).catch((e) => {
this.$loading({}).close();
});
}
}
......@@ -235,8 +245,10 @@
},
async onConflictResolved(data) {
const {projectID, project} = this.$route.params;
await this.saveProject(true, data);
this.loadRemoteVersion(projectID, project, undefined, data);
let cancel = await this.saveProject(true, data);
if (!cancel) {
this.loadRemoteVersion(projectID, project, undefined, data);
}
},
checkAuth() {
let checkStatus = this.operators.includes(this.currentOperator);
......@@ -381,12 +393,55 @@
this.$refs.missingPackagesDialog.show(missingPackages);
},
editBehavior(path, index) {
this.$refs.behaviorEditorDialog.show(path, index);
switch (this.behaviorEditor) {
case 'builtin':
this.$refs.behaviorEditorDialog.show(path, index);
break;
case 'standalone':
this.onOpenStandalone(path, index);
break;
}
},
onOpenStandalone(path, index) {
let processEditorUrl = location.href.substr(0, location.href.indexOf('#')) + '#/process-editor';
let processEditorWin = this.processEditorWin = open(processEditorUrl, 'Process Editor', 'width=800, height=600');
this.updateProcessEditorWin(processEditorWin);
if (processEditorWin.editable) {
if (processEditorWin.editable()) {
this.showStandaloneEditor(path, index);
} else {
this.$message({
message: this.$t('A behavior is being edited. Please save it first'),
type: 'warning'
});
}
} else {
processEditorWin.onload = () => {
this.showStandaloneEditor(path, index);
}
}
},
showStandaloneEditor(path, index) {
let processEditorWin = this.processEditorWin;
let projectData = clonePureObj(this.$store.state.project.data);
let packageState = clonePureObj(this.$store.state.project.package);
processEditorWin.reset({projectData, packageState});
processEditorWin.edit(path, index);
},
saveProcesses(processes){
this.overwriteProcesses({processes});
this.$message({
message: this.$t('Save behavior successfully'),
type: 'success'
});
},
handleBehaviorsChange() {
this.makeProjectDirty();
},
...mapMutations(['makeProjectDirty']),
...mapMutations(['makeProjectDirty', 'overwriteProcesses', 'updateProcessEditorWin']),
...mapActions([
'loadPackages',
'localVersionExist',
......
......@@ -5,6 +5,14 @@
<el-link @click="deleteAll" :underline="false">{{$t('Delete all')}}</el-link>
</div>
<div class="container">
<el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view">
<asset-list ref="assetList" editable @show-file-details="showFileDetails" @click-item="onItemClick">
<div slot="first" class="file-uploader" @click="toUploadFile" @dragover.prevent @drop="onDropFile">
<i class="el-icon-plus file-uploader-icon"></i>
</div>
</asset-list>
</el-scrollbar>
<div class="properties">
<template v-if="currentItem">
<div class="item">
......@@ -14,24 +22,16 @@
<div class="item">
<p>
<span>size</span>:
<el-tag size="mini" type="success">size</el-tag>:
<span>{{assetSize(currentItem.url)}}</span>
</p>
<p v-for="(field, index) in showFields" :key="index">
<span>{{field}}</span>:
<el-tag size="mini">{{field}}</el-tag>:
<span>{{currentItem[field]}}</span>
</p>
</div>
</template>
</div>
<el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view">
<asset-list ref="assetList" editable @show-file-details="showFileDetails" @click-item="onItemClick">
<div slot="first" class="file-uploader" @click="toUploadFile" @dragover.prevent @drop="onDropFile">
<i class="el-icon-plus file-uploader-icon"></i>
</div>
</asset-list>
</el-scrollbar>
</div>
</div>
<assets-show ref="assetsShow"/>
......
......@@ -7,22 +7,39 @@
@click="onClickItem(asset, $event)"
@delete="onDeleteItem(asset)"
@combine="onCombineAssets"
@to-jpg="onToJpg"
/>
<trans-to-jpg-option-dialog ref="transToJpgOptionDialog" @confirm="onConfirmToTransJpg"/>
</div>
</template>
<script>
import {mapGetters, mapMutations} from 'vuex';
import {mapGetters, mapMutations, mapActions} from 'vuex';
import FileItem from "./FileItem";
import {transPngToJpg} from "../../../utils";
import TransToJpgOptionDialog from "./TransToJpgOptionDialog";
export default {
name: "AssetList",
components: {FileItem},
components: {TransToJpgOptionDialog, FileItem},
props: {
editable: {
type: Boolean,
default: false,
},
selectable: {
type: Boolean,
default: true,
},
assetsSource: {
type: Array,
},
explicitDepAssets: {
type: Array,
},
implicitDepAssets: {
type: Array,
},
},
data() {
return {
......@@ -32,17 +49,18 @@
computed: {
selectedAssets() {
let assets = [];
for(let index of this.selectedIndices){
for (let index of this.selectedIndices) {
assets.push(this.assets[index]);
}
return assets;
},
combinable(){
combinable() {
return this.selectedIndices.length > 1
},
...mapGetters([
'assets',
])
assets() {
return this.assetsSource ? this.assetsSource : this.$store.getters.assets;
},
...mapGetters(['assetDepFlag'])
},
methods: {
showFileDetails(asset) {
......@@ -50,6 +68,9 @@
},
onClickItem(asset, event) {
this.$emit('click-item', asset);
if (!this.selectable) {
return;
}
if (event.altKey) {
if (this.isSelected(asset)) {
this.unselectItem(asset);
......@@ -88,6 +109,18 @@
}).catch((e) => {
});
},
async onToJpg(asset) {
this.lastAsset = asset;
this.$refs.transToJpgOptionDialog.show();
},
async onConfirmToTransJpg(option){
let asset = this.lastAsset;
let blob = await transPngToJpg(asset.url, 'blob', option);
this.replaceAsset({
uuid: asset.uuid,
file: new File([blob], asset.name + '.jpg'),
});
},
selectItem(asset, unselectAll = true) {
if (unselectAll) {
this.selectedIndices.splice(0);
......@@ -109,7 +142,21 @@
}
return this.selectedIndices.includes(index);
},
getDepFlag(uuid) {
let flag = -1;
const {explicitDepAssets, implicitDepAssets} = this;
if (explicitDepAssets || implicitDepAssets) {
flag = explicitDepAssets.includes(uuid) ? 1 : -1;
if (flag < 0) {
flag = implicitDepAssets.includes(uuid) ? 2 : -1;
}
} else {
flag = this.assetDepFlag(uuid);
}
return flag;
},
...mapMutations(['combineAssets']),
...mapActions(['replaceAsset']),
}
}
</script>
......
......@@ -22,7 +22,9 @@
:command="key"
:key="key"
:disabled="key === 'combine' && !combinable"
>{{item}}</el-dropdown-item>
v-if="showMenuItem(key)"
>{{item}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
......@@ -43,6 +45,9 @@
<span>{{data.name}}</span>
<span class="full-name">{{data.name}}</span>
</div>
<div :class="{'dep-flag': depFlag >= 0, 'explicit': depFlag === 1, 'implicit': depFlag === 2}">
</div>
</div>
</template>
......@@ -58,7 +63,7 @@
props: {
data: {type: Object},
editable: {type: Boolean, default: false},
combinable: {type: Boolean, default: false}
combinable: {type: Boolean, default: false},
},
data() {
return {
......@@ -86,6 +91,9 @@
ellipsisName() {
return strEllipsis(this.data.name, 14, 5);
},
depFlag() {
return this.$parent.getDepFlag(this.data.uuid);
},
},
methods: {
onDbclick() {
......@@ -118,6 +126,9 @@
case 'combine':
this.$emit('combine', this.data);
break;
case 'to-jpg':
this.$emit('to-jpg', this.data);
break;
}
},
editName() {
......@@ -143,6 +154,14 @@
});
})
},
showMenuItem(key) {
const {ext} = this.data;
if (key === 'to-jpg') {
return ext === '.png';
} else {
return true;
}
},
...mapMutations([
'deleteAsset',
'modifyAsset',
......
<template>
<el-dialog :title="$t('Transform options')" width="70%" :visible.sync="visible" @opened="onOpen"
:append-to-body="true"
custom-class="flex-dialog details-dialog"
>
<el-form v-model="option" label-position="right" label-width="100px" size="mini" style="padding: 20px">
<el-form-item label="品质">
<el-input-number v-model="option.quality" :max="100" :min="10" controls-position="right"/>
</el-form-item>
<el-form-item label="底色">
<el-color-picker v-model="option.bgColor"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<div></div>
<div>
<el-button size="mini" @click="onClose">{{$t('Close')}}</el-button>
<el-button size="mini" @click="onConfirm" type="primary">{{$t('Confirm')}}</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import {mapMutations} from 'vuex';
import ElFormItem from "../../../components/inputs/form-item";
export default {
name: "TransToJpgOptionDialog",
components: {ElFormItem},
data() {
return {
visible: false,
option: {
quality: 70,
bgColor: '#ffffff',
}
}
},
methods: {
show() {
this.option.quality = 70;
this.option.bgColor = '#ffffff';
this.visible = true;
},
onConfirm() {
this.$emit('confirm', this.option);
this.visible = false;
},
onClose() {
this.visible = false;
},
onOpen() {
},
...mapMutations([
'modifyProjectDetails',
]),
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
......@@ -73,7 +73,7 @@
}
},
computed: {
...mapGetters(['activeComponent', 'componentList'])
...mapGetters(['activeComponent', 'componentList', 'behaviorEditable'])
},
methods: {
getBehavior(behavior) {
......@@ -89,20 +89,24 @@
}
},
addTrigger(command) {
let trigger = {behaviors: []};
switch (command) {
case 'custom-global':
case 'custom-private':
trigger.custom = true;
break;
}
if(this.behaviorEditable()){
let trigger = {behaviors: []};
switch (command) {
case 'custom-global':
case 'custom-private':
trigger.custom = true;
break;
}
if (command === 'custom-private') {
trigger.private = true;
}
if (command === 'custom-private') {
trigger.private = true;
}
this.addBehavior(command, trigger.behaviors);
this.$set(this.activeComponent.events, command, trigger);
this.addBehavior(command, trigger.behaviors);
this.$set(this.activeComponent.events, command, trigger);
}else{
this.$message('独立过程编辑器正在使用中,不可编辑')
}
},
toggleCollapse(trigger) {
this.$set(trigger, 'collapse', !trigger.collapse);
......@@ -122,13 +126,17 @@
}
},
addBehavior(eventName, behaviors) {
const alias = `${this.activeComponent.name}_${eventName}` + (behaviors.length > 0 ? '_' + (behaviors.length + 1) : '');
if(this.behaviorEditable()){
const alias = `${this.activeComponent.name}_${eventName}` + (behaviors.length > 0 ? '_' + (behaviors.length + 1) : '');
this.addBehaviorDirect({
alias, behaviors,
});
this.addBehaviorDirect({
alias, behaviors,
});
this.makeProjectDirty();
this.makeProjectDirty();
}else{
this.$message('独立过程编辑器正在使用中,不可编辑')
}
},
editTriggerName(name) {
this.$prompt(this.$t('Input event name'), this.$t('Rename event'), {
......@@ -167,24 +175,29 @@
this.makeProjectDirty();
},
async deleteBehavior(index, behaviors) {
let deleteMeta = false;
await this.$confirm(this.$t('Are you sure to delete it\'s process?'), this.$t('Alert'), {
confirmButtonText: this.$t('Delete'),
cancelButtonText: this.$t('Not delete'),
distinguishCancelAndClose: true,
type: 'warning'
}).then(() => {
deleteMeta = true;
this.makeProjectDirty();
}).catch(action => {
if(this.behaviorEditable()){
let deleteMeta = false;
await this.$confirm(this.$t('Are you sure to delete it\'s process?'), this.$t('Alert'), {
confirmButtonText: this.$t('Delete'),
cancelButtonText: this.$t('Not delete'),
distinguishCancelAndClose: true,
type: 'warning'
}).then(() => {
deleteMeta = true;
this.makeProjectDirty();
}).catch(action => {
});
});
this.deleteBehaviorDirect({
behaviors,
index,
deleteMeta,
})
}else{
this.$message('独立过程编辑器正在使用中,不可编辑')
}
this.deleteBehaviorDirect({
behaviors,
index,
deleteMeta,
})
},
...mapActions([
'modifyActiveView',
......
<template>
<div class="zero-inspector-props-form" v-if="activeComponent.uuid">
<el-form class="form" ref="form" size="mini" :model="form" label-width="100px" @submit.native.prevent>
<div class="top-bar" v-if="activeComponent.$isRootView">
<div class="top-bar" v-if="activeComponent.$isViewRoot">
<el-button size="mini" @click="clickEditStoreExp">{{$t('Edit store')}}</el-button>
<el-button size="mini" @click="clickEditStoreComputed">{{$t('Edit computed')}}</el-button>
<el-button size="mini" @click="clickAssetManager">{{$t('Asset dependence')}}</el-button>
</div>
<el-form-item :label="$t('Name')">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
......@@ -51,6 +52,7 @@
</el-scrollbar>
</el-form>
<store-editor-dialog ref="storeEditorDialog"/>
<asset-dependence-dialog ref="assetDependenceDialog"/>
</div>
</template>
......@@ -60,10 +62,11 @@
import {getCmpProps} from "../../../utils/common";
import StoreEditorDialog from "./PropsTab/StoreEditorDialog";
import ElFormItem from "../../../components/inputs/form-item";
import AssetDependenceDialog from "./PropsTab/AssetDependenceDialog";
export default {
name: 'PropsTab',
components: {ElFormItem, StoreEditorDialog, PropsEditor},
components: {AssetDependenceDialog, ElFormItem, StoreEditorDialog, PropsEditor},
data() {
const componentsMap = this.$t('view_node_menu');
const customCmdNames = this.$t('customCmds');
......@@ -124,6 +127,9 @@
clickEditStoreComputed() {
this.$refs.storeEditorDialog.edit('computed', this.activeComponent);
},
clickAssetManager(){
this.$refs.assetDependenceDialog.edit(this.activeComponent);
},
...mapMutations(['makeProjectDirty', 'addCmd', 'deleteCmd']),
}
};
......
<template>
<el-dialog
:title="$t('Asset dependence')"
width="80vw"
:visible.sync="visible"
:append-to-body="true"
:close-on-click-modal="false"
:show-close="false"
:close-on-press-escape="false"
custom-class="asset-dependence-dialog flex-dialog"
>
<div class="wrapper">
<div class="assets-dep">
<div class="explicit-tags">
<div>
<div class="explicit"></div>
显式依赖
</div>
<div>
<div class="implicit"></div>
隐式依赖
</div>
</div>
<el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view">
<asset-list ref="depAssetList" :editable="false" :selectable="false"
:assets-source="assetsSource"
v-if="view"
@click-item="onClickItem"
:target-view="view"
:explicit-dep-assets="explicitDepAssets"
:implicit-dep-assets="implicitDepAssets"
/>
</el-scrollbar>
</div>
<div class="custom-dep">
<span>自定义模块依赖</span>
<el-select class="select" multiple v-model="depCustoms" size="mini">
<el-option v-for="custom in customs"
:key="custom.id"
:value="custom.id"
:label="custom.name"
>
</el-option>
</el-select>
</div>
</div>
<div slot="footer" class="dialog-footer">
<div></div>
<div>
<el-button slot="reference" size="mini" @click="clickClose">{{ $t("Close") }}</el-button>
<el-button class="save-button" type="primary" size="mini" @click="clickSave">{{ $t("Save") }}
</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import {mapMutations, mapGetters} from 'vuex';
import MonacoEditor from "vue-monaco";
import {clonePureObj, monacoEditorOptions} from "../../../../utils";
import AssetList from "../../Assets/AssetList";
export default {
name: "AssetDependenceDialog",
components: {AssetList, MonacoEditor},
props: [],
data() {
return {
visible: false,
tab: 'exp',
monacoEditorOptions,
showEditor: false,
store: {},
assetsSource: [],
view: null,
explicitDepAssets: [],
implicitDepAssets: [],
depCustoms: [],
}
},
computed: {
...mapGetters(['customs']),
},
methods: {
async edit(view) {
this.view = view;
this.explicitDepAssets = view.explicitDepAssets.concat();
this.implicitDepAssets = view.implicitDepAssets.concat();
this.depCustoms = view.depCustoms ? view.depCustoms.concat() : [];
this.assetsSource = clonePureObj(this.$store.getters.assets);
this.visible = true;
},
clickSave() {
this.modifyViewDepAssets({
view: this.view,
explicitDepAssets: this.explicitDepAssets,
implicitDepAssets: this.implicitDepAssets,
depCustoms: this.depCustoms
});
this.visible = false;
},
clickClose() {
this.visible = false;
},
onClickItem(asset) {
const {uuid} = asset;
const {explicitDepAssets, implicitDepAssets} = this;
if (explicitDepAssets.includes(uuid)) {
return
}
let index = implicitDepAssets.indexOf(uuid);
if (index >= 0) {
implicitDepAssets.splice(index, 1);
} else {
implicitDepAssets.push(uuid);
}
},
...mapMutations(['modifyViewDepAssets'])
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
......@@ -4,19 +4,22 @@
<div class="right-part">
<span>
[{{project.name}}]
<el-button size="mini" circle icon="el-icon-edit" @click="editProjectName"/>
<el-button size="mini" class="micro" circle icon="el-icon-edit" @click="editProjectName"/>
<el-button size="mini" class="micro" circle icon="el-icon-notebook-2" @click="editRemark"/>
</span>
</div>
<project-remark-dialog ref="projectRemarkDialog"/>
</div>
</template>
<script>
import {mapState, mapActions, mapMutations} from 'vuex'
import SampleMenu from "../../components/SampleMenu";
import ProjectRemarkDialog from "./dialogs/ProjectRemarkDialog";
export default {
name: "ToolBar",
components: {SampleMenu},
components: {ProjectRemarkDialog, SampleMenu},
data() {
return {}
},
......@@ -40,7 +43,7 @@
return menu;
}
},
async mounted() {
//监听键盘事件
//document.addEventListener('keydown', this.onKeyPress);
......@@ -63,20 +66,23 @@
}).catch(() => {
});
},
editRemark() {
this.$refs.projectRemarkDialog.show();
},
onKeyPress(e) {
if (e.key === 'z' && (e.ctrlKey || e.metaKey) && (!e.shiftKey)) {
if (e.key === 'z' && (e.ctrlKey || e.metaKey) && (!e.shiftKey)) {
//快捷键ctrl+z执行撤销
e.preventDefault();
//是否可执行撤销
if(!(!this.project.operateStack.length || this.project.operateStack.length === this.project.stackIndex + 1)){
if (!(!this.project.operateStack.length || this.project.operateStack.length === this.project.stackIndex + 1)) {
this.clickMenu("undo");
}
return false;
}else if (e.key === 'z' && (e.ctrlKey || e.metaKey) && (e.shiftKey)) {
} else if (e.key === 'z' && (e.ctrlKey || e.metaKey) && (e.shiftKey)) {
//快捷键ctrl+shift+z执行重做
e.preventDefault();
//是否可以执行重做
if(!(this.project.stackIndex === 0)){
if (!(this.project.stackIndex === 0)) {
this.clickMenu("redo");
}
return false;
......
......@@ -4,7 +4,9 @@
: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">
<div></div>
<div>
</div>
<div>
<!--<el-button size="mini" @click="onSave(true)">{{$t('Save And Preview')}}</el-button>
<el-button size="mini" type="primary" @click="onSave(false)">{{$t('Save And Close')}}</el-button>-->
......@@ -20,6 +22,7 @@
import {mapState, mapMutations, mapActions} from 'vuex'
import BehaviorEditor from "../behavior-editor/BehaviorEditor";
import events from "@/global-events.js"
import {clonePureObj} from "../../../utils";
export default {
name: "BehaviorEditorDialog",
......@@ -49,7 +52,7 @@
this.visible = true;
},
async onSave() {
const behavior = await this.behavior_save();
this.behavior_save();
/*for (let i = 0, li = this.behaviors.length; i < li; i++) {
const b = this.behaviors[i];
if (b.uuid === behavior.uuid) {
......@@ -95,8 +98,6 @@
},
...mapMutations([
'behavior_cancel',
]),
...mapActions([
'behavior_save',
])
}
......
<template>
<el-dialog :title="$t('Project remark')" width="70%" :visible.sync="visible" @opened="onOpen"
:append-to-body="true"
:show-close="false"
fullscreen
custom-class="flex-dialog project-remark-dialog"
>
<div class="wrapper">
<monaco-editor
class="editor"
v-model="content"
language="markdown"
:options="monacoEditorOptions"
v-if="editMode"
/>
<el-scrollbar v-else class="project-scrollbar" wrap-class="wrap-x-hidden" view-class="project-remark">
<div v-html="html"></div>
</el-scrollbar>
</div>
<div slot="footer" class="dialog-footer">
<div>
<el-button v-if="!editMode" size="mini" @click="onEdit" type="primary">{{$t('Edit')}}</el-button>
<el-button v-if="editMode" size="mini" @click="onApply" type="success">{{$t('Apply')}}</el-button>
</div>
<div class="gap-5">
<el-button size="mini" @click="onClose">{{$t('Close')}}</el-button>
<el-badge is-dot :hidden="!showBadge">
<el-button size="mini" @click="onSave" type="primary">{{$t('Save')}}</el-button>
</el-badge>
</div>
</div>
</el-dialog>
</template>
<script>
import {mapActions, mapGetters} from 'vuex';
import MarkdownIt from 'markdown-it';
import MonacoEditor from "vue-monaco";
import {monacoEditorOptions, playWaiting} from "../../../utils";
let md = new MarkdownIt();
export default {
name: "ProjectRemarkDialog",
components: {MonacoEditor},
data() {
return {
visible: false,
editMode: false,
content: '',
monacoEditorOptions,
}
},
computed: {
showBadge(){
return this.projectRemark !== this.content;
},
html(){
return this.content ? md.render(this.content) : '';
},
...mapGetters(['projectRemark'])
},
methods: {
show() {
this.editMode = false;
this.content = this.projectRemark || '';
this.visible = true;
},
async onSave() {
await playWaiting(this.saveRemark({remark: this.content}), this.$t('Saving'));
this.visible = false;
},
async onClose() {
if(this.showBadge){
await this.$confirm(this.$t('Modified, save before exit'), this.$t('Alert'), {
confirmButtonText: this.$t('Save'),
cancelButtonText: this.$t('Still Close'),
distinguishCancelAndClose: true,
type: 'warning'
}).then(() => {
return this.onSave();
}).catch((action) => {
if(action === 'cancel'){
this.visible = false;
}
});
}else{
this.visible = false;
}
},
onOpen() {
},
onEdit() {
this.editMode = true;
},
onApply() {
this.editMode = false;
},
...mapActions([
'saveRemark',
]),
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
......@@ -217,7 +217,7 @@
const params = {
projectId: pxPid,
operate: operate + 1,
type:(operate + 1).toString(),
type:(tplType + 1).toString(),
id:skinId,
html:tpl,
name,
......
......@@ -2,6 +2,15 @@
<el-scrollbar v-if="editData" class="project-scrollbar" wrap-class="wrap-x-hidden" view-class="project-editor">
<el-form @submit.native.prevent ref="form" :model="editData" size="mini" label-position="right"
label-width="150px">
<el-form-item prop="behaviorEditor" :label="$t('Behavior Editor')">
<el-select v-model="editData.behaviorEditor">
<el-option v-for="(label, key) in behaviorEditors"
:key="key"
:value="key"
:label="label"
></el-option>
</el-select>
</el-form-item>
<el-form-item prop="pageTitle" :label="$t('Page title')">
<el-input v-model="editData.pageTitle"/>
</el-form-item>
......@@ -14,6 +23,22 @@
></el-option>
</el-select>
</el-form-item>
<el-form-item prop="loadingView" :label="$t('Loading view')">
<el-select v-model="editData.loadingView">
<el-option
:value="''"
:label="'空'"
></el-option>
<el-option v-for="(view, index) in project.data.views"
:key="index"
:value="view.name"
:label="view.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item prop="lazyLoadWait" :label="$t('Lazy Load Delay')">
<el-input-number v-model="editData.lazyLoadWait" placeholder="1000" controls-position="right"/>
</el-form-item>
<el-form-item prop="containerId" :label="$t('Container ID')">
<el-input v-model="editData.containerId"/>
</el-form-item>
......@@ -67,12 +92,14 @@
data() {
const scaleMode = this.$t('scaleMode');
const rendererType = this.$t('rendererType');
const behaviorEditors = this.$t('behaviorEditors');
return {
visible: false,
editData: null,
scaleMode,
rendererType,
monacoEditorOptions,
behaviorEditors,
}
},
computed: {
......
......@@ -86,6 +86,7 @@
import {PROJECT_PAGE_SIZE} from "../config";
import ProjectHistoryDialog from "./Home/ProjectHistoryDialog";
import ElFormItem from "../components/inputs/form-item";
import db from "../utils/db-storage";
export default {
name: "Home",
......@@ -118,6 +119,7 @@
methods: {
prepare() {
return Promise.all([
db.open('store'),
this.updateEnv(),
this.handleCurrentChange(1),
])
......@@ -188,8 +190,15 @@
onDuplicateProject(projectID) {
this.editProject(projectID);
},
onSelectProject(project, dataUrl) {
this.$router.push({name: 'editor', params: {projectID: project.id, project, dataUrl}});
async onSelectProject(project, dataUrl) {
if(await this.localVersionExist(project.id)){
this.$message({
message: this.$t('Unsaved version found locally, can not open from history'),
type: 'warning'
});
}else{
this.$router.push({name: 'editor', params: {projectID: project.id, project, dataUrl}});
}
},
editProject(projectID) {
this.$router.push({name: 'editor', params: {projectID}});
......@@ -214,6 +223,7 @@
'createProject',
'deleteProject',
'updateEnv',
'localVersionExist',
]),
},
}
......
......@@ -93,7 +93,7 @@
const {projectID} = this.$route.params;
const {data, tpl} = await db.get('preview', projectID);
console.log(data);
//console.log(JSON.stringify(data.processes));
//console.log(JSON.stringify(data));
let {options: {pageTitle, }, } = data;
document.title = pageTitle;
......
<template>
<div class="process-editor-dialog">
<div class="editor-wrapper">
<behavior-editor v-if="editorReady" ref="behaviorEditor" class="behavior-editor"></behavior-editor>
</div>
<div class="bottom-bar">
<div>
</div>
<div class="gap-5">
<el-button size="mini" type="danger" @click="onClose()">{{$t('Close')}}</el-button>
<el-badge :hidden="!showBadge" is-dot>
<el-button size="mini" type="primary" @click="onSave()">{{$t('Save')}}</el-button>
</el-badge>
</div>
</div>
</div>
</template>
<script>
import {mapMutations, mapState, mapGetters} from 'vuex'
import BehaviorEditor from "./Editor/behavior-editor/BehaviorEditor";
import {clonePureObj} from "../utils";
export default {
name: "ProcessEditor",
components: {BehaviorEditor},
data() {
return {
editorReady: false,
}
},
mounted() {
window.reset = ({projectData, packageState}) => {
console.log('reset');
this.resetProjectData(projectData);
this.resetPackageStore(packageState);
};
window.editable = () => {
return !this.showBadge;
};
window.edit = (path, index) => {
this.edit(path, index);
};
window.onbeforeunload = (e) => {
return this.showBadge ? true : null;
};
this.updateProcessTree();
},
computed: {
...mapState({
showBadge: state => state.behavior.dirty,
}),
...mapGetters(['processesInEditor'])
},
methods: {
edit(path, index) {
this.path = path;
this.index = index;
this.editorReady = true;
this.$nextTick(this.startEdit);
},
startEdit() {
this.$refs.behaviorEditor.edit(this.path, this.index);
},
async onSave() {
let opener;
try {
if (window.opener && window.opener.saveProcesses) {
opener = window.opener;
}
} catch (e) {
}
if (opener) {
let processes = clonePureObj(this.processesInEditor);
window.opener.saveProcesses(processes);
this.clearBehavior();
/*this.$message({
message: this.$t('Save behavior successfully'),
type: 'success'
});*/
} else {
this.$alert(this.$t('The main editor is closed and cannot be saved'));
}
},
onClose() {
if (this.showBadge) {
this.$confirm(this.$t('Save this behavior before'), this.$t('Alert'), {
closeOnClickModal: false,
closeOnPressEscape: false,
confirmButtonText: this.$t('Save And Close'),
cancelButtonText: this.$t('Still Close'),
distinguishCancelAndClose: true,
type: 'warning'
}).then(() => {
this.onSave();
this.close();
}).catch((action) => {
switch (action) {
case 'close':
break;
case 'cancel':
this.close();
break;
}
});
} else {
this.close();
}
},
close() {
window.close();
},
...mapMutations(['clearBehavior',
'resetProjectData', 'resetPackageStore', 'behavior_save', 'updateProcessTree'])
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<monaco-editor
class="script-editor"
ref="editor"
v-model="code"
language="json"
:options="monacoEditorOptions"
diff-editor
:original="originalCode"
/>
</template>
<script>
import MonacoEditor from "vue-monaco";
import {monacoEditorOptions} from "../utils";
//import old from './old.json'
//let originalCode = JSON.stringify(old, null, '\t');
let originalCode = `hello`;
console.log(originalCode);
export default {
name: "ProjectDiff",
components: {MonacoEditor},
data() {
return {
monacoEditorOptions,
code: '',
originalCode,
}
},
mounted() {
let editor = this.$refs.editor.getEditor();
editor.onDidUpdateDiff(()=>{
let m = editor.getLineChanges();
console.log(m);
})
}
}
</script>
<style scoped>
.script-editor{
width: 100%;
}
</style>
\ No newline at end of file
......@@ -12,7 +12,7 @@ module.exports = {
enableInSFC: true
}
},
devServer: {
/*devServer: {
host: '0.0.0.0',
port: '8080',
proxy: {
......@@ -26,11 +26,11 @@ module.exports = {
target: serverHost
}
}
},
},*/
configureWebpack: {
plugins: [
new MonacoWebpackPlugin({
languages: ['javascript', 'typescript', 'json', 'html'],
languages: ['javascript', 'typescript', 'json', 'html', 'markdown'],
publicPath: process.env.NODE_ENV === 'production' ? "/monaco" : "",
})
]
......
......@@ -3125,10 +3125,10 @@ electron-to-chromium@^1.3.322:
resolved "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.326.tgz?cache=0&sync_timestamp=1578285906184&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.326.tgz#71715aca9afd328ea208a3bc4651c15b869f0d1b"
integrity sha1-cXFaypr9Mo6iCKO8RlHBW4afDRs=
element-ui@^2.4.5:
version "2.13.0"
resolved "https://registry.npm.taobao.org/element-ui/download/element-ui-2.13.0.tgz#f6bb04e5b0a76ea5f62466044b774407ba4ebd2d"
integrity sha1-9rsE5bCnbqX2JGYES3dEB7pOvS0=
element-ui@^2.13.1:
version "2.13.1"
resolved "https://registry.npm.taobao.org/element-ui/download/element-ui-2.13.1.tgz?cache=0&sync_timestamp=1586760879922&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felement-ui%2Fdownload%2Felement-ui-2.13.1.tgz#0cb1a45cf27aa61c601defbe192740ac5cb9df7c"
integrity sha1-DLGkXPJ6phxgHe++GSdArFy533w=
dependencies:
async-validator "~1.8.1"
babel-helper-vue-jsx-merge-props "^2.0.0"
......@@ -3229,6 +3229,11 @@ entities@^2.0.0:
resolved "https://registry.npm.taobao.org/entities/download/entities-2.0.0.tgz?cache=0&sync_timestamp=1563403318326&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
integrity sha1-aNYITKsbB5dnVA2A5Wo5tCPkq/Q=
entities@~2.0.0:
version "2.0.2"
resolved "https://registry.npm.taobao.org/entities/download/entities-2.0.2.tgz?cache=0&sync_timestamp=1589062013210&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-2.0.2.tgz#ac74db0bba8d33808bbf36809c3a5c3683531436"
integrity sha1-rHTbC7qNM4CLvzaAnDpcNoNTFDY=
errno@^0.1.3, errno@~0.1.7:
version "0.1.7"
resolved "https://registry.npm.taobao.org/errno/download/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
......@@ -5048,6 +5053,13 @@ lines-and-columns@^1.1.6:
resolved "https://registry.npm.taobao.org/lines-and-columns/download/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
linkify-it@^2.0.0:
version "2.2.0"
resolved "https://registry.npm.taobao.org/linkify-it/download/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
integrity sha1-47VGl+eL+RXHCjis14/QngBYsc8=
dependencies:
uc.micro "^1.0.1"
load-json-file@^1.0.0:
version "1.1.0"
resolved "https://registry.npm.taobao.org/load-json-file/download/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
......@@ -5232,6 +5244,17 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
markdown-it@^10.0.0:
version "10.0.0"
resolved "https://registry.npm.taobao.org/markdown-it/download/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
integrity sha1-q/xk8UGxci1mNAIETkOSfx9QqNw=
dependencies:
argparse "^1.0.7"
entities "~2.0.0"
linkify-it "^2.0.0"
mdurl "^1.0.1"
uc.micro "^1.0.5"
md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.npm.taobao.org/md5.js/download/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
......@@ -5246,6 +5269,11 @@ mdn-data@2.0.4:
resolved "https://registry.npm.taobao.org/mdn-data/download/mdn-data-2.0.4.tgz?cache=0&sync_timestamp=1573816265745&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmdn-data%2Fdownload%2Fmdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
integrity sha1-aZs8OKxvHXKAkaZGULZdOIUC/Vs=
mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.npm.taobao.org/mdurl/download/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
......@@ -7064,7 +7092,7 @@ regenerate@^1.2.1, regenerate@^1.4.0:
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
resolved "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz?cache=0&sync_timestamp=1584052392667&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=
regenerator-runtime@^0.13.2:
......@@ -8369,6 +8397,11 @@ typedarray@^0.0.6:
resolved "https://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.npm.taobao.org/uc.micro/download/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha1-nEEagCpAmpH8bPdAgbq6NLJEmaw=
uglify-js@3.4.x:
version "3.4.10"
resolved "https://registry.npm.taobao.org/uglify-js/download/uglify-js-3.4.10.tgz?cache=0&sync_timestamp=1577407920190&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuglify-js%2Fdownload%2Fuglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
......
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