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) { ...@@ -147,3 +147,17 @@ export async function updateOperators(id, operators) {
errMessage: 'Failed to update operator', 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> <template>
<div :draggable="draggable" @dragstart="onDragstart" ref="playerContainer" :style="style"> <div :draggable="draggable" @dragstart="onDragstart" ref="playerContainer" :style="style"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave"
>
</div> </div>
</template> </template>
...@@ -24,6 +27,10 @@ ...@@ -24,6 +27,10 @@
type: Boolean, type: Boolean,
default: false, default: false,
}, },
autoplay: {
type: Boolean,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -75,10 +82,24 @@ ...@@ -75,10 +82,24 @@
this.videoSize.height = videoItem.videoSize.height; this.videoSize.height = videoItem.videoSize.height;
this.$nextTick(() => { this.$nextTick(() => {
player.setVideoItem(videoItem); 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([ ...mapActions([
'loadSvga' 'loadSvga'
]), ]),
......
...@@ -6,7 +6,7 @@ export let API_HOST; ...@@ -6,7 +6,7 @@ export let API_HOST;
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
//API_HOST = '//10.10.95.74:7777'; //API_HOST = '//10.10.95.74:7777';
//API_HOST = '//192.168.1.16: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 = '//192.168.0.105:7777';
//API_HOST = '//localhost:3002'; //API_HOST = '//localhost:3002';
//API_HOST = window.__data.apiHost; //API_HOST = window.__data.apiHost;
......
...@@ -74,6 +74,7 @@ ...@@ -74,6 +74,7 @@
"No projects": "无项目", "No projects": "无项目",
"Create": "创建", "Create": "创建",
"Project details": "项目详情", "Project details": "项目详情",
"Project remark": "项目备注",
"Project does not exist": "项目不存在", "Project does not exist": "项目不存在",
"Project name": "项目名", "Project name": "项目名",
"Data mapping": "数据映射", "Data mapping": "数据映射",
...@@ -101,8 +102,10 @@ ...@@ -101,8 +102,10 @@
"Create project": "创建项目", "Create project": "创建项目",
"Rename project": "重命名项目", "Rename project": "重命名项目",
"Entry scene view": "入口场景视图", "Entry scene view": "入口场景视图",
"Loading view": "加载视图",
"Page title": "页面标题", "Page title": "页面标题",
"Container ID": "容器ID", "Container ID": "容器ID",
"Lazy Load Delay": "懒加载延时",
"Design width": "设计宽度", "Design width": "设计宽度",
"Design height": "设计高度", "Design height": "设计高度",
"Frame Rate": "帧率", "Frame Rate": "帧率",
...@@ -163,10 +166,12 @@ ...@@ -163,10 +166,12 @@
"Are you sure to discard the local modification": "确定丢弃本地修改吗?", "Are you sure to discard the local modification": "确定丢弃本地修改吗?",
"Input view name": "输入视图名", "Input view name": "输入视图名",
"Invalid 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": "本地版本", "Local Version": "本地版本",
"Remote Version": "远程版本", "Remote Version": "远程版本",
"Confirm to exit the editor": "确定退出编辑器吗?", "Confirm to exit the editor": "确定退出编辑器吗?",
"Modified, save before exit": "内容已修改,要保存后关闭吗?",
"Confirm to publish": "确定发布吗?", "Confirm to publish": "确定发布吗?",
"Are you sure to delete this behavior?": "确定删除这个的行为吗", "Are you sure to delete this behavior?": "确定删除这个的行为吗",
"Are you sure to delete this process?": "确定删除这个的过程吗", "Are you sure to delete this process?": "确定删除这个的过程吗",
...@@ -226,6 +231,7 @@ ...@@ -226,6 +231,7 @@
"Function cmd": "功能指令", "Function cmd": "功能指令",
"Edit store": "编辑数据", "Edit store": "编辑数据",
"Edit computed": "计算属性", "Edit computed": "计算属性",
"Asset dependence": "素材依赖",
"Are you sure to delete this cmd?": "确定删除这个指令吗?", "Are you sure to delete this cmd?": "确定删除这个指令吗?",
"Store editor": "数据编辑器", "Store editor": "数据编辑器",
"Store": "数据", "Store": "数据",
...@@ -247,6 +253,10 @@ ...@@ -247,6 +253,10 @@
"Previous Conflict": "上一个冲突", "Previous Conflict": "上一个冲突",
"Next Conflict": "下一个冲突", "Next Conflict": "下一个冲突",
"To Verify": "去验证", "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": { "eventGroup": {
"in": "接收", "in": "接收",
"out": "派发" "out": "派发"
...@@ -355,6 +365,10 @@ ...@@ -355,6 +365,10 @@
"webgl": "WEBGL", "webgl": "WEBGL",
"canvas": "CANVAS" "canvas": "CANVAS"
}, },
"behaviorEditors": {
"builtin": "内建",
"standalone": "独立"
},
"pxEnvs": { "pxEnvs": {
"dev": "开发环境", "dev": "开发环境",
"test": "测试环境", "test": "测试环境",
...@@ -391,7 +405,8 @@ ...@@ -391,7 +405,8 @@
"assetItemMenu": { "assetItemMenu": {
"rename": "重命名", "rename": "重命名",
"replace": "替换", "replace": "替换",
"combine": "合并" "combine": "合并",
"to-jpg": "转换为JPG"
}, },
"customCmds": { "customCmds": {
"z-for": "循环", "z-for": "循环",
......
...@@ -24,9 +24,9 @@ export default new Router({ ...@@ -24,9 +24,9 @@ export default new Router({
component: () => import('./views/Preview.vue') component: () => import('./views/Preview.vue')
}, },
{ {
path: '/diff', path: '/process-editor',
name: 'diff', name: 'process-editor',
component: () => import('./views/ProjectDiff.vue') component: () => import('./views/ProcessEditor.vue')
}, },
] ]
}) })
...@@ -7,8 +7,7 @@ ...@@ -7,8 +7,7 @@
import Vue from "vue"; import Vue from "vue";
import i18n from "../../i18n"; import i18n from "../../i18n";
import generateUUID from "uuid/v4"; import generateUUID from "uuid/v4";
import {clonePureObj, getViewNodePath, findMetaDepById, searchViewNode, updateProcesses} from "../../utils"; import {clonePureObj, findMetaDepById, searchViewNode, updateProcesses} from "../../utils";
import {arrayFind} from "element-ui/src/utils/util";
export const behaviorStore = { export const behaviorStore = {
state: { state: {
...@@ -35,13 +34,16 @@ export const behaviorStore = { ...@@ -35,13 +34,16 @@ export const behaviorStore = {
makeBehaviorDirty(state) { makeBehaviorDirty(state) {
state.dirty = true; state.dirty = true;
}, },
behavior_startEdit(state, {originData, behavior}) { clearBehavior(state) {
state.dirty = false; state.dirty = false;
},
behavior_startEdit(state, {originData, behavior}) {
state.projectData = originData; state.projectData = originData;
state.originProcesses = originData.processes; state.originProcesses = originData.processes;
state.processes = clonePureObj(originData.processes); state.processes = clonePureObj(originData.processes);
state.currentBehavior = clonePureObj(behavior); state.currentBehavior = clonePureObj(behavior);
this.commit('clearBehavior');
this.commit('updateCustomProcesses', state.processes); this.commit('updateCustomProcesses', state.processes);
}, },
behavior_save(state) { behavior_save(state) {
...@@ -117,7 +119,9 @@ export const behaviorStore = { ...@@ -117,7 +119,9 @@ export const behaviorStore = {
newMeta.id = newUUID; newMeta.id = newUUID;
newMeta.isInline = true; newMeta.isInline = true;
Vue.set(parentMeta, 'metas', []); if(!parentMeta.metas){
Vue.set(parentMeta, 'metas', []);
}
parentMeta.metas.push(newMeta); parentMeta.metas.push(newMeta);
process.metaID = newUUID; process.metaID = newUUID;
} }
...@@ -162,13 +166,12 @@ export const behaviorStore = { ...@@ -162,13 +166,12 @@ export const behaviorStore = {
if (uuid) { if (uuid) {
return searchViewNode(state.projectData.views, uuid); return searchViewNode(state.projectData.views, uuid);
} }
} },
processesInEditor(state) {
return state.processes;
},
}, },
actions: { actions: {
behavior_save({commit, state}) {
commit('behavior_save');
return state.currentBehavior;
},
addCustomProcessMeta({commit, state}, {masterProcess, isInline, processId, name}) { addCustomProcessMeta({commit, state}, {masterProcess, isInline, processId, name}) {
let meta = { let meta = {
id: generateUUID(), id: generateUUID(),
......
...@@ -5,8 +5,10 @@ ...@@ -5,8 +5,10 @@
*/ */
import Vue from "vue"; import Vue from "vue";
import {envApi} from "../../api"; import {editorApi, envApi} from "../../api";
import {Parser} from "svgaplayerweb"; import {Parser} from "svgaplayerweb";
import db from "../../utils/db-storage";
import events from "@/global-events"
const storeKey = 'code-sync-serve-config'; const storeKey = 'code-sync-serve-config';
const svgaParser = new Parser(); const svgaParser = new Parser();
...@@ -15,7 +17,7 @@ export const editorStore = { ...@@ -15,7 +17,7 @@ export const editorStore = {
state: { state: {
initialized: false, initialized: false,
name: 'Zeroing Editor', name: 'Zeroing Editor',
version: '0.1.0', version: '0.2.1',
templates: { templates: {
builtin: ['blank'], builtin: ['blank'],
custom: [], custom: [],
...@@ -28,6 +30,7 @@ export const editorStore = { ...@@ -28,6 +30,7 @@ export const editorStore = {
svgaCache: {}, svgaCache: {},
svgaLoadingQueue: {}, svgaLoadingQueue: {},
assetSizeCache: {}, assetSizeCache: {},
processEditorWin: null,
}, },
mutations: { mutations: {
updateEnv(state, env) { updateEnv(state, env) {
...@@ -44,15 +47,22 @@ export const editorStore = { ...@@ -44,15 +47,22 @@ export const editorStore = {
state.codeSyncServeConfig = config; state.codeSyncServeConfig = config;
localStorage.setItem(storeKey, JSON.stringify(state.codeSyncServeConfig)); localStorage.setItem(storeKey, JSON.stringify(state.codeSyncServeConfig));
}, },
updateProcessEditorWin(state, win) {
state.processEditorWin = win;
}
}, },
getters: { getters: {
assetSize: state => url => { assetSize: state => url => {
let size = state.assetSizeCache[url]; let size = state.assetSizeCache[url];
if(size){ if (size) {
return `${size.width}x${size.height}` return `${size.width}x${size.height}`
} }
return '0x0'; return '0x0';
} },
behaviorEditable: state => () => {
let win = state.processEditorWin;
return !win || win.closed || !win.editable;
},
}, },
actions: { actions: {
async updateEnv({state, commit}) { async updateEnv({state, commit}) {
...@@ -125,3 +135,24 @@ export const editorStore = { ...@@ -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 @@ ...@@ -4,10 +4,11 @@
import db from "../../utils/db-storage"; import db from "../../utils/db-storage";
import {packageApi} from "../../api"; 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 i18n from "../../i18n";
import events from "@/global-events.js" import events from "@/global-events.js"
import {timeFormat, typeMapping} from "../../utils"; import {clonePureObj, timeFormat, typeMapping} from "../../utils";
const storeName = 'packages'; const storeName = 'packages';
...@@ -30,15 +31,39 @@ export const packageStore = { ...@@ -30,15 +31,39 @@ export const packageStore = {
customProcesses: [], customProcesses: [],
}, },
mutations: { 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) { updatePackageInfos(state, packageInfos) {
for (let i = 0, li = typeMapping.length; i < li; i++) { for (let i = 0, li = typeMapping.length; i < li; i++) {
state.packageInfos[typeMapping[i]].splice(0); state.packageInfos[typeMapping[i]].splice(0);
} }
for (let packageInfo of packageInfos) { for (let packageInfo of packageInfos) {
packageInfo.versions = packageInfo.versions.split(',').reverse(); let {versions, remarks, update_times, update_time} = packageInfo;
packageInfo.remarks = packageInfo.remarks.split(',').reverse();
packageInfo.update_times = packageInfo.update_times.split(',').map(time => timeFormat(time)).reverse(); versions = versions.split(',');
packageInfo.update_time = timeFormat(packageInfo.update_time); 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); state.packageInfos[typeMapping[packageInfo.type]].push(packageInfo);
} }
}, },
...@@ -185,7 +210,7 @@ export const packageStore = { ...@@ -185,7 +210,7 @@ export const packageStore = {
for (let packageInfo of state.packageInfos[group]) { for (let packageInfo of state.packageInfos[group]) {
const {package_id, versions} = packageInfo; const {package_id, versions} = packageInfo;
if (!dependencies[package_id]) { if (!dependencies[package_id]) {
dependencies[package_id] = maxSatisfying(versions, '*'); dependencies[package_id] = semverMaxSatisfying(versions, '*');
} }
} }
} }
......
...@@ -17,23 +17,25 @@ import { ...@@ -17,23 +17,25 @@ import {
traverseViewNode traverseViewNode
} from "../../utils" } from "../../utils"
import {template} from "../../template" import {template} from "../../template"
import events from "@/global-events" import {packAssetsGroups} from "../../utils/assets-pack"
import {packImages} from "../../utils/sheet-pack"
import {addBehavior, deleteProcessMeta,} from "./behavior" import {addBehavior, deleteProcessMeta,} from "./behavior"
import db from "../../utils/db-storage" import db from "../../utils/db-storage"
import {preprocess} from "../../views/Preview/preview-preprocess" import {preprocess} from "../../views/Preview/preview-preprocess"
import {packageStore} from "./package" import {packageStore} from "./package"
import {fetchApi} from "../../api/common"
import {toZeroing} from "psd-parse-web" import {toZeroing} from "psd-parse-web"
import {arrayFind} from "element-ui/src/utils/util"; import {arrayFind} from "element-ui/src/utils/util";
import {uploadFiles} from "./editor";
const storeName = 'project'; const storeName = 'project';
const psStoreName = 'pack-history'; const psStoreName = 'pack-history';
const defaultOptions = { const defaultOptions = {
behaviorEditor: 'builtin',
pageTitle: 'no title', pageTitle: 'no title',
entrySceneView: '', entrySceneView: '',
loadingView: '',
lazyLoadWait: 1000,
containerId: 'game-container', containerId: 'game-container',
designWidth: 750, designWidth: 750,
designHeight: 1334, designHeight: 1334,
...@@ -65,6 +67,7 @@ export const projectStore = { ...@@ -65,6 +67,7 @@ export const projectStore = {
operator: '', operator: '',
operators: '', operators: '',
update_time: '', update_time: '',
remark: '',
data: { data: {
options: {}, options: {},
views: [], views: [],
...@@ -79,11 +82,11 @@ export const projectStore = { ...@@ -79,11 +82,11 @@ export const projectStore = {
activeComponent: {}, activeComponent: {},
activeComponentCopy: {}, // 当前选中节点的镜像,用来处理拖拽时数据变化频繁的问题 activeComponentCopy: {}, // 当前选中节点的镜像,用来处理拖拽时数据变化频繁的问题
activeIdList: [], activeIdList: [],
activeViews: '',
dragUUID: '', dragUUID: '',
dirty: false, dirty: false,
operateStack: [], operateStack: [],
stackIndex: 0, stackIndex: 0,
activeView: null,
}, },
mutations: { mutations: {
setDirty(state, dirty = true) { setDirty(state, dirty = true) {
...@@ -92,26 +95,30 @@ export const projectStore = { ...@@ -92,26 +95,30 @@ export const projectStore = {
makeProjectDirty() { makeProjectDirty() {
}, },
updateProjectUpdateTime(state, updateTime) { resetProjectData(state, data) {
state.update_time = updateTime; state.data = data;
},
updateProjectUpdateTime(state, {time, dirty = true}) {
state.update_time = time;
if (dirty) {
this.commit('makeProjectDirty');
}
}, },
updateProject(state, project) { 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.id = id;
state.name = name; state.name = name;
state.creator = creator; state.creator = creator;
state.operator = operator; state.operator = operator;
state.operators = operators || creator; //兼容未保存的本地存储 state.operators = operators || creator; //兼容未保存的本地存储
state.update_time = update_time; state.update_time = update_time;
state.remark = remark;
const localData = state.data; const localData = state.data;
if (data) { if (data) {
const {views, assets, dataMapping, processes, options, customs, mock, dependencies} = typeof data === 'string' ? JSON.parse(data) : 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, 'options', options || getDefaultOptions());
Vue.set(localData, 'views', views || []); Vue.set(localData, 'views', views || []);
Vue.set(localData, 'assets', assets || []); Vue.set(localData, 'assets', assets || []);
...@@ -131,11 +138,56 @@ export const projectStore = { ...@@ -131,11 +138,56 @@ export const projectStore = {
Vue.set(localData, 'dependencies', {}); 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.dispatch('fillLastVersion');
this.commit('updateAssetDep');
state.mockServeEnabled = getMockServeEnabled(id); state.mockServeEnabled = getMockServeEnabled(id);
updateMock(localData.mock); 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) { modifyOptions(state, value) {
state.data.options = value; state.data.options = value;
}, },
...@@ -172,6 +224,14 @@ export const projectStore = { ...@@ -172,6 +224,14 @@ export const projectStore = {
state.activeComponentCopy = _.cloneDeep(state.activeComponent); state.activeComponentCopy = _.cloneDeep(state.activeComponent);
} }
}, },
/**
* 设置当前视图
* @param state
* @param view
*/
setActiveView(state, view) {
state.activeView = view;
},
/** /**
* 修改当前选中的节点 * 修改当前选中的节点
* @param {*} state * @param {*} state
...@@ -360,7 +420,7 @@ export const projectStore = { ...@@ -360,7 +420,7 @@ export const projectStore = {
let _node1 = node; let _node1 = node;
console.log(_node1) console.log(_node1)
copyNodeCatch = clonePureObj(_node1) copyNodeCatch = clonePureObj(_node1)
console.log("copyNodeCatch",copyNodeCatch) console.log("copyNodeCatch", copyNodeCatch)
if (copyState == 1) { if (copyState == 1) {
//复制行为 //复制行为
copyNodeCatch.events copyNodeCatch.events
...@@ -369,19 +429,20 @@ export const projectStore = { ...@@ -369,19 +429,20 @@ export const projectStore = {
console.log(this) console.log(this)
deleteChildEvent(copyNodeCatch) deleteChildEvent(copyNodeCatch)
} }
//copy(JSON.stringify(copyNodeCatch)) //copy(JSON.stringify(copyNodeCatch))
function deleteChildEvent(copyNodeCatch){ function deleteChildEvent(copyNodeCatch) {
console.log(123) console.log(123)
delete copyNodeCatch.events; delete copyNodeCatch.events;
if(copyNodeCatch.children){ if (copyNodeCatch.children) {
for(let item of copyNodeCatch.children){ for (let item of copyNodeCatch.children) {
deleteChildEvent(item) deleteChildEvent(item)
} }
} }
} }
}, },
pasteNode(state, {node, parentNode, pasteState}) { pasteNode(state, {node, parentNode, pasteState}) {
let _node1 = copyNodeCatch; let _node1 = copyNodeCatch;
console.log(copyNodeCatch); console.log(copyNodeCatch);
...@@ -438,9 +499,11 @@ export const projectStore = { ...@@ -438,9 +499,11 @@ export const projectStore = {
}) })
}, },
replaceAsset(state, {uuid, url}) { replaceAsset(state, {uuid, url}) {
const ext = path.extname(url);
for (let asset of state.data.assets) { for (let asset of state.data.assets) {
if (asset.uuid === uuid) { if (asset.uuid === uuid) {
asset.url = url; asset.url = url;
asset.ext = ext;
} }
} }
}, },
...@@ -486,6 +549,20 @@ export const projectStore = { ...@@ -486,6 +549,20 @@ export const projectStore = {
Vue.delete(state.activeComponent.properties, cmd); Vue.delete(state.activeComponent.properties, cmd);
this.commit('makeProjectDirty'); 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: { getters: {
project(state) { project(state) {
...@@ -499,6 +576,12 @@ export const projectStore = { ...@@ -499,6 +576,12 @@ export const projectStore = {
data: JSON.stringify(newData), data: JSON.stringify(newData),
}; };
}, },
projectRemark(state) {
return state.remark;
},
behaviorEditor(state) {
return state.data.options.behaviorEditor;
},
menuBadge: (state) => (key) => { menuBadge: (state) => (key) => {
let result = false; let result = false;
switch (key) { switch (key) {
...@@ -539,8 +622,8 @@ export const projectStore = { ...@@ -539,8 +622,8 @@ export const projectStore = {
// 如果有选中的节点,则展示对应的视图组 // 如果有选中的节点,则展示对应的视图组
// 否则展示第一个视图组 // 否则展示第一个视图组
let _view = state.data.views.length ? [].concat(state.data.views[0]) : []; let _view = state.data.views.length ? [].concat(state.data.views[0]) : [];
if (state.activeViews) { if (state.activeView) {
_view = state.data.views.filter(v => v.uuid === state.activeViews) _view = state.data.views.filter(v => v.uuid === state.activeView.data.uuid)
} }
let result = flattenViews(_.cloneDeep(_view)); let result = flattenViews(_.cloneDeep(_view));
...@@ -554,8 +637,8 @@ export const projectStore = { ...@@ -554,8 +637,8 @@ export const projectStore = {
// 如果有选中的节点,则展示对应的视图组 // 如果有选中的节点,则展示对应的视图组
// 否则展示第一个视图组 // 否则展示第一个视图组
let _view = []; let _view = [];
if (state.activeViews) { if (state.activeView) {
_view = state.data.views.filter(v => v.uuid === state.activeViews) _view = state.data.views.filter(v => v.uuid === state.activeView.data.uuid)
} else { } else {
_view = state.data.views.length ? [].concat(state.data.views[0]) : []; _view = state.data.views.length ? [].concat(state.data.views[0]) : [];
} }
...@@ -565,6 +648,14 @@ export const projectStore = { ...@@ -565,6 +648,14 @@ export const projectStore = {
assets(state) { assets(state) {
return state.data.assets; 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 => { getProcess: state => behavior => {
return arrayFind(state.data.processes, process => process.id === behavior.meta); return arrayFind(state.data.processes, process => process.id === behavior.meta);
}, },
...@@ -630,7 +721,10 @@ export const projectStore = { ...@@ -630,7 +721,10 @@ export const projectStore = {
let json = await db.get(storeName, projectID); let json = await db.get(storeName, projectID);
//let json = localStorage.getItem('project-' + projectID); //let json = localStorage.getItem('project-' + projectID);
if (json) { if (json) {
const remoteProject = await projectApi.fetchOne(projectID, false);
const project = JSON.parse(json.data); const project = JSON.parse(json.data);
project.operators = remoteProject.operators;
project.remark = remoteProject.remark;
commit('updateProject', project); commit('updateProject', project);
commit('setDirty', true); commit('setDirty', true);
} }
...@@ -666,17 +760,22 @@ export const projectStore = { ...@@ -666,17 +760,22 @@ export const projectStore = {
commit('updateProject', project); commit('updateProject', project);
}, },
async saveToRemote({state, dispatch, getters, commit}, {remark, data}) { async saveToRemote({state, dispatch, getters, commit}, {remark, data}) {
if (data) { //如果已经合并了冲突
commit('updateProjectUpdateTime', {time: state.base_time});
}
let project = Object.assign({}, getters.project); let project = Object.assign({}, getters.project);
if (data) { if (data) {
project.data = data; project.data = data;
project.force = true;
} }
let resp = await projectApi.saveOne(project, remark); let resp = await projectApi.saveOne(project, remark);
if (resp.result) { if (resp.result) {
commit('updateProjectUpdateTime', resp.project.update_time); commit('updateProjectUpdateTime', {time: resp.project.update_time, dirty: false});
dispatch('deleteLocalVersion', state.id); dispatch('deleteLocalVersion', state.id);
} }
resp.localData = project.data; resp.localData = data || project.data;
if (!resp.result) {
state.base_time = resp.updateTime;
}
return resp; return resp;
}, },
async updateProject({commit}, projectID) { async updateProject({commit}, projectID) {
...@@ -689,7 +788,7 @@ export const projectStore = { ...@@ -689,7 +788,7 @@ export const projectStore = {
* @param {*} context * @param {*} context
* @param {*} data * @param {*} data
*/ */
activeComponent(context, data) { activeComponent({state, commit}, data) {
// debugger; // debugger;
let getTopView = node => { let getTopView = node => {
if (node.parent && !node.parent.parent) { if (node.parent && !node.parent.parent) {
...@@ -698,26 +797,27 @@ export const projectStore = { ...@@ -698,26 +797,27 @@ export const projectStore = {
return getTopView(node.parent); return getTopView(node.parent);
} }
}; };
let _view = getTopView(data.node); let viewRoot = getTopView(data.node);
if (!data.fromPlayground) {
// 点击视图区域选中节点
// 则需要切换当前节点所在的最顶层视图
if (_view && _view.data) {
context.state.activeViews = _view.data.uuid;
}
}
if (_view.data === data.data) { if (state.activeView !== viewRoot) {
if (!data.data.hasOwnProperty('$isRootView')) { commit('setActiveView', viewRoot);
Object.defineProperty(data.data, '$isRootView', {
/*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() { get() {
return true; return true;
} },
enumerable: false,
}) })
} }
} }
context.commit('activeComponent', data.data); commit('activeComponent', data.data);
}, },
/** /**
* 修改属性 * 修改属性
...@@ -726,10 +826,20 @@ export const projectStore = { ...@@ -726,10 +826,20 @@ export const projectStore = {
// debugger; // debugger;
// 如果当前修改的是“来源”属性,节点又没有高度宽度,则取图片的高度宽度 // 如果当前修改的是“来源”属性,节点又没有高度宽度,则取图片的高度宽度
let _props = _.cloneDeep(state.activeComponent.properties); let _props = _.cloneDeep(state.activeComponent.properties);
let hasAssetsDep = false;
_.forIn(props, (value, key) => { _.forIn(props, (value, key) => {
/*if (value && value.startsWith(assetScheme) ||
_props[key] && _props[key].startsWith(assetScheme)) {
hasAssetsDep = true;
}*/
_props[key] = value; _props[key] = value;
}); });
/*if (hasAssetsDep) {
commit('updateAssetDep');
}*/
commit('updateAssetDep');
if (_props.source) { if (_props.source) {
// imageWidth imageHeight // imageWidth imageHeight
// 设置image类型节点的原图高宽度 // 设置image类型节点的原图高宽度
...@@ -836,6 +946,7 @@ export const projectStore = { ...@@ -836,6 +946,7 @@ export const projectStore = {
return failedList; return failedList;
}, },
async replaceAsset({commit}, {uuid, file}) { async replaceAsset({commit}, {uuid, file}) {
console.log(file);
const {failedList, result} = await uploadFiles([file]); const {failedList, result} = await uploadFiles([file]);
commit('replaceAsset', {uuid, url: result[0].url,}); commit('replaceAsset', {uuid, url: result[0].url,});
return failedList; return failedList;
...@@ -844,8 +955,9 @@ export const projectStore = { ...@@ -844,8 +955,9 @@ export const projectStore = {
let debug = params.debug; let debug = params.debug;
let packedAssets; let packedAssets;
//if (!debug) { //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); const packResult = await projectApi.pack(state.id, debug, packedAssets);
console.log(packResult); console.log(packResult);
...@@ -895,111 +1007,14 @@ export const projectStore = { ...@@ -895,111 +1007,14 @@ export const projectStore = {
localStorage.setItem('mock-enabled-' + state.id, JSON.stringify(enabled)); localStorage.setItem('mock-enabled-' + state.id, JSON.stringify(enabled));
commit('setMockServeEnabled', enabled); commit('setMockServeEnabled', enabled);
return 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() { function getDefaultOptions() {
return clonePureObj(defaultOptions) return clonePureObj(defaultOptions)
...@@ -1041,3 +1056,10 @@ function copyBaseRoot(node) { ...@@ -1041,3 +1056,10 @@ function copyBaseRoot(node) {
} }
return data 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 = ...@@ -9,8 +9,7 @@ export const template =
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>$PAGE_TITLE$</title> <title>$PAGE_TITLE$</title>
<meta name="viewport" <meta name="viewport" content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
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="apple-mobile-web-app-capable" content="yes"/>
<meta name="full-screen" content="true"/> <meta name="full-screen" content="true"/>
<meta name="screen-orientation" content="portrait"/> <meta name="screen-orientation" content="portrait"/>
...@@ -29,11 +28,17 @@ content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, u ...@@ -29,11 +28,17 @@ content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, u
position: absolute; position: absolute;
background-color: white; background-color: white;
} }
.game-container{
width: 100%;
height: 100%;
line-height:0;
font-size:0;
}
</style> </style>
</head> </head>
<body> <body>
<div id="$CONTAINER_ID$" style="line-height:0;font-size:0"></div> <div id="$CONTAINER_ID$" class="game-container"></div>
$LIBRARIES.engine$ $LIBRARIES.engine$
$SCRIPTS$ $SCRIPTS$
<script> <script>
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
font-size: 12px; font-size: 12px;
line-height: 14px; line-height: 14px;
color: $--color-text-primary; color: $--color-text-primary;
border-right: 1px solid $--border-color-base; border-left: 1px solid $--border-color-base;
.item{ .item{
border-bottom: 1px solid $--border-color-base; border-bottom: 1px solid $--border-color-base;
...@@ -67,11 +67,11 @@ ...@@ -67,11 +67,11 @@
.file-list { .file-list {
display: flex; display: flex;
flex: 1;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;
.file-item { .file-item {
position: relative;
width: 60px; width: 60px;
padding: 5px; padding: 5px;
color: $--color-text-regular; color: $--color-text-regular;
...@@ -147,6 +147,25 @@ ...@@ -147,6 +147,25 @@
line-height: 17px; 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 { .file-uploader {
...@@ -197,6 +216,7 @@ ...@@ -197,6 +216,7 @@
} }
.item{ .item{
padding: 3px 0;
.field{ .field{
width: 30px; width: 30px;
display: block; display: block;
......
...@@ -13,6 +13,12 @@ html, body, #app { ...@@ -13,6 +13,12 @@ html, body, #app {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
} }
.gap-5{
*:not(:first-child){
margin-left: 5px;
}
}
.modal-layer{ .modal-layer{
position: absolute; position: absolute;
top: 0; top: 0;
......
...@@ -4,322 +4,344 @@ $dock-pin-width: 9px; ...@@ -4,322 +4,344 @@ $dock-pin-width: 9px;
.behavior-editor-dialog { .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; 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: 1;
flex-direction: row;
display: flex;
padding-right: 10px;
.process-tree-node { .node-name {
flex: 1; flex: 1;
flex-direction: row; width: 0;
display: flex; text-overflow: ellipsis;
padding-right: 10px; overflow: hidden;
font-size: 14px;
.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;
}
}
.edit-button { .current-node {
visibility: hidden; background-color: $--color-text-secondary;
color: $--color-white;
border-radius: 5px;
padding: 0 2px;
} }
} }
.edit-button {
visibility: hidden;
}
} }
} }
}
.behavior { .behavior {
border: 1px solid $--border-color-base; //border: 1px solid $--border-color-base;
.background { .background {
background-color: $--background-color-base; 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; display: flex;
flex-direction: column; justify-content: space-between;
align-items: flex-start;
.edit-path {
flex: 1;
padding: 5px;
height: 14px;
.el-button { border-bottom: 1px solid $--border-color-light;
padding: 3px;
margin-top: 2px;
margin-right: 2px;
} }
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: flex-start;
.edit-path { .operate-bar {
flex: 1; padding: 3px;
padding: 5px;
height: 14px;
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 { /*&:hover {
padding: 3px; border-color: $block-border-hover-background-color;
border-bottom: 1px solid $--border-color-light; & > .header {
} background-color: $block-border-hover-background-color;
} }
}*/
.board { /*&:focus {
flex: 1; border-color: $block-border-focus-background-color;
.svg-board { & > .header {
.line { background-color: $block-border-focus-background-color;
stroke: #979797; }
}*/
.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 { .el-link {
stroke: $--color-primary; padding: 3px 2px 2px 2px;
stroke-dasharray: 5, 1;
} }
&.contact { .el-link + .el-link {
stroke: $--color-primary; 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; 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 { i {
& > .top-bar { display: block;
visibility: visible; //color: $--color-warning;
} margin-right: 3px;
} }
/*&:hover { .title {
border-color: $block-border-hover-background-color; flex: 1;
}
}
& > .header { .body {
background-color: $block-border-hover-background-color; display: flex;
} flex-direction: column;
}*/ padding: 3px;
font-size: 12px;
color: $--color-text-regular;
/*&:focus { .field-item {
border-color: $block-border-focus-background-color; display: flex;
align-items: center;
& > .header { & > * {
background-color: $block-border-focus-background-color; flex: 1;
}
}*/
.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;
}
.el-link + .el-link { white-space: nowrap;
margin-left: 5px; overflow: hidden;
} }
}
.header { .key {
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;
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; display: flex;
align-items: center; align-items: center;
justify-content: flex-end;
}
& > * { .string-value {
flex: 1; text-align: right;
white-space: nowrap; .tag {
overflow: hidden; color: $--color-success;
} }
}
.key { .color-value {
width: 15px;
height: 15px;
border-radius: 3px;
float: right;
border: 1px solid $--border-color-base;
}
}
} }
.linked-value { .dock {
display: flex; position: absolute;
align-items: center; top: 50%;
justify-content: flex-end; transform: translateY(-50%);
} display: flex;
flex-direction: column;
.string-value { .pin {
text-align: right; border: 1px solid $block-border-blur-background-color;
padding: 1px;
margin-bottom: 5px;
background-color: white;
.tag { div {
color: $--color-success; width: 3px;
} height: 3px;
} border: 1px solid $block-border-blur-background-color;
border-radius: 3px;
.color-value {
width: 15px;
height: 15px;
border-radius: 3px;
float: right;
border: 1px solid $--border-color-base;
}
} }
} &:hover {
border-color: $--color-primary;
.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 { & > div {
border-color: $--color-primary; border-color: $--color-primary;
& > div {
border-color: $--color-primary;
}
} }
}
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
}
} }
} }
}
.input { .input {
@extend .dock; @extend .dock;
left: -$dock-pin-width; left: -$dock-pin-width;
} }
.output { .output {
@extend .dock; @extend .dock;
right: -$dock-pin-width; right: -$dock-pin-width;
}
} }
}
.active { .active {
border-color: $block-border-focus-background-color; border-color: $block-border-focus-background-color;
& > .header { & > .header {
background-color: $block-border-focus-background-color; background-color: $block-border-focus-background-color;
}
/*& > .top-bar {
visibility: visible;
}*/
} }
/*& > .top-bar {
visibility: visible;
}*/
} }
} }
}
.properties { .properties {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.wrapper { .wrapper {
padding: 5px; padding: 5px;
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.name-input { .name-input {
} }
.scrollbar { .scrollbar {
margin-right: -5px; margin-right: -5px;
padding-top: 5px; padding-top: 5px;
flex: 1; flex: 1;
.scrollbar-view { .scrollbar-view {
padding-right: 10px; padding-right: 10px;
}
} }
}
.inside-code { .inside-code {
flex: 1; flex: 1;
height: 0; height: 0;
border-top: 1px solid $--border-color-base; border-top: 1px solid $--border-color-base;
.scrollbar { .scrollbar {
height: 100%; height: 100%;
.code-body { .code-body {
tab-size: 1.5em; tab-size: 1.5em;
width: 100%; width: 100%;
margin: 0; margin: 0;
font-size: 12px; font-size: 12px;
white-space: pre-wrap; white-space: pre-wrap;
}
} }
} }
} }
......
...@@ -31,10 +31,6 @@ ...@@ -31,10 +31,6 @@
.right-part { .right-part {
color: $--color-text-regular; color: $--color-text-regular;
line-height: 15px; line-height: 15px;
.el-button {
padding: 2px;
}
} }
} }
...@@ -347,45 +343,6 @@ ...@@ -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 { .px-publish-view {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -581,3 +538,21 @@ ...@@ -581,3 +538,21 @@
.local-change-marker-class { .local-change-marker-class {
background-color: rgba(64, 166, 255, 0.5); 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 @@ ...@@ -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) { ...@@ -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') : ''; 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 { ...@@ -131,6 +131,26 @@ export default {
type: 'color', type: 'color',
default: '#000' 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: { stroke: {
alias: '描边宽度', alias: '描边宽度',
type: 'number', type: 'number',
......
...@@ -10,6 +10,10 @@ import generateUUID from "uuid/v4"; ...@@ -10,6 +10,10 @@ import generateUUID from "uuid/v4";
const packExts = ['.png']; //, '.jpg', '.jpeg', '.bmp' const packExts = ['.png']; //, '.jpg', '.jpeg', '.bmp'
let canvas = document.createElement('canvas'); 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 = {}) { export async function packImages(assets, options = {}) {
const padding = options.padding || 1; const padding = options.padding || 1;
...@@ -60,16 +64,24 @@ export async function packImages(assets, options = {}) { ...@@ -60,16 +64,24 @@ export async function packImages(assets, options = {}) {
let pack = new MaxRectsBinPack(maxSize, maxSize, false); let pack = new MaxRectsBinPack(maxSize, maxSize, false);
let packedRects = pack.insertRects(remainRects, mode); let packedRects = pack.insertRects(remainRects, mode);
//document.body.append(canvas);
canvas.width = canvas.height = maxSize; canvas.width = canvas.height = maxSize;
let context = canvas.getContext('2d'); let maxWidth = 0, maxHeight = 0;
//context.fillStyle = `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 1)`;
//context.strokeRect(0, 0, maxSize, maxSize);
for (let rect of packedRects) { 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); 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 => { let blob = await new Promise(resolve => {
canvas.toBlob(function (blob) { canvasFinal.toBlob(function (blob) {
resolve(blob); resolve(blob);
}, 'image/png'); }, 'image/png');
}); });
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
</template> </template>
<script> <script>
import {mapState, mapActions, mapMutations} from 'vuex' import {mapState, mapActions, mapMutations, mapGetters} from 'vuex'
import SplitPanes from 'splitpanes' import SplitPanes from 'splitpanes'
import ToolBar from "./Editor/ToolBar"; import ToolBar from "./Editor/ToolBar";
import Inspector from "./Editor/Inspector"; import Inspector from "./Editor/Inspector";
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
import Playground from "./Editor/Playground"; import Playground from "./Editor/Playground";
import Assets from "./Editor/Assets"; import Assets from "./Editor/Assets";
import DetailsDialog from "./Editor/dialogs/DetailsDialog"; import DetailsDialog from "./Editor/dialogs/DetailsDialog";
import {openPreview, playWaiting} from "../utils"; import {clonePureObj, openPreview, playWaiting} from "../utils";
import i18n from "../i18n"; import i18n from "../i18n";
import events from "@/global-events.js" import events from "@/global-events.js"
import db from "../utils/db-storage"; import db from "../utils/db-storage";
...@@ -92,8 +92,16 @@ ...@@ -92,8 +92,16 @@
operators: (state) => state.project.operators, operators: (state) => state.project.operators,
currentOperator: (state) => state.editor.operator, currentOperator: (state) => state.editor.operator,
}), }),
...mapGetters(['behaviorEditor'])
}, },
async mounted() { async mounted() {
if (!window.saveProcesses) {
window.saveProcesses = (processes) => {
this.$nextTick(()=>{
this.saveProcesses(processes);
});
}
}
document.addEventListener('keydown', this.onKeyPress); document.addEventListener('keydown', this.onKeyPress);
events.$on('save-and-preview', () => { events.$on('save-and-preview', () => {
this.clickMenu("preview"); this.clickMenu("preview");
...@@ -143,7 +151,7 @@ ...@@ -143,7 +151,7 @@
async loadProject(forceRemote) { async loadProject(forceRemote) {
const {projectID, project, dataUrl} = this.$route.params; const {projectID, project, dataUrl} = this.$route.params;
if (!forceRemote && await this.localVersionExist(projectID)) { 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, showClose: false,
closeOnClickModal: false, closeOnClickModal: false,
closeOnPressEscape: false, closeOnPressEscape: false,
...@@ -220,6 +228,7 @@ ...@@ -220,6 +228,7 @@
type: 'success' type: 'success'
}); });
} else { } else {
cancel = true;
await this.$confirm(i18n.t('There are conflicts in the project'), i18n.t('Alert'), { await this.$confirm(i18n.t('There are conflicts in the project'), i18n.t('Alert'), {
confirmButtonText: i18n.t('Confirm'), confirmButtonText: i18n.t('Confirm'),
cancelButtonText: i18n.t('Cancel'), cancelButtonText: i18n.t('Cancel'),
...@@ -227,6 +236,7 @@ ...@@ -227,6 +236,7 @@
}).then(() => { }).then(() => {
this.$refs.projectConflictResolveDialog.show(resp.remoteData, resp.localData); this.$refs.projectConflictResolveDialog.show(resp.remoteData, resp.localData);
}).catch((e) => { }).catch((e) => {
this.$loading({}).close();
}); });
} }
} }
...@@ -235,8 +245,10 @@ ...@@ -235,8 +245,10 @@
}, },
async onConflictResolved(data) { async onConflictResolved(data) {
const {projectID, project} = this.$route.params; const {projectID, project} = this.$route.params;
await this.saveProject(true, data); let cancel = await this.saveProject(true, data);
this.loadRemoteVersion(projectID, project, undefined, data); if (!cancel) {
this.loadRemoteVersion(projectID, project, undefined, data);
}
}, },
checkAuth() { checkAuth() {
let checkStatus = this.operators.includes(this.currentOperator); let checkStatus = this.operators.includes(this.currentOperator);
...@@ -381,12 +393,55 @@ ...@@ -381,12 +393,55 @@
this.$refs.missingPackagesDialog.show(missingPackages); this.$refs.missingPackagesDialog.show(missingPackages);
}, },
editBehavior(path, index) { 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() { handleBehaviorsChange() {
this.makeProjectDirty(); this.makeProjectDirty();
}, },
...mapMutations(['makeProjectDirty']), ...mapMutations(['makeProjectDirty', 'overwriteProcesses', 'updateProcessEditorWin']),
...mapActions([ ...mapActions([
'loadPackages', 'loadPackages',
'localVersionExist', 'localVersionExist',
......
...@@ -5,6 +5,14 @@ ...@@ -5,6 +5,14 @@
<el-link @click="deleteAll" :underline="false">{{$t('Delete all')}}</el-link> <el-link @click="deleteAll" :underline="false">{{$t('Delete all')}}</el-link>
</div> </div>
<div class="container"> <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"> <div class="properties">
<template v-if="currentItem"> <template v-if="currentItem">
<div class="item"> <div class="item">
...@@ -14,24 +22,16 @@ ...@@ -14,24 +22,16 @@
<div class="item"> <div class="item">
<p> <p>
<span>size</span>: <el-tag size="mini" type="success">size</el-tag>:
<span>{{assetSize(currentItem.url)}}</span> <span>{{assetSize(currentItem.url)}}</span>
</p> </p>
<p v-for="(field, index) in showFields" :key="index"> <p v-for="(field, index) in showFields" :key="index">
<span>{{field}}</span>: <el-tag size="mini">{{field}}</el-tag>:
<span>{{currentItem[field]}}</span> <span>{{currentItem[field]}}</span>
</p> </p>
</div> </div>
</template> </template>
</div> </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>
</div> </div>
<assets-show ref="assetsShow"/> <assets-show ref="assetsShow"/>
......
...@@ -7,22 +7,39 @@ ...@@ -7,22 +7,39 @@
@click="onClickItem(asset, $event)" @click="onClickItem(asset, $event)"
@delete="onDeleteItem(asset)" @delete="onDeleteItem(asset)"
@combine="onCombineAssets" @combine="onCombineAssets"
@to-jpg="onToJpg"
/> />
<trans-to-jpg-option-dialog ref="transToJpgOptionDialog" @confirm="onConfirmToTransJpg"/>
</div> </div>
</template> </template>
<script> <script>
import {mapGetters, mapMutations} from 'vuex'; import {mapGetters, mapMutations, mapActions} from 'vuex';
import FileItem from "./FileItem"; import FileItem from "./FileItem";
import {transPngToJpg} from "../../../utils";
import TransToJpgOptionDialog from "./TransToJpgOptionDialog";
export default { export default {
name: "AssetList", name: "AssetList",
components: {FileItem}, components: {TransToJpgOptionDialog, FileItem},
props: { props: {
editable: { editable: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
selectable: {
type: Boolean,
default: true,
},
assetsSource: {
type: Array,
},
explicitDepAssets: {
type: Array,
},
implicitDepAssets: {
type: Array,
},
}, },
data() { data() {
return { return {
...@@ -32,17 +49,18 @@ ...@@ -32,17 +49,18 @@
computed: { computed: {
selectedAssets() { selectedAssets() {
let assets = []; let assets = [];
for(let index of this.selectedIndices){ for (let index of this.selectedIndices) {
assets.push(this.assets[index]); assets.push(this.assets[index]);
} }
return assets; return assets;
}, },
combinable(){ combinable() {
return this.selectedIndices.length > 1 return this.selectedIndices.length > 1
}, },
...mapGetters([ assets() {
'assets', return this.assetsSource ? this.assetsSource : this.$store.getters.assets;
]) },
...mapGetters(['assetDepFlag'])
}, },
methods: { methods: {
showFileDetails(asset) { showFileDetails(asset) {
...@@ -50,6 +68,9 @@ ...@@ -50,6 +68,9 @@
}, },
onClickItem(asset, event) { onClickItem(asset, event) {
this.$emit('click-item', asset); this.$emit('click-item', asset);
if (!this.selectable) {
return;
}
if (event.altKey) { if (event.altKey) {
if (this.isSelected(asset)) { if (this.isSelected(asset)) {
this.unselectItem(asset); this.unselectItem(asset);
...@@ -88,6 +109,18 @@ ...@@ -88,6 +109,18 @@
}).catch((e) => { }).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) { selectItem(asset, unselectAll = true) {
if (unselectAll) { if (unselectAll) {
this.selectedIndices.splice(0); this.selectedIndices.splice(0);
...@@ -109,7 +142,21 @@ ...@@ -109,7 +142,21 @@
} }
return this.selectedIndices.includes(index); 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']), ...mapMutations(['combineAssets']),
...mapActions(['replaceAsset']),
} }
} }
</script> </script>
......
...@@ -22,7 +22,9 @@ ...@@ -22,7 +22,9 @@
:command="key" :command="key"
:key="key" :key="key"
:disabled="key === 'combine' && !combinable" :disabled="key === 'combine' && !combinable"
>{{item}}</el-dropdown-item> v-if="showMenuItem(key)"
>{{item}}
</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
...@@ -43,6 +45,9 @@ ...@@ -43,6 +45,9 @@
<span>{{data.name}}</span> <span>{{data.name}}</span>
<span class="full-name">{{data.name}}</span> <span class="full-name">{{data.name}}</span>
</div> </div>
<div :class="{'dep-flag': depFlag >= 0, 'explicit': depFlag === 1, 'implicit': depFlag === 2}">
</div>
</div> </div>
</template> </template>
...@@ -58,7 +63,7 @@ ...@@ -58,7 +63,7 @@
props: { props: {
data: {type: Object}, data: {type: Object},
editable: {type: Boolean, default: false}, editable: {type: Boolean, default: false},
combinable: {type: Boolean, default: false} combinable: {type: Boolean, default: false},
}, },
data() { data() {
return { return {
...@@ -86,6 +91,9 @@ ...@@ -86,6 +91,9 @@
ellipsisName() { ellipsisName() {
return strEllipsis(this.data.name, 14, 5); return strEllipsis(this.data.name, 14, 5);
}, },
depFlag() {
return this.$parent.getDepFlag(this.data.uuid);
},
}, },
methods: { methods: {
onDbclick() { onDbclick() {
...@@ -118,6 +126,9 @@ ...@@ -118,6 +126,9 @@
case 'combine': case 'combine':
this.$emit('combine', this.data); this.$emit('combine', this.data);
break; break;
case 'to-jpg':
this.$emit('to-jpg', this.data);
break;
} }
}, },
editName() { editName() {
...@@ -143,6 +154,14 @@ ...@@ -143,6 +154,14 @@
}); });
}) })
}, },
showMenuItem(key) {
const {ext} = this.data;
if (key === 'to-jpg') {
return ext === '.png';
} else {
return true;
}
},
...mapMutations([ ...mapMutations([
'deleteAsset', 'deleteAsset',
'modifyAsset', '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 @@ ...@@ -73,7 +73,7 @@
} }
}, },
computed: { computed: {
...mapGetters(['activeComponent', 'componentList']) ...mapGetters(['activeComponent', 'componentList', 'behaviorEditable'])
}, },
methods: { methods: {
getBehavior(behavior) { getBehavior(behavior) {
...@@ -89,20 +89,24 @@ ...@@ -89,20 +89,24 @@
} }
}, },
addTrigger(command) { addTrigger(command) {
let trigger = {behaviors: []}; if(this.behaviorEditable()){
switch (command) { let trigger = {behaviors: []};
case 'custom-global': switch (command) {
case 'custom-private': case 'custom-global':
trigger.custom = true; case 'custom-private':
break; trigger.custom = true;
} break;
}
if (command === 'custom-private') { if (command === 'custom-private') {
trigger.private = true; trigger.private = true;
} }
this.addBehavior(command, trigger.behaviors); this.addBehavior(command, trigger.behaviors);
this.$set(this.activeComponent.events, command, trigger); this.$set(this.activeComponent.events, command, trigger);
}else{
this.$message('独立过程编辑器正在使用中,不可编辑')
}
}, },
toggleCollapse(trigger) { toggleCollapse(trigger) {
this.$set(trigger, 'collapse', !trigger.collapse); this.$set(trigger, 'collapse', !trigger.collapse);
...@@ -122,13 +126,17 @@ ...@@ -122,13 +126,17 @@
} }
}, },
addBehavior(eventName, behaviors) { 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({ this.addBehaviorDirect({
alias, behaviors, alias, behaviors,
}); });
this.makeProjectDirty(); this.makeProjectDirty();
}else{
this.$message('独立过程编辑器正在使用中,不可编辑')
}
}, },
editTriggerName(name) { editTriggerName(name) {
this.$prompt(this.$t('Input event name'), this.$t('Rename event'), { this.$prompt(this.$t('Input event name'), this.$t('Rename event'), {
...@@ -167,24 +175,29 @@ ...@@ -167,24 +175,29 @@
this.makeProjectDirty(); this.makeProjectDirty();
}, },
async deleteBehavior(index, behaviors) { async deleteBehavior(index, behaviors) {
let deleteMeta = false; if(this.behaviorEditable()){
await this.$confirm(this.$t('Are you sure to delete it\'s process?'), this.$t('Alert'), { let deleteMeta = false;
confirmButtonText: this.$t('Delete'), await this.$confirm(this.$t('Are you sure to delete it\'s process?'), this.$t('Alert'), {
cancelButtonText: this.$t('Not delete'), confirmButtonText: this.$t('Delete'),
distinguishCancelAndClose: true, cancelButtonText: this.$t('Not delete'),
type: 'warning' distinguishCancelAndClose: true,
}).then(() => { type: 'warning'
deleteMeta = true; }).then(() => {
this.makeProjectDirty(); deleteMeta = true;
}).catch(action => { this.makeProjectDirty();
}).catch(action => {
}); });
this.deleteBehaviorDirect({
behaviors,
index,
deleteMeta,
})
}else{
this.$message('独立过程编辑器正在使用中,不可编辑')
}
this.deleteBehaviorDirect({
behaviors,
index,
deleteMeta,
})
}, },
...mapActions([ ...mapActions([
'modifyActiveView', 'modifyActiveView',
......
<template> <template>
<div class="zero-inspector-props-form" v-if="activeComponent.uuid"> <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> <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="clickEditStoreExp">{{$t('Edit store')}}</el-button>
<el-button size="mini" @click="clickEditStoreComputed">{{$t('Edit computed')}}</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> </div>
<el-form-item :label="$t('Name')"> <el-form-item :label="$t('Name')">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input> <el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
...@@ -51,6 +52,7 @@ ...@@ -51,6 +52,7 @@
</el-scrollbar> </el-scrollbar>
</el-form> </el-form>
<store-editor-dialog ref="storeEditorDialog"/> <store-editor-dialog ref="storeEditorDialog"/>
<asset-dependence-dialog ref="assetDependenceDialog"/>
</div> </div>
</template> </template>
...@@ -60,10 +62,11 @@ ...@@ -60,10 +62,11 @@
import {getCmpProps} from "../../../utils/common"; import {getCmpProps} from "../../../utils/common";
import StoreEditorDialog from "./PropsTab/StoreEditorDialog"; import StoreEditorDialog from "./PropsTab/StoreEditorDialog";
import ElFormItem from "../../../components/inputs/form-item"; import ElFormItem from "../../../components/inputs/form-item";
import AssetDependenceDialog from "./PropsTab/AssetDependenceDialog";
export default { export default {
name: 'PropsTab', name: 'PropsTab',
components: {ElFormItem, StoreEditorDialog, PropsEditor}, components: {AssetDependenceDialog, ElFormItem, StoreEditorDialog, PropsEditor},
data() { data() {
const componentsMap = this.$t('view_node_menu'); const componentsMap = this.$t('view_node_menu');
const customCmdNames = this.$t('customCmds'); const customCmdNames = this.$t('customCmds');
...@@ -124,6 +127,9 @@ ...@@ -124,6 +127,9 @@
clickEditStoreComputed() { clickEditStoreComputed() {
this.$refs.storeEditorDialog.edit('computed', this.activeComponent); this.$refs.storeEditorDialog.edit('computed', this.activeComponent);
}, },
clickAssetManager(){
this.$refs.assetDependenceDialog.edit(this.activeComponent);
},
...mapMutations(['makeProjectDirty', 'addCmd', 'deleteCmd']), ...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 @@ ...@@ -4,19 +4,22 @@
<div class="right-part"> <div class="right-part">
<span> <span>
[{{project.name}}] [{{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> </span>
</div> </div>
<project-remark-dialog ref="projectRemarkDialog"/>
</div> </div>
</template> </template>
<script> <script>
import {mapState, mapActions, mapMutations} from 'vuex' import {mapState, mapActions, mapMutations} from 'vuex'
import SampleMenu from "../../components/SampleMenu"; import SampleMenu from "../../components/SampleMenu";
import ProjectRemarkDialog from "./dialogs/ProjectRemarkDialog";
export default { export default {
name: "ToolBar", name: "ToolBar",
components: {SampleMenu}, components: {ProjectRemarkDialog, SampleMenu},
data() { data() {
return {} return {}
}, },
...@@ -40,7 +43,7 @@ ...@@ -40,7 +43,7 @@
return menu; return menu;
} }
}, },
async mounted() { async mounted() {
//监听键盘事件 //监听键盘事件
//document.addEventListener('keydown', this.onKeyPress); //document.addEventListener('keydown', this.onKeyPress);
...@@ -63,20 +66,23 @@ ...@@ -63,20 +66,23 @@
}).catch(() => { }).catch(() => {
}); });
}, },
editRemark() {
this.$refs.projectRemarkDialog.show();
},
onKeyPress(e) { onKeyPress(e) {
if (e.key === 'z' && (e.ctrlKey || e.metaKey) && (!e.shiftKey)) { if (e.key === 'z' && (e.ctrlKey || e.metaKey) && (!e.shiftKey)) {
//快捷键ctrl+z执行撤销 //快捷键ctrl+z执行撤销
e.preventDefault(); 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"); this.clickMenu("undo");
} }
return false; 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执行重做 //快捷键ctrl+shift+z执行重做
e.preventDefault(); e.preventDefault();
//是否可以执行重做 //是否可以执行重做
if(!(this.project.stackIndex === 0)){ if (!(this.project.stackIndex === 0)) {
this.clickMenu("redo"); this.clickMenu("redo");
} }
return false; return false;
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
:append-to-body="true" custom-class="flex-dialog behavior-editor-dialog"> :append-to-body="true" custom-class="flex-dialog behavior-editor-dialog">
<behavior-editor v-if="editorReady" ref="behaviorEditor" class="full-size"></behavior-editor> <behavior-editor v-if="editorReady" ref="behaviorEditor" class="full-size"></behavior-editor>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<div></div> <div>
</div>
<div> <div>
<!--<el-button size="mini" @click="onSave(true)">{{$t('Save And Preview')}}</el-button> <!--<el-button size="mini" @click="onSave(true)">{{$t('Save And Preview')}}</el-button>
<el-button size="mini" type="primary" @click="onSave(false)">{{$t('Save And Close')}}</el-button>--> <el-button size="mini" type="primary" @click="onSave(false)">{{$t('Save And Close')}}</el-button>-->
...@@ -20,6 +22,7 @@ ...@@ -20,6 +22,7 @@
import {mapState, mapMutations, mapActions} from 'vuex' import {mapState, mapMutations, mapActions} from 'vuex'
import BehaviorEditor from "../behavior-editor/BehaviorEditor"; import BehaviorEditor from "../behavior-editor/BehaviorEditor";
import events from "@/global-events.js" import events from "@/global-events.js"
import {clonePureObj} from "../../../utils";
export default { export default {
name: "BehaviorEditorDialog", name: "BehaviorEditorDialog",
...@@ -49,7 +52,7 @@ ...@@ -49,7 +52,7 @@
this.visible = true; this.visible = true;
}, },
async onSave() { async onSave() {
const behavior = await this.behavior_save(); this.behavior_save();
/*for (let i = 0, li = this.behaviors.length; i < li; i++) { /*for (let i = 0, li = this.behaviors.length; i < li; i++) {
const b = this.behaviors[i]; const b = this.behaviors[i];
if (b.uuid === behavior.uuid) { if (b.uuid === behavior.uuid) {
...@@ -95,8 +98,6 @@ ...@@ -95,8 +98,6 @@
}, },
...mapMutations([ ...mapMutations([
'behavior_cancel', 'behavior_cancel',
]),
...mapActions([
'behavior_save', '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 @@ ...@@ -217,7 +217,7 @@
const params = { const params = {
projectId: pxPid, projectId: pxPid,
operate: operate + 1, operate: operate + 1,
type:(operate + 1).toString(), type:(tplType + 1).toString(),
id:skinId, id:skinId,
html:tpl, html:tpl,
name, name,
......
...@@ -2,6 +2,15 @@ ...@@ -2,6 +2,15 @@
<el-scrollbar v-if="editData" class="project-scrollbar" wrap-class="wrap-x-hidden" view-class="project-editor"> <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" <el-form @submit.native.prevent ref="form" :model="editData" size="mini" label-position="right"
label-width="150px"> 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-form-item prop="pageTitle" :label="$t('Page title')">
<el-input v-model="editData.pageTitle"/> <el-input v-model="editData.pageTitle"/>
</el-form-item> </el-form-item>
...@@ -14,6 +23,22 @@ ...@@ -14,6 +23,22 @@
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </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-form-item prop="containerId" :label="$t('Container ID')">
<el-input v-model="editData.containerId"/> <el-input v-model="editData.containerId"/>
</el-form-item> </el-form-item>
...@@ -67,12 +92,14 @@ ...@@ -67,12 +92,14 @@
data() { data() {
const scaleMode = this.$t('scaleMode'); const scaleMode = this.$t('scaleMode');
const rendererType = this.$t('rendererType'); const rendererType = this.$t('rendererType');
const behaviorEditors = this.$t('behaviorEditors');
return { return {
visible: false, visible: false,
editData: null, editData: null,
scaleMode, scaleMode,
rendererType, rendererType,
monacoEditorOptions, monacoEditorOptions,
behaviorEditors,
} }
}, },
computed: { computed: {
......
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
import {PROJECT_PAGE_SIZE} from "../config"; import {PROJECT_PAGE_SIZE} from "../config";
import ProjectHistoryDialog from "./Home/ProjectHistoryDialog"; import ProjectHistoryDialog from "./Home/ProjectHistoryDialog";
import ElFormItem from "../components/inputs/form-item"; import ElFormItem from "../components/inputs/form-item";
import db from "../utils/db-storage";
export default { export default {
name: "Home", name: "Home",
...@@ -118,6 +119,7 @@ ...@@ -118,6 +119,7 @@
methods: { methods: {
prepare() { prepare() {
return Promise.all([ return Promise.all([
db.open('store'),
this.updateEnv(), this.updateEnv(),
this.handleCurrentChange(1), this.handleCurrentChange(1),
]) ])
...@@ -188,8 +190,15 @@ ...@@ -188,8 +190,15 @@
onDuplicateProject(projectID) { onDuplicateProject(projectID) {
this.editProject(projectID); this.editProject(projectID);
}, },
onSelectProject(project, dataUrl) { async onSelectProject(project, dataUrl) {
this.$router.push({name: 'editor', params: {projectID: project.id, 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) { editProject(projectID) {
this.$router.push({name: 'editor', params: {projectID}}); this.$router.push({name: 'editor', params: {projectID}});
...@@ -214,6 +223,7 @@ ...@@ -214,6 +223,7 @@
'createProject', 'createProject',
'deleteProject', 'deleteProject',
'updateEnv', 'updateEnv',
'localVersionExist',
]), ]),
}, },
} }
......
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
const {projectID} = this.$route.params; const {projectID} = this.$route.params;
const {data, tpl} = await db.get('preview', projectID); const {data, tpl} = await db.get('preview', projectID);
console.log(data); console.log(data);
//console.log(JSON.stringify(data.processes)); //console.log(JSON.stringify(data));
let {options: {pageTitle, }, } = data; let {options: {pageTitle, }, } = data;
document.title = pageTitle; 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 = { ...@@ -12,7 +12,7 @@ module.exports = {
enableInSFC: true enableInSFC: true
} }
}, },
devServer: { /*devServer: {
host: '0.0.0.0', host: '0.0.0.0',
port: '8080', port: '8080',
proxy: { proxy: {
...@@ -26,11 +26,11 @@ module.exports = { ...@@ -26,11 +26,11 @@ module.exports = {
target: serverHost target: serverHost
} }
} }
}, },*/
configureWebpack: { configureWebpack: {
plugins: [ plugins: [
new MonacoWebpackPlugin({ new MonacoWebpackPlugin({
languages: ['javascript', 'typescript', 'json', 'html'], languages: ['javascript', 'typescript', 'json', 'html', 'markdown'],
publicPath: process.env.NODE_ENV === 'production' ? "/monaco" : "", publicPath: process.env.NODE_ENV === 'production' ? "/monaco" : "",
}) })
] ]
......
...@@ -3125,10 +3125,10 @@ electron-to-chromium@^1.3.322: ...@@ -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" 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= integrity sha1-cXFaypr9Mo6iCKO8RlHBW4afDRs=
element-ui@^2.4.5: element-ui@^2.13.1:
version "2.13.0" version "2.13.1"
resolved "https://registry.npm.taobao.org/element-ui/download/element-ui-2.13.0.tgz#f6bb04e5b0a76ea5f62466044b774407ba4ebd2d" 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-9rsE5bCnbqX2JGYES3dEB7pOvS0= integrity sha1-DLGkXPJ6phxgHe++GSdArFy533w=
dependencies: dependencies:
async-validator "~1.8.1" async-validator "~1.8.1"
babel-helper-vue-jsx-merge-props "^2.0.0" babel-helper-vue-jsx-merge-props "^2.0.0"
...@@ -3229,6 +3229,11 @@ entities@^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" 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= 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: errno@^0.1.3, errno@~0.1.7:
version "0.1.7" version "0.1.7"
resolved "https://registry.npm.taobao.org/errno/download/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" resolved "https://registry.npm.taobao.org/errno/download/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
...@@ -5048,6 +5053,13 @@ lines-and-columns@^1.1.6: ...@@ -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" resolved "https://registry.npm.taobao.org/lines-and-columns/download/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= 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: load-json-file@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.npm.taobao.org/load-json-file/download/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" 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: ...@@ -5232,6 +5244,17 @@ map-visit@^1.0.0:
dependencies: dependencies:
object-visit "^1.0.0" 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: md5.js@^1.3.4:
version "1.3.5" version "1.3.5"
resolved "https://registry.npm.taobao.org/md5.js/download/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" 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: ...@@ -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" 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= 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: media-typer@0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 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: ...@@ -7064,7 +7092,7 @@ regenerate@^1.2.1, regenerate@^1.4.0:
regenerator-runtime@^0.11.0: regenerator-runtime@^0.11.0:
version "0.11.1" 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= integrity sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=
regenerator-runtime@^0.13.2: regenerator-runtime@^0.13.2:
...@@ -8369,6 +8397,11 @@ typedarray@^0.0.6: ...@@ -8369,6 +8397,11 @@ typedarray@^0.0.6:
resolved "https://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 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: uglify-js@3.4.x:
version "3.4.10" 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" 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