// ==UserScript==
// @name GitLab Viewer Publish and Deploy Project
// @namespace http://tampermonkey.net/
// @version 0.1
// @description GitLab Viewer Publish and Deploy Project!
// @author Sean
// @match http://192.168.0.200/fe3project/*
// @icon http://192.168.0.200/assets/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.png
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js
// @require https://unpkg.com/element-ui/lib/index.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js
// @resource myFontFile https://element.eleme.io/2.11/static/element-icons.535877f.woff
// @grant GM_getResourceURL
// @grant GM_addStyle
// @grant GM_getResourceText
// @resource ElementCSS https://unpkg.com/element-ui/lib/theme-chalk/index.css
// @grant GM_xmlhttpRequest
// @license MIT
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
const fontUrl = 'https://element.eleme.io/2.11/static/element-icons.535877f.woff';
// 添加样式规则,将字体应用到指定元素上
GM_addStyle(`
@font-face {
font-family: element-icons;
src: url(${fontUrl}) format("woff");
}
`);
GM_addStyle(GM_getResourceText('ElementCSS'));
let epointCss = ".epoint-tool {position: fixed; bottom: 0%; right:10px; transform: translateY(-50%);}";
epointCss += ".el-row { padding: 3px 0;} .el-dialog__body .el-tree{min-height: 420px; max-height: 500px;overflow: auto;}";
epointCss += ".deploy-body {height: 306px;}"
epointCss += ".deploy-body .el-loading-spinner {margin-top: -50px;}";
// 添加注入样式
let extraStyleElement = document.createElement("style");
extraStyleElement.innerHTML = epointCss;
document.head.appendChild(extraStyleElement);
const MyComponent = {
template: `<div class="epoint-wrap">
<div class="epoint-tool">
<el-row><el-button type="primary" icon="el-icon-search" round @click="viewProject">查看</el-button></el-row>
<el-row>
<el-tooltip content="一键部署到170服务器" placement="top" effect="light">
<el-button type="primary" icon="el-icon-s-unfold" round @click="doDeploy">部署</el-button>
</el-tooltip>
</el-row>
<el-row>
<el-tooltip content="一键发布到项目案例库" placement="top" effect="light">
<el-button type="primary" icon="el-icon-upload" round @click="publish">发布</el-button>
</el-tooltip>
</el-row>
</div>
<el-dialog
title="目录结构"
width="900px"
:append-to-body="true"
:visible.sync="dialogVisible"
:before-close="handleClose">
<el-tree
class="filter-tree"
:data="data"
:props="defaultProps"
node-key="id"
default-expand-all
@node-click="handleNodeClick"
v-loading="loadingTree"
ref="tree">
</el-tree>
</el-dialog>
<el-dialog
title="部署"
width="420px"
:visible.sync="depDialogVisible">
<div class="deploy-body"
v-loading="loading"
element-loading-text="正在打包部署至 170 服务器,请耐心等待"
element-loading-spinner="el-icon-loading">
</div>
</el-dialog>
</div>`,
data() {
return {
dialogVisible: false, // 查看目录结构弹窗
data: [], // 目录结构树的数据结构
defaultProps: {
children: 'children',
label: 'label'
},
loadingTree: false,
depDialogVisible: false, // 部署中弹窗
loading: false,
projectLibUrl: 'http://192.168.201.159:9999/webapp/pages/default/onlinecase.html', // 项目案例库地址
projectIsDeployed: false, // 项目是否部署过
};
},
methods: {
handleClose(done) {
done();
/*
this.$confirm('确认关闭?')
.then(_ => {
done();
})
.catch(_ => {});*/
},
handleNodeClick(data) {
console.log(data);
},
// 部署
doDeploy () {
this.$confirm('此操作将把 GitLab 资源打包部署至 170 服务器,已部署过的项目会进行覆盖部署,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.depDialogVisible = true;
this.loading = true;
// 部署
this.getDeployInfo({ type: '1' }, (data)=> {
this.loading = false;
this.depDialogVisible = false;
this.$message({
type: 'success',
message: '部署成功!'
});
// this.data = data;
// 打开查看弹窗
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消部署'
});
});
},
// 发布项目案例库
publish () {
this.$confirm('此操作将把项目发布至 <a href="'+ this.projectLibUrl +'" target="_blank">项目案例库</a>,已发布过的项目案例库会有重复项,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
dangerouslyUseHTMLString: true,
type: 'warning'
}).then(() => {
const themes = document.querySelectorAll('.badge-secondary');
let keys = [];
for(let i = 0, l = themes.length; i < l; i++) {
if(themes[0].innerText == '智能设备' && i > 0) {
keys.push(themes[i].innerText);
} else if (themes[0].innerText !== '智能设备' && i > 1) {
keys.push(themes[i].innerText);
}
}
// 组织项目案例库所需参数
const projectName = document.querySelector('.home-panel-title').innerText;
const projectBU = themes[0] ? themes[0].innerText : null;
const projectType = themes[1] ? themes[0].innerText : null;
const projectKeys = keys.join(' ');
const entryUrl = 'http://test.html';
this.$message({
type: 'success',
message: this.projectLibUrl + '?projectName=' + projectName + '&projectBU=' + projectBU + '&projectType=' + projectType + '&projectKeys=' + projectKeys + '&entryUrl=' + entryUrl
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消发布'
});
});
},
// 查看项目
viewProject () {
this.dialogVisible = true;
this.loadingTree = true;
let self = this;
// 发送ajax请求,查看是否进行过部署
this.getDeployInfo((data)=> {
// 有部署信息,直接赋值,
if(true) {
self.projectIsDeployed = true;
// self.data = data;
} else {
// 无部署信息,仅查看文件目录
getZipResource((data)=> {
self.data = data;
self.loadingTree = false;
});
}
});
},
// 项目部署信息
getDeployInfo(params, callback) {
const projectId = document.body.getAttribute('data-project-id');
const downloadUrl = window.location.origin + document.querySelector('.gl-button.btn-sm.btn-confirm').getAttribute('href');
if(typeof params == 'function') {
callback = params;
params = null;
}
if(projectId && projectId.length && downloadUrl) {
GM_xmlhttpRequest({
method: 'POST',
url: '',
data: JSON.stringify({
projectId: projectId,
downloadUrl: downloadUrl,
type: (params && params.type !== undefined) ? params.type : '0'// 0 代表查看, 1代表部署
}),
onload: function (res) {
var data = JSON.parse(res.response).data;
callback && callback(data);
}
});
} else {
console.error('部署信息请求参数error');
}
}
},
mounted() {
}
};
const placeholder = document.createElement('div');
// 创建 Vue 实例并挂载到页面
const vueInstance = new Vue({
el: placeholder,
components: {
MyComponent
},
methods: {
},
template: `<my-component></my-component>`
});
// 等待页面加载完成
window.addEventListener('load', function() {
// 将占位元素追加到 body 元素中
document.body.appendChild(vueInstance.$el);
});
// 将文件条目组织成嵌套结构
function organizeFileEntries(fileEntries) {
const root = {
label: document.querySelector('.home-panel-title').innerText || document.getElementById('project_name_edit').value,
type: 'folder',
children: []
};
// 创建嵌套结构
fileEntries.forEach(entry => {
const pathSegments = entry.name.split('/');
let currentFolder = root;
// 遍历路径中的每个部分,创建相应的文件夹节点
for (let i = 0; i < pathSegments.length - 1; i++) {
const folderName = pathSegments[i];
let folder = currentFolder.children.find(child => child.label === folderName);
if(isExcludeFolder(entry.name)) {
continue;
}
if (!folder) {
folder = {
label: folderName,
type: 'folder',
children: []
};
currentFolder.children.push(folder);
}
currentFolder = folder;
}
// 创建文件节点并添加到相应的文件夹中
const fileName = pathSegments[pathSegments.length - 1];
if(fileName && fileName.length && isIncludeFile(fileName) && !isExcludeFolder(entry.name)) {
const fileNode = {
label: fileName,
type: 'file',
entry: entry.name
};
currentFolder.children.push(fileNode);
}
});
return [root];
}
// 是否排除的文件夹
function isExcludeFolder(entry) {
if(entry.indexOf('css') > -1 ||
entry.indexOf('scss') > -1 ||
entry.indexOf('js') > -1 ||
entry.indexOf('images') > -1 ||
entry.indexOf('fui') > -1 ||
entry.indexOf('lib') > -1 ||
entry.indexOf('test') > -1 ||
entry.indexOf('font') > -1 ||
entry.indexOf('frame/fui') > -1) {
return true;
} else {
return false;
}
}
// 是否包含的文件
function isIncludeFile(fileName) {
if(fileName.indexOf('.html') > -1) {
return true;
} else {
return false;
}
}
function getZipResource(callback) {
const downloadUrl = window.location.origin + document.querySelector('.gl-button.btn-sm.btn-confirm').getAttribute('href');
fetch(downloadUrl)
.then(response => response.arrayBuffer())
.then(data => {
// 将 ZIP 文件的二进制数据传递给 JSZip 进行解析
return JSZip.loadAsync(data);
})
.then(zip => {
// 获取 ZIP 文件中的所有条目(文件和目录)
const zipEntries = Object.values(zip.files);
const treeData = organizeFileEntries(zipEntries)
callback && callback(treeData);
})
.catch(error => {
console.error(error);
});
}
})();