Commit 480c0363 authored by 张晨辰's avatar 张晨辰

feat: merge

parents 6bdad43a f8719341
...@@ -59,7 +59,8 @@ export async function fetchApi(uri, {params, method = 'get', contentType = 'json ...@@ -59,7 +59,8 @@ export async function fetchApi(uri, {params, method = 'get', contentType = 'json
break; break;
} }
} else { } else {
url += (url.indexOf('?') < 0 ? '?' : '') + (url.endsWith('&') ? '' : '&') + stringify(params); url += (url.indexOf('?') < 0 ? '?' : '');
url += (url.endsWith('?') ? '' : '&') + stringify(params);
} }
} }
......
...@@ -6,8 +6,9 @@ ...@@ -6,8 +6,9 @@
import {fetchApi} from "./common"; import {fetchApi} from "./common";
export async function fetchAll() { export async function fetchAll(currentPage, pageSize) {
return await fetchApi('/api/project/query', { return await fetchApi('/api/project/query', {
params: {currentPage, pageSize,},
errMessage: 'Failed to fetch projects', errMessage: 'Failed to fetch projects',
}) })
} }
...@@ -52,9 +53,9 @@ export async function saveOne(project) { ...@@ -52,9 +53,9 @@ export async function saveOne(project) {
}) })
} }
export async function pack(id, debug) { export async function pack(id, debug, packedAssets) {
return await fetchApi('/api/project/pack', { return await fetchApi('/api/project/pack', {
params: {id, debug}, params: {id, debug, packedAssets},
method: 'post', method: 'post',
errMessage: 'Failed to pack project', errMessage: 'Failed to pack project',
}) })
...@@ -73,11 +74,13 @@ export async function importView(file) { ...@@ -73,11 +74,13 @@ export async function importView(file) {
return response; return response;
} }
export async function uploadFile(file) { export async function uploadFile(file, compress = false) {
let params = {file};
if (compress) {
params.compress = true;
}
const response = await fetchApi('/api/uploadFile', { const response = await fetchApi('/api/uploadFile', {
params: { params,
file,
},
method: 'post', method: 'post',
contentType: 'form-data', contentType: 'form-data',
errMessage: 'Failed to upload file', errMessage: 'Failed to upload file',
......
...@@ -6,6 +6,7 @@ export let API_HOST; ...@@ -6,6 +6,7 @@ export let API_HOST;
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
//API_HOST = 'http://10.10.95.74:7777'; //API_HOST = 'http://10.10.95.74:7777';
//API_HOST = 'http://192.168.1.16:7777'; //API_HOST = 'http://192.168.1.16:7777';
//API_HOST = 'http://192.168.0.104:7777';
//API_HOST = 'http://10.10.92.33:7777'; //API_HOST = 'http://10.10.92.33:7777';
//API_HOST = 'http://localhost:3002'; //API_HOST = 'http://localhost:3002';
API_HOST = window.__data.apiHost; API_HOST = window.__data.apiHost;
...@@ -22,6 +23,8 @@ export const SSO_VERIFY_PAGE_URL = '/sso/logout'; ...@@ -22,6 +23,8 @@ export const SSO_VERIFY_PAGE_URL = '/sso/logout';
export const DOCK_POINT_OFFSET = 4; export const DOCK_POINT_OFFSET = 4;
export const PAGE_SIZE = 20;
//文件类型图标 t表示展示缩略图 //文件类型图标 t表示展示缩略图
export const fileTypeIcon = { export const fileTypeIcon = {
'': 'file-empty', '': 'file-empty',
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
"Delete all": "Delete all", "Delete all": "Delete all",
"Import": "Import", "Import": "Import",
"Export": "Export", "Export": "Export",
"Importing project": "Importing project",
"Exporting project": "Exporting project",
"Upload": "Upload", "Upload": "Upload",
"Uploading": "Uploading", "Uploading": "Uploading",
"Edit": "Edit", "Edit": "Edit",
...@@ -55,6 +57,7 @@ ...@@ -55,6 +57,7 @@
"Packing": "Packing", "Packing": "Packing",
"Type": "Type", "Type": "Type",
"Group": "Group", "Group": "Group",
"Meta Search": "Meta Search",
"Access denied": "Access denied", "Access denied": "Access denied",
"Invalid router": "Invalid router", "Invalid router": "Invalid router",
"Jump after": "Jump after {cd}s", "Jump after": "Jump after {cd}s",
...@@ -133,7 +136,8 @@ ...@@ -133,7 +136,8 @@
"save": "Save", "save": "Save",
"details": "Details", "details": "Details",
"preview": "Preview", "preview": "Preview",
"pack": "Pack", "preview2": "Fast-Preview",
"publish": "Publish",
"exit": "Exit", "exit": "Exit",
"undo": "Undo", "undo": "Undo",
"redo": "Redo" "redo": "Redo"
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
"Delete all": "删除全部", "Delete all": "删除全部",
"Import": "导入", "Import": "导入",
"Export": "导出", "Export": "导出",
"Importing project": "项目导入中",
"Exporting project": "项目导出中",
"Upload": "上传", "Upload": "上传",
"Uploading": "上传中", "Uploading": "上传中",
"Edit": "编辑", "Edit": "编辑",
...@@ -55,6 +57,7 @@ ...@@ -55,6 +57,7 @@
"Packing": "打包", "Packing": "打包",
"Type": "类型", "Type": "类型",
"Group": "分组", "Group": "分组",
"Meta Search": "过程元查找",
"Access denied": "无权限", "Access denied": "无权限",
"Invalid router": "无效的页面", "Invalid router": "无效的页面",
"Jump after": "{cd}秒后跳转", "Jump after": "{cd}秒后跳转",
...@@ -133,7 +136,8 @@ ...@@ -133,7 +136,8 @@
"save": "保存", "save": "保存",
"details": "详情", "details": "详情",
"preview": "预览", "preview": "预览",
"pack": "发布", "preview2": "快速预览",
"publish": "发布",
"exit": "退出", "exit": "退出",
"undo": "撤销", "undo": "撤销",
"redo": "重做" "redo": "重做"
......
...@@ -18,5 +18,10 @@ export default new Router({ ...@@ -18,5 +18,10 @@ export default new Router({
name: 'editor', name: 'editor',
component: () => import('./views/Editor.vue') component: () => import('./views/Editor.vue')
}, },
{
path: '/preview/:projectID',
name: 'preview',
component: () => import('./views/Preview.vue')
},
] ]
}) })
...@@ -186,7 +186,10 @@ export const behaviorStore = { ...@@ -186,7 +186,10 @@ export const behaviorStore = {
addProcessMeta(commit, isInline, masterProcess, meta); addProcessMeta(commit, isInline, masterProcess, meta);
return meta; return meta;
} },
searchMeta({state}, {keyword}) {
searchMeta(state.data.processes, keyword);
},
} }
}; };
...@@ -204,3 +207,11 @@ function addProcessMeta(commit, isInline, masterProcess, meta) { ...@@ -204,3 +207,11 @@ function addProcessMeta(commit, isInline, masterProcess, meta) {
commit('addProcessMeta', meta); commit('addProcessMeta', meta);
} }
} }
function searchMeta(processes, keyword, path) {
for(let process of processes){
/*if(process.id === keyword || process.name === keyword){
}*/
}
}
...@@ -11,7 +11,7 @@ export const envStore = { ...@@ -11,7 +11,7 @@ export const envStore = {
state: { state: {
initialized: false, initialized: false,
name: 'Zeroing Editor', name: 'Zeroing Editor',
version: '1.0.1', version: '1.0.2',
templates: { templates: {
builtin: ['blank'], builtin: ['blank'],
custom: [], custom: [],
...@@ -19,6 +19,7 @@ export const envStore = { ...@@ -19,6 +19,7 @@ export const envStore = {
processes: [], processes: [],
scripts: [], scripts: [],
customs: [], customs: [],
projectCount: 0,
}, },
mutations: { mutations: {
updateEnv(state, env) { updateEnv(state, env) {
......
...@@ -11,6 +11,7 @@ import { clonePureObj, saveAs } from "../../utils"; ...@@ -11,6 +11,7 @@ import { clonePureObj, saveAs } from "../../utils";
import { template } from "../../template"; import { template } from "../../template";
import { importView, uploadFile } from "../../api/project"; import { importView, uploadFile } from "../../api/project";
import events from "@/global-events"; import events from "@/global-events";
import {packImages} from "../../utils/sheet-pack";
const defaultOptions = { const defaultOptions = {
pageTitle: 'no title', pageTitle: 'no title',
...@@ -313,6 +314,13 @@ export const projectStore = { ...@@ -313,6 +314,13 @@ export const projectStore = {
uuid: generateUUID(), uuid: generateUUID(),
}) })
}, },
replaceAsset(state, {uuid, url}) {
for(let asset of state.data.assets){
if(asset.uuid === uuid){
asset.url = url;
}
}
},
deleteAsset(state, uuid) { deleteAsset(state, uuid) {
const { assets } = state.data; const { assets } = state.data;
for (let i = 0, li = assets.length; i < li; i++) { for (let i = 0, li = assets.length; i < li; i++) {
...@@ -429,7 +437,7 @@ export const projectStore = { ...@@ -429,7 +437,7 @@ export const projectStore = {
/** /**
* 返回Array格式的views * 返回Array格式的views
*/ */
views: state => { views(state) {
// 如果有选中的节点,则展示对应的视图组 // 如果有选中的节点,则展示对应的视图组
// 否则展示第一个视图组 // 否则展示第一个视图组
let _view = []; let _view = [];
...@@ -440,6 +448,9 @@ export const projectStore = { ...@@ -440,6 +448,9 @@ export const projectStore = {
} }
return _view; return _view;
},
assets(state) {
return state.data.assets;
} }
}, },
actions: { actions: {
...@@ -558,11 +569,10 @@ export const projectStore = { ...@@ -558,11 +569,10 @@ export const projectStore = {
modifyActiveView({ commit }, view) { modifyActiveView({ commit }, view) {
commit('modifyActiveView', view) commit('modifyActiveView', view)
}, },
/** /**
* 新增节点脚本 * 新增节点脚本
* @param {*} param0 * @param {*} param0
* @param {*} data * @param {*} script
*/ */
addNodeScript({ commit, state }, script) { addNodeScript({ commit, state }, script) {
let _scripts = _.cloneDeep(state.activeComponent.scripts || []); let _scripts = _.cloneDeep(state.activeComponent.scripts || []);
...@@ -596,38 +606,91 @@ export const projectStore = { ...@@ -596,38 +606,91 @@ export const projectStore = {
saveAs(content, `view-${view.name}.zrv`); saveAs(content, `view-${view.name}.zrv`);
}); });
}, },
async uploadFiles({ commit }, files) { async uploadFiles({commit}, files) {
const {failedList, result} = await uploadFiles(files);
for (let item of result) {
const { url, __originFile } = item;
commit('addAsset', { url, file: __originFile });
}
return failedList;
},
async replaceAsset({commit}, {uuid, file}) {
const {failedList, result} = await uploadFiles([file]);
commit('replaceAsset', {uuid, url: result[0].url,});
return failedList;
},
async packProject({state}, debug) {
let packedAssets;
if(!debug){
packedAssets = await packAssets(state.data.assets);
}
const result = await projectApi.pack(state.id, debug, packedAssets);
console.log(result);
return result;
},
savePreview({state, rootState, getters}) {
const {project} = getters;
const {processes, scripts, customs} = rootState.env;
const data = {
processes, scripts, customs,
data: state.data,
};
localStorage.setItem('preview-project-' + project.id, JSON.stringify(data));
localStorage.setItem('preview-ts', Date.now().toString());
}
},
};
async function uploadFiles(files) {
const failedList = []; const failedList = [];
let ps = []; let ps = [];
for (let file of files) { for (let file of files) {
events.$emit('upload-indicator', true); events.$emit('upload-indicator', true);
ps.push( ps.push(
uploadFile(file).catch(e => { uploadFile(file).catch(e => {
failedList.push(file);
}).finally(() => { }).finally(() => {
events.$emit('upload-indicator', false); events.$emit('upload-indicator', false);
}) })
); );
} }
const result = await Promise.all(ps); const result = await Promise.all(ps);
for (let item of result) {
const { url, __originFile } = item; return {
commit('addAsset', { url, file: __originFile }); failedList,
} result,
/*for (let file of files) { };
try { }
const {url} = await uploadFile(file);
commit('addAsset', {url, file}); async function packAssets(assets) {
}catch (e) { let failedList = [];
failedList.push(file); let newAssets = assets.concat();
await packImages(newAssets);
for (let asset of newAssets) {
if (asset.file) {
const {url} = await uploadFile(asset.file, true).catch(e => {
failedList.push(asset);
});
let sheetConfig = {
file: url,
type: 'sheet',
frames: asset.frames,
};
let sheetConfigFile = new File([JSON.stringify(sheetConfig)], 'sheet.json', {type: 'plain/text'});
const {url: sheetConfigUrl} = await uploadFile(sheetConfigFile).catch(e => {
failedList.push(asset);
});
asset.url = sheetConfigUrl;
asset.uuid = generateUUID();
delete asset.file;
delete asset.frames;
} }
events.$emit('upload-indicator', false);
}*/
return failedList;
},
async packProject({ state }, debug) {
const result = await projectApi.pack(state.id, debug);
console.log(result);
return result;
} }
}, console.log(newAssets);
};
return newAssets;
}
...@@ -25,10 +25,13 @@ export const projectsStore = { ...@@ -25,10 +25,13 @@ export const projectsStore = {
}, },
}, },
actions: { actions: {
async updateProjects({commit}) { async fetchProjects({commit}, {currentPage, pageSize}) {
const projects = await projectApi.fetchAll(); const projects = await projectApi.fetchAll(currentPage, pageSize);
commit('updateProjects', projects); commit('updateProjects', projects);
}, },
async fetchProject({commit}, projectId) {
return await projectApi.fetchOne(projectId);
},
async createProject({commit}, data) { async createProject({commit}, data) {
const project = await projectApi.createOne(data); const project = await projectApi.createOne(data);
commit('addProject', project); commit('addProject', project);
......
...@@ -34,10 +34,8 @@ content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, u ...@@ -34,10 +34,8 @@ content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, u
<body> <body>
<div id="$CONTAINER_ID$" style="line-height:0;font-size:0"></div> <div id="$CONTAINER_ID$" style="line-height:0;font-size:0"></div>
<script src="${process.env.NODE_ENV === 'development' ? 'http://10.10.94.134:4002/dist/index.js' : 'http://yun.duiba.com.cn/editor/zeroing/libs/engine.a913456caee2ee40c20108a1c4571f61479e54a1.js'}"></script> <script src="${process.env.NODE_ENV === 'development' ? 'http://10.10.94.134:4002/dist/index.js' : 'http://yun.duiba.com.cn/editor/zeroing/libs/engine.d20665eae76962ad21c153fe7a719130b08e292c.js.js'}"></script>
<script src="$PROCESSES_URL$"></script> $SCRIPTS$
<script src="$SCRIPTS_URL$"></script>
<script src="$CUSTOMS_URL$"></script>
<script> <script>
engine.launch('//yun.duiba.com.cn/aurora/$VERSION$-data.json'); engine.launch('//yun.duiba.com.cn/aurora/$VERSION$-data.json');
</script> </script>
......
...@@ -67,11 +67,11 @@ ...@@ -67,11 +67,11 @@
&:hover { &:hover {
& > .icon > .operate-bar { & > .icon > .operate-bar {
display: block; visibility: visible;
} }
& > .name > .full-name { & > .name > .full-name {
display: block; visibility: visible;
} }
} }
...@@ -93,14 +93,18 @@ ...@@ -93,14 +93,18 @@
} }
.operate-bar { .operate-bar {
display: none; visibility: hidden;
position: absolute; position: absolute;
right: 1px; right: 1px;
bottom: 1px; bottom: 1px;
display: flex;
.el-button + .el-button{
margin-left: 2px;
}
.el-button { .el-button {
padding: 2px; padding: 2px;
margin-left: 2px;
} }
} }
} }
...@@ -121,7 +125,7 @@ ...@@ -121,7 +125,7 @@
} }
.full-name { .full-name {
display: none; visibility: hidden;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
......
...@@ -64,12 +64,26 @@ $dock-pin-width: 9px; ...@@ -64,12 +64,26 @@ $dock-pin-width: 9px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.el-button{
padding: 3px;
margin-top: 2px;
margin-right: 2px;
}
.top-bar{
display: flex;
justify-content: space-between;
align-items: flex-start;
.edit-path { .edit-path {
flex: 1;
padding: 5px; padding: 5px;
height: 14px; height: 14px;
border-bottom: 1px solid $--border-color-light; border-bottom: 1px solid $--border-color-light;
} }
}
.operate-bar{ .operate-bar{
padding: 3px; padding: 3px;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
.editor-name { .editor-name {
font-size: 50px; font-size: 50px;
color: $--color-primary; color: $--color-primary;
margin: 10px 0;
} }
.editor-version { .editor-version {
...@@ -25,6 +26,7 @@ ...@@ -25,6 +26,7 @@
.project-list { .project-list {
flex: 1; flex: 1;
margin-bottom: 10px; margin-bottom: 10px;
height: 0;
} }
.project-info { .project-info {
...@@ -43,4 +45,9 @@ ...@@ -43,4 +45,9 @@
.project-detail { .project-detail {
} }
.bottom-bar{
display: flex;
justify-content: space-between;
}
} }
\ No newline at end of file
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
} }
.active {
border: 2px dashed rgb(20, 100, 206);
}
} }
// .zero-playground-draw-panel{ // .zero-playground-draw-panel{
// min-height: 1200px; // min-height: 1200px;
...@@ -60,6 +64,3 @@ ...@@ -60,6 +64,3 @@
cursor: default; cursor: default;
user-select: none; user-select: none;
} }
.active {
border: 2px dashed rgb(20, 100, 206);
}
/**
* Based on the Public Domain MaxRectanglesBinPack.cpp source by Jukka Jylänki
* https://github.com/juj/RectangleBinPack/
*
* Based on C# port by Sven Magnus
* http://unifycommunity.com/wiki/index.php?title=MaxRectanglesBinPack
*
* Based on ActionScript3 by DUZENGQIANG
* http://www.duzengqiang.com/blog/post/971.html
*
* Ported to javascript by 06wj
* https://github.com/06wj/MaxRectsBinPack
*/
/**
* Rect
* @param {Number} x 矩形坐标x
* @param {Number} y 矩形坐标y
* @param {Number} width 矩形宽
* @param {Number} height 矩形高
*/
function Rect(x, y, width, height) {
this.x = x || 0;
this.y = y || 0;
this.width = width || 0;
this.height = height || 0;
}
Rect.prototype = {
constructor: Rect,
/**
* clone 复制
* @return {Rect}
*/
clone: function () {
return new Rect(this.x, this.y, this.width, this.height);
}
};
Rect.isContainedIn = function (a, b) {
return a.x >= b.x && a.y >= b.y
&& a.x + a.width <= b.x + b.width
&& a.y + a.height <= b.y + b.height;
};
var BestShortSideFit = 0; ///< -BSSF: Positions the Rectangle against the short side of a free Rectangle into which it fits the best.
var BestLongSideFit = 1; ///< -BLSF: Positions the Rectangle against the long side of a free Rectangle into which it fits the best.
var BestAreaFit = 2; ///< -BAF: Positions the Rectangle into the smallest free Rectangle into which it fits.
var BottomLeftRule = 3; ///< -BL: Does the Tetris placement.
var ContactPointRule = 4; ///< -CP: Choosest the placement where the Rectangle touches other Rectangles as much as possible.
/**
* MaxRectanglesBinPack
* @param {Number} width 容器宽度
* @param {Number} height 容器高度
* @param {Boolean} allowRotate 是否允许旋转
*/
export default function MaxRectsBinPack(width, height, allowRotate) {
this.binWidth = 0;
this.binHeight = 0;
this.allowRotate = false;
this.usedRectangles = [];
this.freeRectangles = [];
this.init(width, height, allowRotate);
}
MaxRectsBinPack.prototype = {
constructor: MaxRectsBinPack,
/**
* 初始化
* @param {Number} width 容器宽度
* @param {Number} height 容器高度
* @param {Boolean} allowRotate 是否允许旋转
*/
init: function (width, height, allowRotate) {
this.binWidth = width;
this.binHeight = height;
this.allowRotate = allowRotate || false;
this.usedRectangles.length = 0;
this.freeRectangles.length = 0;
this.freeRectangles.push(new Rect(0, 0, width, height));
},
/**
* insert a new rect
* @param {Number} width 矩形宽
* @param {Number} height 矩形高
* @param {Number} method 分配方法 0~4
* @return {Rect}
*/
insert: function (width, height, method) {
var newNode = new Rect();
var score1 = {
value: 0
};
var score2 = {
value: 0
};
method = method || 0;
switch (method) {
case BestShortSideFit:
newNode = this._findPositionForNewNodeBestShortSideFit(width, height, score1, score2);
break;
case BottomLeftRule:
newNode = this._findPositionForNewNodeBottomLeft(width, height, score1, score2);
break;
case ContactPointRule:
newNode = this._findPositionForNewNodeContactPoint(width, height, score1);
break;
case BestLongSideFit:
newNode = this._findPositionForNewNodeBestLongSideFit(width, height, score2, score1);
break;
case BestAreaFit:
newNode = this._findPositionForNewNodeBestAreaFit(width, height, score1, score2);
break;
}
if (newNode.height === 0) {
return newNode;
}
this._placeRectangle(newNode);
return newNode;
},
/**
* 插入一组矩形
* @param {Array} rectangles 矩形数组
* @param {Number} method 分配方法 0~4
* @return {Array} 成功插入的数组
*/
insertRects: function (rectangles, method) {
var res = [];
while (rectangles.length > 0) {
var bestScore1 = Infinity;
var bestScore2 = Infinity;
var bestRectangleIndex = -1;
var bestNode = new Rect();
for (var i = 0; i < rectangles.length; i++) {
var score1 = {
value: 0
};
var score2 = {
value: 0
};
var newNode = this._scoreRectangle(rectangles[i].width, rectangles[i].height, method, score1, score2);
if (score1.value < bestScore1 || (score1.value == bestScore1 && score2.value < bestScore2)) {
bestScore1 = score1.value;
bestScore2 = score2.value;
bestNode = newNode;
bestRectangleIndex = i;
}
}
if (bestRectangleIndex == -1) {
return res;
}
this._placeRectangle(bestNode);
var rect = rectangles.splice(bestRectangleIndex, 1)[0];
rect.x = bestNode.x;
rect.y = bestNode.y;
res.push(rect);
}
return res;
},
_placeRectangle: function (node) {
var numRectanglesToProcess = this.freeRectangles.length;
for (var i = 0; i < numRectanglesToProcess; i++) {
if (this._splitFreeNode(this.freeRectangles[i], node)) {
this.freeRectangles.splice(i, 1);
i--;
numRectanglesToProcess--;
}
}
this._pruneFreeList();
this.usedRectangles.push(node);
},
_scoreRectangle: function (width, height, method, score1, score2) {
var newNode = new Rect();
score1.value = Infinity;
score2.value = Infinity;
switch (method) {
case BestShortSideFit:
newNode = this._findPositionForNewNodeBestShortSideFit(width, height, score1, score2);
break;
case BottomLeftRule:
newNode = this._findPositionForNewNodeBottomLeft(width, height, score1, score2);
break;
case ContactPointRule:
newNode = this._findPositionForNewNodeContactPoint(width, height, score1);
// todo: reverse
score1 = -score1; // Reverse since we are minimizing, but for contact point score bigger is better.
break;
case BestLongSideFit:
newNode = this._findPositionForNewNodeBestLongSideFit(width, height, score2, score1);
break;
case BestAreaFit:
newNode = this._findPositionForNewNodeBestAreaFit(width, height, score1, score2);
break;
}
// Cannot fit the current Rectangle.
if (newNode.height === 0) {
score1.value = Infinity;
score2.value = Infinity;
}
return newNode;
},
_occupancy: function () {
var usedRectangles = this.usedRectangles;
var usedSurfaceArea = 0;
for (var i = 0; i < usedRectangles.length; i++) {
usedSurfaceArea += usedRectangles[i].width * usedRectangles[i].height;
}
return usedSurfaceArea / (this.binWidth * this.binHeight);
},
_findPositionForNewNodeBottomLeft: function (width, height, bestY, bestX) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(bestNode, 0, sizeof(Rectangle));
bestY.value = Infinity;
var rect;
var topSideY;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
topSideY = rect.y + height;
if (topSideY < bestY.value || (topSideY == bestY.value && rect.x < bestX.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestY.value = topSideY;
bestX.value = rect.x;
}
}
if (this.allowRotate && rect.width >= height && rect.height >= width) {
topSideY = rect.y + width;
if (topSideY < bestY.value || (topSideY == bestY.value && rect.x < bestX.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestY.value = topSideY;
bestX.value = rect.x;
}
}
}
return bestNode;
},
_findPositionForNewNodeBestShortSideFit: function (width, height, bestShortSideFit, bestLongSideFit) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rectangle));
bestShortSideFit.value = Infinity;
var rect;
var leftoverHoriz;
var leftoverVert;
var shortSideFit;
var longSideFit;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
leftoverHoriz = Math.abs(rect.width - width);
leftoverVert = Math.abs(rect.height - height);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
longSideFit = Math.max(leftoverHoriz, leftoverVert);
if (shortSideFit < bestShortSideFit.value || (shortSideFit == bestShortSideFit.value && longSideFit < bestLongSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestShortSideFit.value = shortSideFit;
bestLongSideFit.value = longSideFit;
}
}
var flippedLeftoverHoriz;
var flippedLeftoverVert;
var flippedShortSideFit;
var flippedLongSideFit;
if (this.allowRotate && rect.width >= height && rect.height >= width) {
flippedLeftoverHoriz = Math.abs(rect.width - height);
flippedLeftoverVert = Math.abs(rect.height - width);
flippedShortSideFit = Math.min(flippedLeftoverHoriz, flippedLeftoverVert);
flippedLongSideFit = Math.max(flippedLeftoverHoriz, flippedLeftoverVert);
if (flippedShortSideFit < bestShortSideFit.value || (flippedShortSideFit == bestShortSideFit.value && flippedLongSideFit < bestLongSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestShortSideFit.value = flippedShortSideFit;
bestLongSideFit.value = flippedLongSideFit;
}
}
}
return bestNode;
},
_findPositionForNewNodeBestLongSideFit: function (width, height, bestShortSideFit, bestLongSideFit) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rectangle));
bestLongSideFit.value = Infinity;
var rect;
var leftoverHoriz;
var leftoverVert;
var shortSideFit;
var longSideFit;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
leftoverHoriz = Math.abs(rect.width - width);
leftoverVert = Math.abs(rect.height - height);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
longSideFit = Math.max(leftoverHoriz, leftoverVert);
if (longSideFit < bestLongSideFit.value || (longSideFit == bestLongSideFit.value && shortSideFit < bestShortSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestShortSideFit.value = shortSideFit;
bestLongSideFit.value = longSideFit;
}
}
if (this.allowRotate && rect.width >= height && rect.height >= width) {
leftoverHoriz = Math.abs(rect.width - height);
leftoverVert = Math.abs(rect.height - width);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
longSideFit = Math.max(leftoverHoriz, leftoverVert);
if (longSideFit < bestLongSideFit.value || (longSideFit == bestLongSideFit.value && shortSideFit < bestShortSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestShortSideFit.value = shortSideFit;
bestLongSideFit.value = longSideFit;
}
}
}
return bestNode;
},
_findPositionForNewNodeBestAreaFit: function (width, height, bestAreaFit, bestShortSideFit) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rectangle));
bestAreaFit.value = Infinity;
var rect;
var leftoverHoriz;
var leftoverVert;
var shortSideFit;
var areaFit;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
areaFit = rect.width * rect.height - width * height;
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
leftoverHoriz = Math.abs(rect.width - width);
leftoverVert = Math.abs(rect.height - height);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
if (areaFit < bestAreaFit.value || (areaFit == bestAreaFit.value && shortSideFit < bestShortSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestShortSideFit.value = shortSideFit;
bestAreaFit = areaFit;
}
}
if (this.allowRotate && rect.width >= height && rect.height >= width) {
leftoverHoriz = Math.abs(rect.width - height);
leftoverVert = Math.abs(rect.height - width);
shortSideFit = Math.min(leftoverHoriz, leftoverVert);
if (areaFit < bestAreaFit.value || (areaFit == bestAreaFit.value && shortSideFit < bestShortSideFit.value)) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestShortSideFit.value = shortSideFit;
bestAreaFit.value = areaFit;
}
}
}
return bestNode;
},
/// Returns 0 if the two intervals i1 and i2 are disjoint, or the length of their overlap otherwise.
_commonIntervalLength: function (i1start, i1end, i2start, i2end) {
if (i1end < i2start || i2end < i1start) {
return 0;
}
return Math.min(i1end, i2end) - Math.max(i1start, i2start);
},
_contactPointScoreNode: function (x, y, width, height) {
var usedRectangles = this.usedRectangles;
var score = 0;
if (x == 0 || x + width === this.binWidth)
score += height;
if (y == 0 || y + height === this.binHeight)
score += width;
var rect;
for (var i = 0; i < usedRectangles.length; i++) {
rect = usedRectangles[i];
if (rect.x == x + width || rect.x + rect.width == x)
score += this._commonIntervalLength(rect.y, rect.y + rect.height, y, y + height);
if (rect.y == y + height || rect.y + rect.height == y)
score += this._commonIntervalLength(rect.x, rect.x + rect.width, x, x + width);
}
return score;
},
_findPositionForNewNodeContactPoint: function (width, height, bestContactScore) {
var freeRectangles = this.freeRectangles;
var bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rectangle));
bestContactScore.value = -1;
var rect;
var score;
for (var i = 0; i < freeRectangles.length; i++) {
rect = freeRectangles[i];
// Try to place the Rectangle in upright (non-flipped) orientation.
if (rect.width >= width && rect.height >= height) {
score = this._contactPointScoreNode(rect.x, rect.y, width, height);
if (score > bestContactScore.value) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = width;
bestNode.height = height;
bestContactScore = score;
}
}
if (this.allowRotate && rect.width >= height && rect.height >= width) {
score = this._contactPointScoreNode(rect.x, rect.y, height, width);
if (score > bestContactScore.value) {
bestNode.x = rect.x;
bestNode.y = rect.y;
bestNode.width = height;
bestNode.height = width;
bestContactScore.value = score;
}
}
}
return bestNode;
},
_splitFreeNode: function (freeNode, usedNode) {
var freeRectangles = this.freeRectangles;
// Test with SAT if the Rectangles even intersect.
if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x ||
usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y)
return false;
var newNode;
if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x) {
// New node at the top side of the used node.
if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height) {
newNode = freeNode.clone();
newNode.height = usedNode.y - newNode.y;
freeRectangles.push(newNode);
}
// New node at the bottom side of the used node.
if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) {
newNode = freeNode.clone();
newNode.y = usedNode.y + usedNode.height;
newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height);
freeRectangles.push(newNode);
}
}
if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y) {
// New node at the left side of the used node.
if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width) {
newNode = freeNode.clone();
newNode.width = usedNode.x - newNode.x;
freeRectangles.push(newNode);
}
// New node at the right side of the used node.
if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) {
newNode = freeNode.clone();
newNode.x = usedNode.x + usedNode.width;
newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width);
freeRectangles.push(newNode);
}
}
return true;
},
_pruneFreeList: function () {
var freeRectangles = this.freeRectangles;
for (var i = 0; i < freeRectangles.length; i++)
for (var j = i + 1; j < freeRectangles.length; j++) {
if (Rect.isContainedIn(freeRectangles[i], freeRectangles[j])) {
freeRectangles.splice(i, 1);
break;
}
if (Rect.isContainedIn(freeRectangles[j], freeRectangles[i])) {
freeRectangles.splice(j, 1);
}
}
}
};
...@@ -47,26 +47,6 @@ export function strEllipsis(str, maxLength = 0, rightOffset = 0) { ...@@ -47,26 +47,6 @@ export function strEllipsis(str, maxLength = 0, rightOffset = 0) {
return result; return result;
} }
export function saveAs(blob, fileName) {
if ('msSaveOrOpenBlob' in navigator) {
navigator.msSaveOrOpenBlob(blob, fileName);
} else {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
setTimeout(() => {
URL.revokeObjectURL(url);
}, 500);
}
}
export function getInputDefaultValue(property) { export function getInputDefaultValue(property) {
return property ? property.hasOwnProperty('default') ? property.default + '' : 'unset' : 'unset'; return property ? property.hasOwnProperty('default') ? property.default + '' : 'unset' : 'unset';
} }
...@@ -101,6 +81,26 @@ export function clonePureObj(obj) { ...@@ -101,6 +81,26 @@ export function clonePureObj(obj) {
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
} }
export function saveAs(blob, fileName) {
if ('msSaveOrOpenBlob' in navigator) {
navigator.msSaveOrOpenBlob(blob, fileName);
} else {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
setTimeout(() => {
URL.revokeObjectURL(url);
}, 500);
}
}
export function readTextFile(file) { export function readTextFile(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const fileReader = new FileReader(); const fileReader = new FileReader();
......
/**
* Created by rockyl on 2019-12-08.
*/
import MaxRectsBinPack from "./MaxRectsBinPack";
const packExts = ['.png']; //, '.jpg', '.jpeg', '.bmp'
let canvas = document.createElement('canvas');
export async function packImages(asssts, options = {}) {
const padding = options.padding || 1;
const maxSize = options.maxSize || 2048;
const mode = options.mode || 0;
const images = await preProcessing(asssts);
let rects = [], singles = [];
for (let item of images) {
const {image, assets, image: {width, height}} = item;
if (width < maxSize && height < maxSize) {
const rectWidth = width + padding * 2;
const rectHeight = height + padding * 2;
rects.push({
image,
assets,
width: rectWidth,
height: rectHeight,
area: rectWidth * rectHeight,
sourceW: width,
sourceH: height,
offX: 0,
offY: 0,
})
} else {
asssts.push({
name: assets[0].name,
ext: assets[0].ext,
url: assets[0].url,
uuids: assets.map(asset => asset.uuid),
})
}
}
rects.sort((a, b) => {
return b.area - a.area;
});
let remainRects = rects.concat();
let index = 0;
while (remainRects.length > 0) {
let name = 'sheet' + index;
let pack = new MaxRectsBinPack(maxSize, maxSize, false);
let packedRects = pack.insertRects(remainRects, mode);
//document.body.append(canvas);
canvas.width = canvas.height = maxSize;
let context = canvas.getContext('2d');
//context.fillStyle = `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 1)`;
//context.strokeRect(0, 0, maxSize, maxSize);
for (let rect of packedRects) {
context.drawImage(rect.image, rect.x + padding, rect.y + padding);
}
let blob = await new Promise(resolve => {
canvas.toBlob(function (blob) {
resolve(blob);
}, 'image/png');
});
let frames = {};
let i = 0;
for (let rect of packedRects) {
let sprite = {
x: rect.x + padding,
y: rect.y + padding,
w: rect.width - padding * 2,
h: rect.height - padding * 2,
ox: rect.offX,
oy: rect.offY,
sw: rect.sourceW,
sh: rect.sourceH,
};
for (let asset of rect.assets) {
frames[asset.uuid] = sprite;
i++;
}
}
asssts.push({
ext: '.sht',
file: new File([blob], name + '.png'),
frames,
});
index++;
}
}
function loadImage(url, assets) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function () {
resolve({
image: img,
assets,
})
};
img.onerror = reject;
img.src = url;
})
}
async function preProcessing(assets) {
let targetAssets = [];
for (let i = 0, li = assets.length; i < li; i++) {
const asset = assets[i];
if (packExts.includes(asset.ext)) {
targetAssets.push(assets.splice(i, 1)[0]);
i--;
li--;
}
}
let groups = {};
for (let asset of targetAssets) {
let group = groups[asset.url];
if (!group) {
group = groups[asset.url] = [];
}
group.push(asset);
}
let ps = [];
for (let url in groups) {
ps.push(loadImage(url, groups[url]))
}
return await Promise.all(ps);
}
...@@ -72,8 +72,8 @@ ...@@ -72,8 +72,8 @@
destroyed() { destroyed() {
document.removeEventListener('keydown', this.onKeyPress) document.removeEventListener('keydown', this.onKeyPress)
}, },
created(){ created() {
events.$on('saveAndPreview',()=>{ events.$on('saveAndPreview', () => {
this.clickMenu("preview"); this.clickMenu("preview");
}); });
}, },
...@@ -87,7 +87,9 @@ ...@@ -87,7 +87,9 @@
onKeyPress(e) { onKeyPress(e) {
if (e.key === 's' && (e.ctrlKey || e.metaKey)) { if (e.key === 's' && (e.ctrlKey || e.metaKey)) {
e.preventDefault(); e.preventDefault();
this.clickMenu("preview");
this.saveProject();
return false; return false;
} }
}, },
...@@ -166,8 +168,11 @@ ...@@ -166,8 +168,11 @@
case 'preview': case 'preview':
await this.pack(true); await this.pack(true);
break; break;
case 'pack': case 'preview2':
await this.pack(); this.preview();
break;
case 'publish':
await this.publish();
break; break;
case 'undo': case 'undo':
this.$store.commit('undoRedo', 1); this.$store.commit('undoRedo', 1);
...@@ -197,13 +202,18 @@ ...@@ -197,13 +202,18 @@
break; break;
} }
}, },
async publish(){
this.pack();
},
async pack(debug = false) { async pack(debug = false) {
const loading = this.$loading({ const loading = this.$loading({
lock: true, lock: true,
text: this.$t('Packing'), text: this.$t('Packing'),
}); });
try { try {
if(!debug){
await this.saveProject(false); await this.saveProject(false);
}
const packResult = await this.packProject(debug); const packResult = await this.packProject(debug);
this.$message({ this.$message({
message: this.$t('Pack project successfully'), message: this.$t('Pack project successfully'),
...@@ -227,6 +237,7 @@ ...@@ -227,6 +237,7 @@
});*/ });*/
} catch (e) { } catch (e) {
console.log(e);
this.$message({ this.$message({
message: this.$t('Pack project failed'), message: this.$t('Pack project failed'),
type: 'error', type: 'error',
...@@ -238,6 +249,18 @@ ...@@ -238,6 +249,18 @@
backToHome() { backToHome() {
this.$router.replace({name: 'home'}); this.$router.replace({name: 'home'});
}, },
preview() {
this.savePreview();
const {projectID} = this.$route.params;
let previewUrl = new URL(location.href);
previewUrl.hash = '#/preview/' + projectID;
setTimeout(()=>{
window.open(previewUrl.href, 'blank');
}, 300);
},
...mapActions([ ...mapActions([
'localVersionExist', 'localVersionExist',
'loadFromLocal', 'loadFromLocal',
...@@ -246,6 +269,8 @@ ...@@ -246,6 +269,8 @@
"saveToRemote", "saveToRemote",
'updateEnv', 'updateEnv',
'packProject', 'packProject',
'savePreview',
'packImages',
]) ])
} }
} }
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
</template> </template>
<script> <script>
import {mapState, mapMutations, mapActions} from 'vuex' import {mapGetters, mapMutations, mapActions} from 'vuex'
import Pane from "../../components/Pane"; import Pane from "../../components/Pane";
import FileItem from "./Assets/FileItem"; import FileItem from "./Assets/FileItem";
import AssetsShow from "./Assets/AssetsShow"; import AssetsShow from "./Assets/AssetsShow";
...@@ -57,9 +57,7 @@ ...@@ -57,9 +57,7 @@
} }
}, },
computed: { computed: {
...mapState({ ...mapGetters(['assets']),
assets: state => state.project.data.assets
}),
}, },
mounted() { mounted() {
this.currentItem = null; this.currentItem = null;
......
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
<div class="file-item" @click="$emit('click', $event)"> <div class="file-item" @click="$emit('click', $event)">
<div class="icon"> <div class="icon">
<i v-if="!showThumbnail" draggable="true" class="file-icon" :class="fileIcon"></i> <i v-if="!showThumbnail" draggable="true" class="file-icon" :class="fileIcon"></i>
<img @dragstart="assetDragStart(data)" v-if="showThumbnail" draggable="true" class="thumbnail" :src="thumbnailUrl" alt="thumb" @dblclick="onDbclick()"> <img @dragstart="assetDragStart(data)" v-if="showThumbnail" draggable="true" class="thumbnail" :src="thumbnailUrl"
alt="thumb" @dblclick="onDbclick()">
<div class="operate-bar"> <div class="operate-bar">
<el-button circle size="mini" type="success" icon="el-icon-upload2" @dblclick.native.stop
@click="onClickReplace"/>
<el-button circle size="mini" type="success" icon="el-icon-edit" @dblclick.native.stop @click="onClickEdit"/> <el-button circle size="mini" type="success" icon="el-icon-edit" @dblclick.native.stop @click="onClickEdit"/>
<el-button circle size="mini" type="danger" icon="el-icon-delete" @dblclick.native.stop <el-button circle size="mini" type="danger" icon="el-icon-delete" @dblclick.native.stop
@click="onClickDelete"/> @click="onClickDelete"/>
...@@ -17,21 +20,17 @@ ...@@ -17,21 +20,17 @@
</template> </template>
<script> <script>
import {mapMutations} from "vuex"; import {mapMutations, mapActions} from "vuex";
import {fileTypeIcon} from "../../../config"; import {fileTypeIcon} from "../../../config";
import {strEllipsis} from "../../../utils"; import {selectFile, strEllipsis} from "../../../utils";
export default { export default {
name: "FileItem", name: "FileItem",
props: ['data'], props: ['data'],
data() { data() {
return { return {}
}
},
watch: {
}, },
watch: {},
computed: { computed: {
fileIcon() { fileIcon() {
let icon = fileTypeIcon[this.data.ext]; let icon = fileTypeIcon[this.data.ext];
...@@ -43,7 +42,7 @@ ...@@ -43,7 +42,7 @@
thumbnailUrl() { thumbnailUrl() {
return this.data.url; return this.data.url;
}, },
ellipsisName(){ ellipsisName() {
return strEllipsis(this.data.name, 14, 5); return strEllipsis(this.data.name, 14, 5);
}, },
}, },
...@@ -78,11 +77,23 @@ ...@@ -78,11 +77,23 @@
}).catch(() => { }).catch(() => {
}); });
}, },
onClickReplace() {
selectFile((files) => {
let file = files[0];
this.replaceAsset({
file: file,
uuid: this.data.uuid,
});
})
},
...mapMutations([ ...mapMutations([
'deleteAsset', 'deleteAsset',
'modifyAsset', 'modifyAsset',
'assetDragStart' 'assetDragStart',
]), ]),
...mapActions([
'replaceAsset'
])
} }
} }
</script> </script>
......
...@@ -10,7 +10,10 @@ ...@@ -10,7 +10,10 @@
:splitpanes-size="80"/> :splitpanes-size="80"/>
</split-panes> </split-panes>
<div class="center full-size background" splitpanes-min="20" :splitpanes-size="70"> <div class="center full-size background" splitpanes-min="20" :splitpanes-size="70">
<div class="top-bar">
<edit-path :processStack="processStack" @pop="onPop"/> <edit-path :processStack="processStack" @pop="onPop"/>
<el-button icon="el-icon-search" circle plain size="mini" @click="onClickSearch"></el-button>
</div>
<!--<div class="operate-bar"> <!--<div class="operate-bar">
<el-button-group> <el-button-group>
<el-button size="mini" icon="el-icon-zoom-out" @click="setScale(-0.1)"/> <el-button size="mini" icon="el-icon-zoom-out" @click="setScale(-0.1)"/>
...@@ -25,6 +28,7 @@ ...@@ -25,6 +28,7 @@
</div> </div>
</split-panes> </split-panes>
<meta-editor-dialog ref="metaEditorDialog" @input="onSaveMeta"/> <meta-editor-dialog ref="metaEditorDialog" @input="onSaveMeta"/>
<meta-search-dialog ref="metaSearchDialog"/>
</div> </div>
</template> </template>
...@@ -38,11 +42,12 @@ ...@@ -38,11 +42,12 @@
import Process from "./Board/Process"; import Process from "./Board/Process";
import MetaEditorDialog from "./MetaEditorDialog"; import MetaEditorDialog from "./MetaEditorDialog";
import events from "@/global-events.js" import events from "@/global-events.js"
import MetaSearchDialog from "./MetaSearchDialog";
export default { export default {
name: "BehaviorEditor", name: "BehaviorEditor",
components: {MetaEditorDialog, PropertiesEditor, EditPath, ProcessList, Board, SplitPanes,}, components: {MetaSearchDialog, MetaEditorDialog, PropertiesEditor, EditPath, ProcessList, Board, SplitPanes,},
props: [], props: [],
data() { data() {
return { return {
...@@ -99,6 +104,9 @@ ...@@ -99,6 +104,9 @@
this.$refs.board.edit(process, this.resolveProcess); this.$refs.board.edit(process, this.resolveProcess);
this.$refs.properties.edit(); this.$refs.properties.edit();
}, },
onClickSearch(){
this.$refs.metaSearchDialog.show();
},
onEditMeta(meta) { onEditMeta(meta) {
this.metaInEditing = meta; this.metaInEditing = meta;
this.$refs.metaEditorDialog.edit(meta); this.$refs.metaEditorDialog.edit(meta);
......
...@@ -115,7 +115,13 @@ ...@@ -115,7 +115,13 @@
}, },
addSubProcess(uuid, data) { addSubProcess(uuid, data) {
const process = new Process(this.process, data, this.resolveProcess); const process = new Process(this.process, data, this.resolveProcess);
if (process.meta) {
this.$set(this.subProcessMap, uuid, process); this.$set(this.subProcessMap, uuid, process);
} else {
console.log('节点丢失:', uuid);
}
}, },
showInlineChoose() { showInlineChoose() {
return this.$confirm(this.$t('As inline')); return this.$confirm(this.$t('As inline'));
...@@ -192,8 +198,8 @@ ...@@ -192,8 +198,8 @@
this.addProcessFromCopy(dataStr); this.addProcessFromCopy(dataStr);
} }
}, },
addProcessFromCopy(avatar){ addProcessFromCopy(avatar) {
if(typeof avatar === 'string'){ if (typeof avatar === 'string') {
avatar = JSON.parse(avatar); avatar = JSON.parse(avatar);
} }
...@@ -249,6 +255,17 @@ ...@@ -249,6 +255,17 @@
outputIndex, outputIndex,
}); });
this.drawState.lineID++; this.drawState.lineID++;
} else {
for (let uuid in this.subProcessMap) {
const process = this.subProcessMap[uuid];
for (let key in process.data.output) {
let item = process.data.output[key];
let index = item.indexOf(outputID);
if(index >= 0){
item.splice(index, 1);
}
}
}
} }
}, },
onPinHover(x, y, pin) { onPinHover(x, y, pin) {
...@@ -299,6 +316,14 @@ ...@@ -299,6 +316,14 @@
this.$delete(this.subProcessMap, process.uuid); this.$delete(this.subProcessMap, process.uuid);
this.$delete(this.process.meta.sub, process.uuid); this.$delete(this.process.meta.sub, process.uuid);
let remain = 0;
for(let key in this.process.meta.sub){
let p = this.process.meta.sub[key];
if (p.meta === meta.id) {
remain ++;
}
}
for (let id in this.lines) { for (let id in this.lines) {
const line = this.lines[id]; const line = this.lines[id];
const {prev, next} = line; const {prev, next} = line;
...@@ -309,18 +334,20 @@ ...@@ -309,18 +334,20 @@
} }
if (meta.isDivider || meta.isInline) { //如果是分流节点或者内联节点还要删除对应的meta if (meta.isDivider || meta.isInline) { //如果是分流节点或者内联节点还要删除对应的meta
if(remain === 0){
this.deleteProcessMeta({ this.deleteProcessMeta({
meta, meta,
process: this.process, process: this.process,
}); });
} }
}
}, },
onProcessNodeCopy(data, meta) { onProcessNodeCopy(data, meta) {
if(meta.isInline){ //内联直接复制 if (meta.isInline) { //内联直接复制
let avatar = clonePureObj(data); let avatar = clonePureObj(data);
this.addProcessFromCopy(avatar); this.addProcessFromCopy(avatar);
}else{ } else {
copy(JSON.stringify(data), {format: 'process-data'}); copy(JSON.stringify(data), {format: 'process-data'});
this.$message({ this.$message({
message: this.$t('Copied process to clipboard'), message: this.$t('Copied process to clipboard'),
......
<template>
<el-dialog :title="$t('Meta Search')" width="80%" :visible.sync="visible"
:append-to-body="true"
custom-class="behavior-editor-dialog"
>
<div style="padding: 10px;">
<el-input size="mini" v-model="keyword">
<el-button slot="append" icon="el-icon-search" @click="search"></el-button>
</el-input>
<div style="height: 50vh;">
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button size="mini" plain @click="close">{{$t('Close')}}</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: "MetaSearchDialog",
data() {
return {
visible: false,
keyword: '',
}
},
methods:{
show(){
this.visible = true;
},
close(){
this.visible = false;
},
search(){
},
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
</el-input> </el-input>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" <el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view"> view-class="scrollbar-view">
<el-form v-model="process" size="mini" label-width="150px" label-position="left" @submit.native.prevent> <el-form v-model="process" size="mini" label-width="100px" label-position="left" @submit.native.prevent>
<component v-for="(property, key) in process.meta.props" <component v-for="(property, key) in process.meta.props"
:is="getInput(property)" :is="getInput(property)"
v-model="process.data.props[key]" v-model="process.data.props[key]"
......
<template> <template>
<div class="home"> <div class="home">
<p class="editor-name" style="font-weight: bold">{{env.name}}<span <div class="editor-name" style="font-weight: bold">{{env.name}}
class="editor-version">{{env.version}}</span></p> <span class="editor-version">{{env.version}}</span>
</div>
<main> <main>
<div class="project-list"> <div class="project-list">
<el-table <el-table
...@@ -33,6 +34,11 @@ ...@@ -33,6 +34,11 @@
type="success" icon="el-icon-document-copy" type="success" icon="el-icon-document-copy"
size="small" circle plain> size="small" circle plain>
</el-button> </el-button>
<el-button
@click.native.prevent="exportProject(scope.row)"
type="warning" icon="el-icon-receiving"
size="small" circle plain>
</el-button>
<!--<el-button <!--<el-button
@click.native.prevent="onDeleteProject(scope.row)" @click.native.prevent="onDeleteProject(scope.row)"
type="danger" icon="el-icon-delete" type="danger" icon="el-icon-delete"
...@@ -42,8 +48,18 @@ ...@@ -42,8 +48,18 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<div class="bottom-bar">
<div> <div>
<el-button @click="showCreateProjectDialog()">{{$t('Create')}}</el-button> <el-button type="primary" @click="showCreateProjectDialog()">{{$t('Create')}}</el-button>
<el-button @click="importProject()">{{$t('Import')}}</el-button>
</div>
<el-pagination
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-size="pageSize"
layout="prev, pager, next, jumper"
:total="env.projectCount">
</el-pagination>
</div> </div>
<create-project-dialog ref="createProjectDialog" @success="onCreateProject"/> <create-project-dialog ref="createProjectDialog" @success="onCreateProject"/>
<duplicate-project-dialog ref="duplicateProjectDialog" @success="onDuplicateProject"/> <duplicate-project-dialog ref="duplicateProjectDialog" @success="onDuplicateProject"/>
...@@ -54,9 +70,10 @@ ...@@ -54,9 +70,10 @@
<script> <script>
import {mapState, mapActions} from 'vuex' import {mapState, mapActions} from 'vuex'
import CreateProjectDialog from "./Home/CreateProjectDialog"; import CreateProjectDialog from "./Home/CreateProjectDialog";
import {playWaiting} from "../utils"; import {playWaiting, readTextFile, saveAs, selectFile} from "../utils";
import moment from "moment"; import moment from "moment";
import DuplicateProjectDialog from "./Home/DuplicateProjectDialog"; import DuplicateProjectDialog from "./Home/DuplicateProjectDialog";
import {PAGE_SIZE} from "../config";
export default { export default {
name: "Home", name: "Home",
...@@ -64,10 +81,13 @@ ...@@ -64,10 +81,13 @@
data() { data() {
return { return {
appVersion: 'v1.0.0', appVersion: 'v1.0.0',
currentPage: 1,
pageSize: PAGE_SIZE,
} }
}, },
mounted() { mounted() {
playWaiting(this.prepare(), this.$t('Preparing')).catch(e => {}); playWaiting(this.prepare(), this.$t('Preparing')).catch(e => {
});
}, },
computed: { computed: {
...mapState([ ...mapState([
...@@ -76,10 +96,10 @@ ...@@ -76,10 +96,10 @@
]), ]),
}, },
methods: { methods: {
prepare(){ prepare() {
return Promise.all([ return Promise.all([
this.updateEnv(), this.updateEnv(),
this.updateProjects(), this.handleCurrentChange(1),
]) ])
}, },
moment(time) { moment(time) {
...@@ -88,6 +108,21 @@ ...@@ -88,6 +108,21 @@
showCreateProjectDialog() { showCreateProjectDialog() {
this.$refs.createProjectDialog.show(); this.$refs.createProjectDialog.show();
}, },
importProject() {
selectFile((files) => {
readTextFile(files[0])
.then(
txt => {
let data = JSON.parse(txt);
return playWaiting(this.createProject(data), this.$t('Importing project'));
}
)
});
},
async exportProject({id}) {
const project = await playWaiting(this.fetchProject(id), this.$t('Exporting project'));
saveAs(new Blob([JSON.stringify(project)]), project.name + '.json');
},
showDuplicateProjectDialog(project) { showDuplicateProjectDialog(project) {
this.$refs.duplicateProjectDialog.show(project); this.$refs.duplicateProjectDialog.show(project);
}, },
...@@ -100,10 +135,12 @@ ...@@ -100,10 +135,12 @@
cancelButtonText: this.$t('Cancel'), cancelButtonText: this.$t('Cancel'),
type: 'warning' type: 'warning'
}).then( }).then(
()=>{ () => {
playWaiting(this.deleteProject(project.id), this.$t('Deleting')).catch(e => {}); playWaiting(this.deleteProject(project.id), this.$t('Deleting')).catch(e => {
});
}, },
()=>{} () => {
}
) )
}, },
onCreateProject(projectID) { onCreateProject(projectID) {
...@@ -115,8 +152,18 @@ ...@@ -115,8 +152,18 @@
editProject(projectID) { editProject(projectID) {
this.$router.push({name: 'editor', params: {projectID}}); this.$router.push({name: 'editor', params: {projectID}});
}, },
async handleCurrentChange(page) {
const loading = this.$loading({
lock: true,
text: this.$t('In processing'),
});
await this.fetchProjects({currentPage: page, pageSize: this.pageSize});
loading.close();
},
...mapActions([ ...mapActions([
'updateProjects', 'fetchProjects',
'fetchProject',
'createProject',
'deleteProject', 'deleteProject',
'updateEnv', 'updateEnv',
]), ]),
......
<template>
<iframe ref="iframe" class="player-wrapper">
</iframe>
</template>
<script>
export default {
name: "Preview",
data() {
return {
ts: '',
}
},
mounted() {
if(!this.ts){
this.ts = localStorage.getItem('preview-ts');
}
document.addEventListener("visibilitychange", this.onVisibilityChange);
setTimeout(()=>{
this.buildPage();
}, 100);
},
destroyed(){
document.removeEventListener("visibilitychange", this.onVisibilityChange);
},
methods: {
onVisibilityChange(e){
if(!document.hidden){
let ts = localStorage.getItem('preview-ts');
if(this.ts !== ts){
this.$refs.iframe.contentDocument.location.reload();
document.location.reload();
this.ts = ts;
}
}
},
buildPage() {
const {projectID} = this.$route.params;
const storeKey = 'preview-project-' + projectID;
let data = localStorage.getItem(storeKey);
let dataObj = JSON.parse(data);
let {options: {tpl, pageTitle, containerId}} = dataObj.data;
const launchCode = `engine.launchWithLocalStorage("${projectID}");`;
tpl = tpl
.replace('$PAGE_TITLE$', pageTitle)
.replace('$CONTAINER_ID$', containerId)
.replace('$SCRIPTS$', '')
.replace('engine.launch(\'//yun.duiba.com.cn/aurora/$VERSION$-data.json\');', launchCode);
const doc = this.$refs.iframe.contentDocument;
doc.write(tpl);
},
}
}
</script>
<style scoped>
.player-wrapper{
border: 0;
width: 100%;
height: 100%;
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment