GitLab Assistant

GitLab Viewer Publish and Deploy Project!

当前为 2023-06-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name GitLab Assistant
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.093
  5. // @description GitLab Viewer Publish and Deploy Project!
  6. // @author Sean
  7. // @match http://192.168.0.200*
  8. // @match http://192.168.0.200/*
  9. // @match http://192.168.0.200/fe3project/*
  10. // @match http://192.168.0.200/frontend_pc/project/*
  11. // @match https://oa.epoint.com.cn/interaction-design-portal/portal/pages/casestemplates/casetemplatesdetail*
  12. // @match http://192.168.201.159:9999/webapp/pages/default/onlinecase.html*
  13. // @match http://192.168.118.60:9999/webapp/pages/caselib/create.html*
  14. // @icon http://192.168.0.200/assets/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.png
  15. // @require https://cdn.bootcdn.net/ajax/libs/vue/2.7.14/vue.min.js
  16. // @require https://unpkg.com/element-ui/lib/index.js
  17. // @require https://cdn.bootcdn.net/ajax/libs/jszip/3.7.1/jszip.min.js
  18. // @grant GM_getResourceURL
  19. // @grant GM_addStyle
  20. // @grant GM_getResourceText
  21. // @resource ElementCSS https://unpkg.com/element-ui/lib/theme-chalk/index.css
  22. // @grant GM_xmlhttpRequest
  23. // @license MIT
  24. // @run-at document-end
  25. // ==/UserScript==
  26.  
  27.  
  28. // 个性化 gitlab 样式,
  29. // 满足项目详情多行显示,
  30. // 项目列表中的链接新窗口打开,
  31. // 搜索框 placeholder 个性化提示
  32. // 增加CodePipeline 入口等
  33. (function() {
  34. 'use strict';
  35.  
  36. let regs = [/^http:\/\/192\.168\.0\.200\/fe3project\//,
  37. /^http:\/\/192\.168\.0\.200\/frontend_pc\/project\//,
  38. /^http:\/\/192\.168\.0\.200/,
  39. /^http:\/\/192\.168\.0\.200\//];
  40. let match = false;
  41.  
  42. for(let r = 0, lr = regs.length; r < lr; r++) {
  43. if(regs[r].test(location.href)) {
  44. match = true;
  45. break;
  46. }
  47. }
  48.  
  49. if(!match) {
  50. return;
  51. }
  52.  
  53. // 注入样式:改变容器宽度,项目描述多行展示
  54. let injectStyle = ".group-list-tree .group-row-contents .description p { white-space: normal; } .container-limited.limit-container-width { max-width: 1400px } .limit-container-width .info-well {max-width: none;}";
  55.  
  56. injectStyle += ".container-fluid.container-limited.limit-container-width .file-holder.readme-holder.limited-width-container .file-content {max-width: none;}"
  57. injectStyle += 'button:focus {outline-color: transparent !important;}'
  58. // 添加注入样式
  59. let extraStyleElement = document.createElement("style");
  60. extraStyleElement.innerHTML = injectStyle;
  61. document.head.appendChild(extraStyleElement);
  62.  
  63. const fontUrl = 'https://element.eleme.io/2.11/static/element-icons.535877f.woff';
  64.  
  65. // 添加样式规则,将字体应用到指定元素上
  66. GM_addStyle(`
  67. @font-face {
  68. font-family: element-icons;
  69. src: url(${fontUrl}) format("woff");
  70. }
  71. `);
  72.  
  73. GM_addStyle(GM_getResourceText('ElementCSS'));
  74.  
  75. // 改变列表打开链接方式,改为新窗口打开
  76. let change = false;
  77. let tryTimes = 3;
  78.  
  79. function changeOpenType() {
  80. if(!change){
  81. setTimeout(()=> {
  82. let links = document.querySelectorAll('.description a');
  83. if(links.length) {
  84. for(let i = 0, l = links.length; i < l; i++) {
  85. links[i].target = "_blank";
  86. if(i === l - 1) {
  87. change = true;
  88. }
  89. }
  90. } else {
  91. changeOpenType();
  92. }
  93. }, 1000);
  94. }
  95. }
  96.  
  97. function stopLinkProp() {
  98. setTimeout(()=> {
  99. const links = document.querySelectorAll('.description a');
  100. for(let i = 0, l = links.length; i < l; i++) {
  101. links[i].addEventListener('click', ()=> {
  102. event.stopPropagation();
  103. });
  104. }
  105. }, 1000);
  106. }
  107.  
  108. // 等待页面加载完成
  109. window.addEventListener('load', function() {
  110.  
  111. var targetDiv = document.querySelector('section');
  112.  
  113. if(targetDiv) {
  114. // 创建一个 Mutation Observer 实例
  115. var observer = new MutationObserver(function(mutations) {
  116. // 在这里处理 div 子元素的变化
  117. mutations.forEach(function(mutation) {
  118. if(mutation.addedNodes && mutation.addedNodes.length) {
  119. change = false;
  120. changeOpenType();
  121.  
  122. stopLinkProp();
  123. }
  124. });
  125. });
  126.  
  127. // 配置 Mutation Observer
  128. var config = { childList: true, subtree: true };
  129.  
  130. // 开始观察目标 div 元素
  131. observer.observe(targetDiv, config);
  132. }
  133.  
  134. const placeholder = document.createElement('div');
  135.  
  136. // 创建 Vue 实例并挂载到页面
  137. const vueInstance = new Vue({
  138. el: placeholder,
  139. methods: {
  140. // 进入管理平台 code pipeline
  141. manage() {
  142. window.open('http://192.168.219.170/code-pipeline')
  143. }
  144. },
  145. template: `<div id="my-ext" style="margin-top:4px;">
  146. <el-tooltip content="进入 Code Pipeline 管理平台" placement="top" effect="light">
  147. <el-button type="primary" icon="el-icon-attract" size="small" circle @click="manage"></el-button>
  148. </el-tooltip>
  149. </div>`
  150. });
  151.  
  152. // 将占位元素追加到 body 元素中
  153. document.querySelector('.title-container').appendChild(vueInstance.$el);
  154.  
  155. // 修改 placehodler
  156. const listInput = document.getElementById('group-filter-form-field');
  157. const listInput2 = document.getElementById('project-filter-form-field');
  158.  
  159. if(listInput) {
  160. listInput.setAttribute("placeholder", "按项目名称、日期、开发者搜索,关键字≥3");
  161. listInput.style.width = '305px';
  162. }
  163. if(listInput2) {
  164. listInput2.setAttribute("placeholder", "按项目名称、日期、开发者搜索,关键字≥3");
  165. listInput2.style.width = '305px';
  166. }
  167. });
  168.  
  169.  
  170. })();
  171.  
  172. // GitLab Viewer Publish and Deploy Project
  173. // 查看项目、部署项目、发布项目功能
  174. (function() {
  175. 'use strict';
  176. let regs = [/^http:\/\/192\.168\.0\.200\/fe3project\//,
  177. /^http:\/\/192\.168\.0\.200\/frontend_pc\/project\//];
  178. let match = false;
  179.  
  180. for(let r = 0, lr = regs.length; r < lr; r++) {
  181. if(regs[r].test(location.href)) {
  182. match = true;
  183. break;
  184. }
  185. }
  186.  
  187. if(!match) {
  188. return;
  189. }
  190. /*
  191. const fontUrl = 'https://element.eleme.io/2.11/static/element-icons.535877f.woff';
  192.  
  193. // 添加样式规则,将字体应用到指定元素上
  194. GM_addStyle(`
  195. @font-face {
  196. font-family: element-icons;
  197. src: url(${fontUrl}) format("woff");
  198. }
  199. `);
  200.  
  201. GM_addStyle(GM_getResourceText('ElementCSS'));
  202. */
  203.  
  204. let epointCss = ".epoint-tool {position: fixed; bottom: 0%; right:10px; transform: translateY(-40%);}";
  205. epointCss += ".el-row { padding: 3px 0;} .el-dialog__body .el-tree{min-height: 420px; max-height: 500px;overflow: auto;}";
  206. epointCss += ".deploy-body {height: 306px;}"
  207. epointCss += ".el-loading-spinner {margin-top: -60px;} .el-button--primary:focus {outline: 0 !important;}";
  208. // 添加注入样式
  209. let extraStyleElement = document.createElement("style");
  210. extraStyleElement.innerHTML = epointCss;
  211. document.head.appendChild(extraStyleElement);
  212.  
  213. const MyComponent = {
  214. template: `<div class="epoint-wrap">
  215. <div class="epoint-tool">
  216. <el-row><el-button type="primary" icon="el-icon-search" round @click="viewProject">查看</el-button></el-row>
  217. <el-row>
  218. <el-tooltip content="一键部署到170服务器" placement="top" effect="light">
  219. <el-button type="primary" icon="el-icon-s-unfold" round @click="doDeploy">部署</el-button>
  220. </el-tooltip>
  221. </el-row>
  222. <el-row>
  223. <el-tooltip content="一键发布到项目案例库" placement="top" effect="light">
  224. <el-button type="primary" icon="el-icon-upload" round @click="publish">发布</el-button>
  225. </el-tooltip>
  226. </el-row>
  227. <el-row>
  228. <el-tooltip content="进入 Code Pipeline 管理平台进行更多操作" placement="top" effect="light">
  229. <el-button type="primary" icon="el-icon-attract" round @click="manage">管理</el-button>
  230. </el-tooltip>
  231. </el-row>
  232. </div>
  233. <el-dialog
  234. title="目录结构"
  235. width="900px"
  236. :append-to-body="true"
  237. :visible.sync="dialogVisible"
  238. :before-close="handleClose">
  239. <el-tree
  240. class="filter-tree"
  241. :data="data"
  242. :props="defaultProps"
  243. node-key="id"
  244. default-expand-all
  245. @node-click="handleNodeClick"
  246. v-loading="loadingTree"
  247. element-loading-background="rgba(255, 255, 255, 1)"
  248. element-loading-text="拼命加载中......"
  249. ref="tree">
  250. </el-tree>
  251. </el-dialog>
  252. <el-dialog
  253. title="部署"
  254. width="420px"
  255. :visible.sync="depDialogVisible">
  256. <div class="deploy-body"
  257. v-loading="loading"
  258. element-loading-text="正在打包部署至 170 服务器,请耐心等待"
  259. element-loading-spinner="el-icon-loading">
  260. </div>
  261. </el-dialog>
  262. <el-dialog title="部署提示" width="640px" :visible.sync="dialogDeployVisible">
  263. <el-alert
  264. title="此操作将把 GitLab 资源打包部署至 170 服务器,已部署过的项目会进行覆盖部署,是否继续?"
  265. type="warning" :closable="false" style="margin-bottom: 20px;">
  266. </el-alert>
  267. <el-form :model="form" ref="form" :rules="rules">
  268. <el-form-item label="框架类型" :label-width="formLabelWidth" prop="frame">
  269. <el-select v-model="form.frame" placeholder="请框架类型">
  270. <el-option v-for="item in framework" :key="item.value" :label="item.label" :value="item.value"></el-option>
  271. </el-select>
  272. </el-form-item>
  273. </el-form>
  274. <div slot="footer" class="dialog-footer">
  275. <el-button @click="dialogDeployVisible = false">取 消</el-button>
  276. <el-button type="primary" @click="doDeploy">确 定</el-button>
  277. </div>
  278. </el-dialog>
  279. </div>`,
  280. data() {
  281. return {
  282. dialogVisible: false, // 查看目录结构弹窗
  283. data: [], // 目录结构树的数据结构
  284. defaultProps: {
  285. children: 'children',
  286. label: 'label'
  287. },
  288. loadingTree: false,
  289. depDialogVisible: false, // 部署中弹窗
  290. loading: false,
  291. projectLibUrl: 'http://192.168.118.60:9999/webapp/pages/caselib/create.html', // 项目案例库地址
  292. projectIsDeployed: false, // 项目是否部署过
  293. projectFtpUrl: '', // ftp路径
  294. projectEntryUrl: '', // 项目案例库发布表单的入口页面
  295. supportDeploy: true, // 项目是否支持部署
  296. dialogDeployVisible: false,
  297. formLabelWidth: '120px',
  298. form: {
  299. frame: ''
  300. },
  301. framework: [
  302. {label: '重构模板', value: 1},
  303. {label: 'f9x1.0', value: 2},
  304. {label: 'f9x2.0', value: 3},
  305. {label: 'f950', value: 4},
  306. {label: 'f950sp1', value: 5},
  307. {label: 'f950sp2', value: 6},
  308. {label: 'f950sp3', value: 7},
  309. {label: 'f951', value: 18},
  310. {label: 'f940', value: 8},
  311. {label: 'f941', value: 10},
  312. {label: 'f942', value: 9},
  313. {label: 'f934', value: 11},
  314. {label: 'f933', value: 12},
  315. {label: 'f932', value: 13},
  316. {label: 'f9211', value: 14},
  317. {label: '骨架', value: 15},
  318. {label: 'Vue', value: 16},
  319. {label: 'React', value: 17}
  320. ],
  321. rules: {
  322. frame: [
  323. { required: true, message: '请选择框架类型', trigger: 'change' }
  324. ]
  325. },
  326. clickNodeEntry: null
  327. };
  328. },
  329. methods: {
  330. handleClose(done) {
  331. done();
  332. /*
  333. this.$confirm('确认关闭?')
  334. .then(_ => {
  335. done();
  336. })
  337. .catch(_ => {});*/
  338. },
  339. handleNodeClick(data) {
  340. console.log(data);
  341. if(data.type === 'folder') {
  342. return false;
  343. }
  344. var self = this;
  345. var entry = data.entry;
  346. self.clickNodeEntry = entry;
  347. if(self.projectFtpUrl) {
  348. window.open('http://192.168.219.170' + self.projectFtpUrl + data.entry);
  349. self.clickNodeEntry = null;
  350. } else {
  351. if(this.supportDeploy === false) {
  352. this.$message({
  353. type: 'error',
  354. message: '当前项目暂时只支持查看,请耐心等待功能升级。'
  355. });
  356. self.clickNodeEntry = null;
  357. return false;
  358. }
  359.  
  360. this.$confirm('资源未部署,部署至 170 服务器后可查看,是否部署?')
  361. .then(_ => {
  362.  
  363. this.dialogDeployVisible = true;
  364. /*
  365. this.depDialogVisible = true;
  366. this.loading = true;
  367. // 部署
  368. this.getDeployInfo({ type: '1' }, (data)=> {
  369. this.loading = false;
  370. this.depDialogVisible = false;
  371.  
  372. this.data = data.custom.detail;
  373. this.projectFtpUrl = data.custom.ftpUrl;
  374.  
  375. this.$alert('部署成功!', '提示', {
  376. confirmButtonText: '确定',
  377. callback: action => {
  378. window.open('http://192.168.219.170' + self.projectFtpUrl + entry)
  379. }
  380. });
  381.  
  382. });*/
  383. })
  384. .catch(_ => {});
  385. }
  386. },
  387. // 部署
  388. doDeploy () {
  389. if(!this.isDownLoadPage()) {
  390. return;
  391. }
  392. if(!this.dialogDeployVisible) {
  393. this.dialogDeployVisible = true;
  394. return;
  395. }
  396.  
  397. let self = this;
  398.  
  399. this.$refs['form'].validate((valid) => {
  400. if (valid) {
  401. this.dialogDeployVisible = false;
  402. this.depDialogVisible = true;
  403. this.loading = true;
  404. // 部署
  405. this.getDeployInfo({ type: '1', frame: this.form.frame }, (data)=> {
  406. this.loading = false;
  407. this.depDialogVisible = false;
  408.  
  409. if(!data.custom.text){
  410.  
  411. this.data = data.custom.pageTreeData;
  412. this.projectFtpUrl = data.custom.projectRootPath;
  413. this.supportDeploy = data.custom.supportDeploy;
  414.  
  415. this.setProjectEntry();
  416. // 从部署按钮直接过来的
  417. if(!this.clickNodeEntry) {
  418. this.$message({
  419. type: 'success',
  420. message: '部署成功!'
  421. });
  422. // 打开查看弹窗
  423. this.viewProject();
  424. } else {// 从点击目录结构过来的,可以调整点击的树节点页面
  425. this.$alert('部署成功!', '提示', {
  426. confirmButtonText: '确定',
  427. callback: action => {
  428. window.open('http://192.168.219.170' + self.projectFtpUrl + self.clickNodeEntry);
  429. self.clickNodeEntry = null;
  430. }
  431. });
  432. }
  433.  
  434. } else {
  435. this.$message({
  436. type: 'error',
  437. message: data.custom.text
  438. });
  439. }
  440. });
  441. } else {
  442. console.log('error submit!!');
  443. return false;
  444. }
  445. });
  446.  
  447. /*
  448. this.$confirm('此操作将把 GitLab 资源打包部署至 170 服务器,已部署过的项目会进行覆盖部署,是否继续?', '提示', {
  449. confirmButtonText: '确定',
  450. cancelButtonText: '取消',
  451. type: 'warning'
  452. }).then(() => {
  453. }).catch(() => {
  454. this.$message({
  455. type: 'info',
  456. message: '已取消部署'
  457. });
  458. });*/
  459. },
  460. // 发布项目案例库
  461. publish () {
  462. this.$confirm('此操作将把项目发布至 <a href="'+ this.projectLibUrl +'" target="_blank">项目案例库</a>,已发布过的项目案例库会有重复项,是否继续?', '提示', {
  463. confirmButtonText: '确定',
  464. cancelButtonText: '取消',
  465. dangerouslyUseHTMLString: true,
  466. type: 'warning'
  467. }).then(() => {
  468. const themes = document.querySelectorAll('.badge-secondary');
  469. let keys = [];
  470.  
  471. for(let i = 0, l = themes.length; i < l; i++) {
  472. if(themes[0].innerText == '智能设备' && i > 0) {
  473. keys.push(themes[i].innerText);
  474. } else if (themes[0].innerText !== '智能设备' && i > 1) {
  475. keys.push(themes[i].innerText);
  476. }
  477. }
  478. // 存在更多主题的情况
  479. const moreKeyEl = document.querySelector('.gl-w-full .text-nowrap');
  480. if(moreKeyEl) {
  481. const moreKeyElContent = moreKeyEl.getAttribute('data-content');
  482. const regex = />([^<]+)</g;
  483. const matches = moreKeyElContent.match(regex);
  484. const results = matches.filter(function(match) {
  485. return match.length > 3;
  486. });
  487. const moreKeyData = results.map(function(match) {
  488. return match.substring(2, match.length - 2);
  489. });
  490.  
  491. if(moreKeyData && moreKeyData.length) {
  492. keys = keys.concat(moreKeyData);
  493. }
  494. }
  495.  
  496. // 组织项目案例库所需参数
  497. const projectName = document.querySelector('.home-panel-title').innerText;
  498. const projectBU = themes[0] ? themes[0].innerText : null;
  499. const projectKeys = keys.join(' ');
  500. const entryUrl = this.projectEntryUrl;
  501. let projectType = themes[1] ? themes[1].innerText : null;
  502.  
  503. if(themes[0]) {
  504. if(themes[0].innerText == '智能设备') {
  505. projectType = themes[1] ? themes[0].innerText : null;
  506. } else {
  507. projectType = themes[1] ? themes[1].innerText : null;
  508. }
  509. }
  510.  
  511. const destUrl = this.projectLibUrl + '?projectName=' + projectName + '&projectBU=' + projectBU + '&projectType=' + projectType + '&projectKeys=' + projectKeys + '&entryUrl=' + entryUrl + '&git=' + window.location.href;
  512.  
  513. this.$message({
  514. type: 'success',
  515. message: destUrl
  516. });
  517.  
  518. window.open(destUrl);
  519.  
  520. }).catch(() => {
  521. this.$message({
  522. type: 'info',
  523. message: '已取消发布'
  524. });
  525. });
  526. },
  527. // 查看项目
  528. viewProject () {
  529. if(!this.isDownLoadPage()) {
  530. return;
  531. }
  532. this.dialogVisible = true;
  533. this.loadingTree = true;
  534. let self = this;
  535.  
  536. // 发送ajax请求,查看是否进行过部署
  537. this.getDeployInfo((data)=> {
  538. // 有部署信息,直接赋值,
  539. if(data) {
  540. self.projectIsDeployed = true;
  541. self.data = data.custom.pageTreeData;
  542. self.projectFtpUrl = data.custom.projectRootPath;
  543. self.supportDeploy = data.custom.supportDeploy;
  544. self.loadingTree = false;
  545.  
  546. self.setProjectEntry();
  547.  
  548. } else {
  549. // 无部署信息,仅查看文件目录
  550. getZipResource((data)=> {
  551. self.data = data;
  552. self.loadingTree = false;
  553. });
  554. }
  555. });
  556.  
  557. },
  558. // 项目部署信息
  559. getDeployInfo(params, callback) {
  560. const projectId = document.body.getAttribute('data-project-id');
  561. const downloadBtn = document.querySelector('.gl-button.btn-sm.btn-confirm');
  562.  
  563. const sourceUrl = downloadBtn.getAttribute('href');
  564. const downloadUrl = window.location.origin + sourceUrl;
  565. const files = document.body.getAttribute('data-find-file').split('/');
  566. const name = document.body.getAttribute('data-project') + '-' + files[files.length - 1];
  567. const author = document.querySelector('.current-user .gl-font-weight-bold').innerText.trim();
  568. const projectName = document.querySelector('.sidebar-context-title').innerText.trim();
  569. const deployManOA = document.querySelector('.current-user>a').getAttribute('data-user');
  570. const projectGitUrl = 'http://192.168.0.200' + document.body.getAttribute('data-find-file').split('/-/')[0];
  571.  
  572. if(typeof params == 'function') {
  573. callback = params;
  574. params = null;
  575. }
  576.  
  577. if(projectId && projectId.length && sourceUrl) {
  578. fetch('http://192.168.219.170:3008/api/getDeployInfo', {
  579. method: 'POST',
  580. // 允许跨域请求
  581. mode: 'cors',
  582. headers: {
  583. 'Accept': 'application/json',
  584. 'Content-Type': 'application/json'
  585. },
  586. body: JSON.stringify({//post请求参数
  587. params: {
  588. "type": (params && params.type !== undefined) ? params.type : '0',// 0 代表查看, 1代表部署
  589. "name": name, // 项目路径英文名
  590. "deployMan": author, // 部署人姓名
  591. "deployManOA": deployManOA, // 部署人账号
  592. "projectName": projectName, // 项目名称
  593. "downloadUrl": downloadUrl, // 下载地址
  594. "projectId": projectId, // 主键,gitlab上的项目id
  595. "projectGitUrl": projectGitUrl,
  596. "frame": (params && params.frame) ? params.frame : undefined
  597. }
  598. })
  599. })
  600. .then(response => response.text())
  601. .then((result) => {
  602. var data = JSON.parse(result);
  603. callback && callback(data);
  604. })
  605. .catch(error => {
  606. this.depDialogVisible = false;
  607. this.dialogVisible = false;
  608. this.$message({
  609. type: 'error',
  610. message: '系统故障,请联系管理员。'
  611. });
  612. console.error(error);
  613. });
  614. } else {
  615. this.$message({
  616. type: 'error',
  617. message: '本页不支持查看和部署,请至仓库页。'
  618. });
  619. console.error('部署信息请求参数error');
  620. }
  621. },
  622. // 进入管理平台 code pipeline
  623. manage() {
  624. window.open('http://192.168.219.170/code-pipeline')
  625. },
  626. // 设置入口
  627. setProjectEntry(){
  628. const firstNode = findFirstFileNode(this.data);
  629. if(firstNode) {
  630. this.projectEntryUrl = 'http://192.168.219.170' + this.projectFtpUrl + firstNode.entry;
  631. }else {
  632. this.projectEntryUrl = null;
  633. }
  634. },
  635. isDownLoadPage() {
  636. const downloadBtn = document.querySelector('.gl-button.btn-sm.btn-confirm');
  637. const sourceUrl = downloadBtn && downloadBtn.getAttribute('href');
  638.  
  639. if(downloadBtn && sourceUrl) {
  640. return true;
  641. } else {
  642. this.$message({
  643. type: 'error',
  644. message: '本页面不支持查看和部署,请移至仓库页。'
  645. });
  646. return false;
  647. }
  648. }
  649. },
  650. mounted() {
  651. }
  652. };
  653.  
  654. const placeholder = document.createElement('div');
  655.  
  656. // 创建 Vue 实例并挂载到页面
  657. const vueInstance = new Vue({
  658. el: placeholder,
  659. components: {
  660. MyComponent
  661. },
  662. methods: {
  663. },
  664. template: `<my-component></my-component>`
  665. });
  666.  
  667. // 等待页面加载完成
  668. window.addEventListener('load', function() {
  669. // 将占位元素追加到 body 元素中
  670. document.body.appendChild(vueInstance.$el);
  671. });
  672.  
  673. // 将文件条目组织成嵌套结构
  674. function organizeFileEntries(fileEntries) {
  675. const root = {
  676. label: document.querySelector('.home-panel-title').innerText || document.getElementById('project_name_edit').value,
  677. type: 'folder',
  678. children: []
  679. };
  680.  
  681. // 创建嵌套结构
  682. fileEntries.forEach(entry => {
  683. const pathSegments = entry.name.split('/');
  684. let currentFolder = root;
  685.  
  686. // 遍历路径中的每个部分,创建相应的文件夹节点
  687. for (let i = 0; i < pathSegments.length - 1; i++) {
  688. const folderName = pathSegments[i];
  689. let folder = currentFolder.children.find(child => child.label === folderName);
  690.  
  691. if(isExcludeFolder(entry.name)) {
  692. continue;
  693. }
  694.  
  695. if (!folder) {
  696. folder = {
  697. label: folderName,
  698. type: 'folder',
  699. children: []
  700. };
  701. currentFolder.children.push(folder);
  702. }
  703.  
  704. currentFolder = folder;
  705. }
  706.  
  707. // 创建文件节点并添加到相应的文件夹中
  708. const fileName = pathSegments[pathSegments.length - 1];
  709.  
  710. if(fileName && fileName.length && isIncludeFile(fileName) && !isExcludeFolder(entry.name)) {
  711. const fileNode = {
  712. label: fileName,
  713. type: 'file',
  714. entry: entry.name
  715. };
  716. currentFolder.children.push(fileNode);
  717. }
  718. });
  719.  
  720. return [root];
  721. }
  722. // 是否排除的文件夹
  723. function isExcludeFolder(entry) {
  724. if(entry.indexOf('css') > -1 ||
  725. entry.indexOf('scss') > -1 ||
  726. entry.indexOf('js') > -1 ||
  727. entry.indexOf('images') > -1 ||
  728. entry.indexOf('fui') > -1 ||
  729. entry.indexOf('lib') > -1 ||
  730. entry.indexOf('test') > -1 ||
  731. entry.indexOf('font') > -1 ||
  732. entry.indexOf('frame/fui') > -1) {
  733. return true;
  734. } else {
  735. return false;
  736. }
  737. }
  738. // 是否包含的文件
  739. function isIncludeFile(fileName) {
  740. if(fileName.indexOf('.html') > -1) {
  741. return true;
  742. } else {
  743. return false;
  744. }
  745. }
  746.  
  747. function getZipResource(callback) {
  748. const downloadUrl = window.location.origin + document.querySelector('.gl-button.btn-sm.btn-confirm').getAttribute('href');
  749.  
  750. fetch(downloadUrl)
  751. .then(response => response.arrayBuffer())
  752. .then(data => {
  753. // 将 ZIP 文件的二进制数据传递给 JSZip 进行解析
  754. return JSZip.loadAsync(data);
  755. })
  756. .then(zip => {
  757. // 获取 ZIP 文件中的所有条目(文件和目录)
  758. const zipEntries = Object.values(zip.files);
  759. const treeData = organizeFileEntries(zipEntries)
  760.  
  761. callback && callback(treeData);
  762.  
  763. })
  764. .catch(error => {
  765. console.error(error);
  766. });
  767. }
  768.  
  769. // 树结构第一个节点数据
  770. function findFirstFileNode(tree) {
  771. // 遍历树的节点
  772. for (let i = 0; i < tree.length; i++) {
  773. const node = tree[i];
  774.  
  775. // 如果节点的类型为 file,则返回该节点
  776. if (node.type === 'file') {
  777. return node;
  778. }
  779.  
  780. // 如果节点有子节点,则递归调用该函数查找子节点中的第一个 file 节点
  781. if (node.children && node.children.length > 0) {
  782. const fileNode = findFirstFileNode(node.children);
  783. if (fileNode) {
  784. return fileNode;
  785. }
  786. }
  787. }
  788.  
  789. // 如果没有找到 file 节点,则返回 null
  790. return null;
  791. }
  792. })();
  793.  
  794. // 修改项目描述的长度
  795. (function() {
  796. 'use strict';
  797. let regs = [/^http:\/\/192\.168\.0\.200\/fe3project\//,
  798. /^http:\/\/192\.168\.0\.200\/frontend_pc\/project\//];
  799. let match = false;
  800.  
  801. for(let r = 0, lr = regs.length; r < lr; r++) {
  802. if(regs[r].test(location.href)) {
  803. match = true;
  804. break;
  805. }
  806. }
  807.  
  808. if(!match) {
  809. return;
  810. }
  811.  
  812. let tryTimes = 6;
  813. let changed = false;
  814.  
  815. // 增加项目描述的输入长度
  816. function modifyTextareaLen() {
  817. if(tryTimes > 0 && !changed) {
  818. setTimeout(() => {
  819. const textarea = document.getElementById('project_description');
  820. tryTimes--;
  821. if(textarea) {
  822. textarea.setAttribute("maxlength", "1000");
  823. changed = true;
  824. } else {
  825. modifyTextareaLen();
  826. }
  827. }, 1000);
  828. }
  829. }
  830.  
  831. window.onload = function() {
  832. modifyTextareaLen();
  833. }
  834. })();
  835.  
  836. // 设计门户增加通往前端仓库的跳板
  837. (function() {
  838. 'use strict';
  839.  
  840. if(!/^https:\/\/oa\.epoint\.com\.cn\/interaction-design-portal\/portal\/pages\/casestemplates\/casetemplatesdetail/.test(location.href)) {
  841. return;
  842. }
  843.  
  844. function addStyle() {
  845. // 注入样式:增加按钮
  846. let injectStyle = ".content { position: relative; } .front-proto { position: absolute; top: 20px; right: 20px; width: 106px; height: 36px; border-radius: 4px; cursor: pointer; line-height: 36px; background: #25c2c9; color: #fff; text-align: center; font-size: 14px}";
  847.  
  848.  
  849. // 添加注入样式
  850. let extraStyleElement = document.createElement("style");
  851. extraStyleElement.innerHTML = injectStyle;
  852. document.head.appendChild(extraStyleElement);
  853. }
  854.  
  855. function getUrlParameters() {
  856. var params = {};
  857. var search = window.location.search.substring(1);
  858. var urlParams = search.split('&');
  859.  
  860. for (var i = 0; i < urlParams.length; i++) {
  861. var param = urlParams[i].split('=');
  862. var paramName = decodeURIComponent(param[0]);
  863. var paramValue = decodeURIComponent(param[1] || '');
  864. params[paramName] = paramValue;
  865. }
  866.  
  867. return params;
  868. }
  869.  
  870. window.onload = ()=> {
  871. addStyle();
  872. const $content = jQuery('.content');
  873.  
  874. $content.append('<div class="front-proto">前端原型</div>');
  875.  
  876. const $frontBtn = jQuery('.front-proto', $content);
  877.  
  878. $frontBtn.on('click', ()=> {
  879. window.open('http://192.168.0.200/fe3project?filter=' + getUrlParameters().guid);
  880. });
  881. };
  882. })();
  883.  
  884. // 前端项目案例库增加获取参数的能力
  885. (function() {
  886. 'use strict';
  887.  
  888. let regs = [/^http:\/\/192\.168\.118\.60:9999\/webapp\/pages\/caselib\/create\.html/,
  889. /^http:\/\/192\.168\.201\.159:9999\/webapp\/pages\/default\/onlinecase.html/];
  890. let match = false;
  891.  
  892. for(let r = 0, lr = regs.length; r < lr; r++) {
  893. if(regs[r].test(location.href)) {
  894. match = true;
  895. break;
  896. }
  897. }
  898.  
  899. if(!match) {
  900. return;
  901. }
  902.  
  903. const businessType = [
  904. { Value: '7a20e23c-30b8-47e2-8d8d-f2691c9c63c4', Text: '政务服务' },
  905. { Value: 'c12150bb-b358-452f-87f0-8a2254df87cb', Text: '政务协同' },
  906. { Value: '3845804e-de68-421c-9402-7b238cfb5a70', Text: '大数据' },
  907. { Value: '3c28ee56-f24d-4843-b9a2-93e6b96264f4', Text: '电子交易' },
  908. { Value: '673b5918-51bc-4f1a-ab73-fca86e54d7d1', Text: '数字建设' },
  909. { Value: '6d9e7d84-7de3-4e0f-bd4f-ed4722ed25b5', Text: '建筑企业' },
  910. { Value: 'c22f8d2f-518d-4381-b88c-1da68536ed3a', Text: '公共安全' },
  911. { Value: 'c5810829-1b21-4b22-85cd-390b1edd9614', Text: '智能设备' },
  912. { Value: '080c7560-c261-428b-a45d-b86b57b47ffb', Text: '中央研究院' }
  913. ];
  914.  
  915. const projectType = [
  916. { Value: 'dca44f63-be3f-4e9c-b78f-d786571c22c9', Text: '网站' },
  917. { Value: 'c7861460-163b-4060-80ec-d60604c50435', Text: '业务系统' },
  918. { Value: '49accc71-6f7d-43f3-b726-58decf58b6fa', Text: '智能设备' },
  919. { Value: '90209c65-1a55-4d8c-a836-2e5c6b834ada', Text: '大屏可视化' },
  920. { Value: 'fb0415fb-65ee-42c1-895a-dca042c2568e', Text: '中屏可视化' },
  921. { Value: '2b83f9b1-ec78-4819-a400-d7d49ea1ecc5', Text: '其他' }
  922. ];
  923.  
  924. let $businesstype;
  925. let $projecttype;
  926.  
  927. function getUrlParameters() {
  928. var params = {};
  929. var search = window.location.search.substring(1);
  930. var urlParams = search.split('&');
  931.  
  932. for (var i = 0; i < urlParams.length; i++) {
  933. var param = urlParams[i].split('=');
  934. var paramName = decodeURIComponent(param[0]);
  935. var paramValue = decodeURIComponent(param[1] || '');
  936. params[paramName] = paramValue;
  937. }
  938.  
  939. return params;
  940. }
  941.  
  942. function initForm (params) {
  943. if(typeof params === 'object') {
  944. document.getElementsByName('Title')[0].value = params.projectName ? params.projectName : '';
  945. document.getElementsByName('KeyWords')[0].value = params.projectKeys ? params.projectKeys : '';
  946. document.getElementsByName('Entry')[0].value = params.entryUrl ? params.entryUrl : '';
  947. document.getElementsByName('SourceCode')[0].value = params.git ? params.git : '';
  948. }
  949. }
  950.  
  951. let setSuccess = false;
  952. let setTimes = 5;
  953.  
  954. function initSelect(params) {
  955. if(typeof params !== 'object') {
  956. return;
  957. }
  958.  
  959. if(setTimes > 0 && !setSuccess) {
  960. setTimeout(()=> {
  961. setTimes--;
  962.  
  963. businessType.forEach((item)=> {
  964. if(params.projectBU) {
  965. if(item.Text === params.projectBU.trim()) {
  966. $businesstype.val(item.Value);
  967. } else if( params.projectBU.trim() == '一网统管' || params.projectBU.trim() == '一网协同' || params.projectBU.trim() == '一网通办' ) {
  968. $businesstype.val('7a20e23c-30b8-47e2-8d8d-f2691c9c63c4');
  969. }
  970. $businesstype.trigger("chosen:updated");
  971. }
  972. });
  973.  
  974. projectType.forEach((item)=> {
  975. if(params.projectType && item.Text === params.projectType.trim()) {
  976. $projecttype.val(item.Value);
  977. $projecttype.trigger("chosen:updated");
  978. }
  979. });
  980.  
  981. setSuccess = true;
  982.  
  983. }, 1000);
  984. } else {
  985. initSelect(params);
  986. }
  987. }
  988.  
  989. window.onload = ()=> {
  990. const params = getUrlParameters();
  991.  
  992. $businesstype = jQuery('#businesstype');
  993. $projecttype = jQuery('#projecttype');
  994.  
  995. initForm(params);
  996. initSelect(params);
  997. // console.log(jQuery.ajax);
  998. };
  999. })();