GitLab Assistant

GitLab Viewer Publish and Deploy Project!

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

  1. // ==UserScript==
  2. // @name GitLab Assistant
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.09987
  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 https://oa.epoint.com.cn/interaction-design-portal/portal/pages/generalpagetemplates/generalpagetemplatesdetail*
  13. // @match http://192.168.201.159:9999/webapp/pages/default/onlinecase.html*
  14. // @match http://192.168.118.60:9999/webapp/pages/caselib/create.html*
  15. // @match https://oa.epoint.com.cn/epointoa9/frame/fui/pages/themes/aide/aide*
  16. // @match https://oa.epoint.com.cn/interaction-design-portal/portal/pages/casestemplates/casetemplateslist*
  17. // @match https://oa.epoint.com.cn:8080/OA9/oa9/mail/mailreceivedetail*
  18. // @icon http://192.168.0.200/assets/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.png
  19. // @require https://cdn.bootcdn.net/ajax/libs/vue/2.7.14/vue.min.js
  20. // @require https://unpkg.com/element-ui/lib/index.js
  21. // @require https://cdn.bootcdn.net/ajax/libs/jszip/3.7.1/jszip.min.js
  22. // @grant GM_getResourceURL
  23. // @grant GM_addStyle
  24. // @grant GM_getResourceText
  25. // @resource ElementCSS https://unpkg.com/element-ui/lib/theme-chalk/index.css
  26. // @grant GM_xmlhttpRequest
  27. // @license MIT
  28. // @run-at document-end
  29. // ==/UserScript==
  30.  
  31.  
  32. // 个性化 gitlab 样式,
  33. // 满足项目详情多行显示,
  34. // 项目列表中的链接新窗口打开,
  35. // 搜索框 placeholder 个性化提示
  36. // 增加CodePipeline 入口等
  37. (function() {
  38. 'use strict';
  39.  
  40. let regs = [/^http:\/\/192\.168\.0\.200\/fe3project\//,
  41. /^http:\/\/192\.168\.0\.200\/frontend_pc\/project\//,
  42. /^http:\/\/192\.168\.0\.200/,
  43. /^http:\/\/192\.168\.0\.200\//];
  44. let match = false;
  45.  
  46. for(let r = 0, lr = regs.length; r < lr; r++) {
  47. if(regs[r].test(location.href)) {
  48. match = true;
  49. break;
  50. }
  51. }
  52.  
  53. if(!match) {
  54. return;
  55. }
  56.  
  57. // 注入样式:改变容器宽度,项目描述多行展示
  58. 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;}";
  59.  
  60. injectStyle += ".container-fluid.container-limited.limit-container-width .file-holder.readme-holder.limited-width-container .file-content {max-width: none;}"
  61. injectStyle += 'button:focus {outline-color: transparent !important;}'
  62. injectStyle += '.has-description .description {word-break: break-all;}'
  63. // 添加注入样式
  64. let extraStyleElement = document.createElement("style");
  65. extraStyleElement.innerHTML = injectStyle;
  66. document.head.appendChild(extraStyleElement);
  67.  
  68. const fontUrl = 'https://element.eleme.io/2.11/static/element-icons.535877f.woff';
  69.  
  70. // 添加样式规则,将字体应用到指定元素上
  71. GM_addStyle(`
  72. @font-face {
  73. font-family: element-icons;
  74. src: url(${fontUrl}) format("woff");
  75. }
  76. `);
  77.  
  78. GM_addStyle(GM_getResourceText('ElementCSS'));
  79.  
  80. // 改变列表打开链接方式,改为新窗口打开
  81. let change = false;
  82. let tryTimes = 3;
  83.  
  84. function changeOpenType() {
  85. if(!change){
  86. setTimeout(()=> {
  87. let links = document.querySelectorAll('.description a');
  88. if(links.length) {
  89. for(let i = 0, l = links.length; i < l; i++) {
  90. links[i].target = "_blank";
  91. if(i === l - 1) {
  92. change = true;
  93. }
  94. }
  95. } else {
  96. changeOpenType();
  97. }
  98. }, 1000);
  99. }
  100. }
  101.  
  102. function stopLinkProp() {
  103. setTimeout(()=> {
  104. const links = document.querySelectorAll('.description a');
  105. for(let i = 0, l = links.length; i < l; i++) {
  106. links[i].addEventListener('click', ()=> {
  107. event.stopPropagation();
  108. });
  109. }
  110. }, 1000);
  111. }
  112.  
  113. // 等待页面加载完成
  114. window.addEventListener('load', function() {
  115.  
  116. var targetDiv = document.querySelector('section');
  117.  
  118. if(targetDiv) {
  119. // 创建一个 Mutation Observer 实例
  120. var observer = new MutationObserver(function(mutations) {
  121. // 在这里处理 div 子元素的变化
  122. mutations.forEach(function(mutation) {
  123. if(mutation.addedNodes && mutation.addedNodes.length) {
  124. change = false;
  125. changeOpenType();
  126.  
  127. stopLinkProp();
  128. }
  129. });
  130. });
  131.  
  132. // 配置 Mutation Observer
  133. var config = { childList: true, subtree: true };
  134.  
  135. // 开始观察目标 div 元素
  136. observer.observe(targetDiv, config);
  137. }
  138.  
  139. const placeholder = document.createElement('div');
  140.  
  141. // 创建 Vue 实例并挂载到页面
  142. const vueInstance = new Vue({
  143. el: placeholder,
  144. methods: {
  145. // 进入管理平台 code pipeline
  146. manage() {
  147. window.open('http://192.168.219.170/code-pipeline')
  148. }
  149. },
  150. template: `<div id="my-ext" style="margin-top:4px;">
  151. <el-tooltip content="进入 Code Pipeline 管理平台" placement="top" effect="light">
  152. <el-button type="primary" icon="el-icon-attract" size="small" circle @click="manage"></el-button>
  153. </el-tooltip>
  154. </div>`
  155. });
  156.  
  157. // 将占位元素追加到 body 元素中
  158. document.querySelector('.title-container').appendChild(vueInstance.$el);
  159.  
  160. // 修改 placehodler
  161. const listInput = document.getElementById('group-filter-form-field');
  162. const listInput2 = document.getElementById('project-filter-form-field');
  163.  
  164. if(listInput) {
  165. listInput.setAttribute("placeholder", "按项目名称、日期、开发者搜索,关键字≥3");
  166. listInput.style.width = '305px';
  167. }
  168. if(listInput2) {
  169. listInput2.setAttribute("placeholder", "按项目名称、日期、开发者搜索,关键字≥3");
  170. listInput2.style.width = '305px';
  171. }
  172. });
  173.  
  174.  
  175. })();
  176.  
  177. (function(){
  178. function convertDateFormat(inputString) {
  179. // 匹配日期格式为yyyy-mm-dd或yyyy-m-dd或yyyy-mm-d或yyyy-m-d的正则表达式
  180. const dateRegex = /\d{4}-\d{1,2}-\d{1,2}/g;
  181.  
  182. // 找到所有匹配的日期格式
  183. const dates = inputString.match(dateRegex);
  184.  
  185. // 如果没有匹配到日期,则直接返回原始字符串
  186. if (!dates || dates.length === 0) {
  187. return inputString;
  188. }
  189.  
  190. // 遍历所有匹配到的日期,进行转换
  191. dates.forEach((date) => {
  192. const [year, month, day] = date.split('-');
  193. const formattedDate = `${parseInt(year, 10)}-${parseInt(month, 10)}-${parseInt(day, 10)}`;
  194. inputString = inputString.replace(date, formattedDate);
  195. });
  196.  
  197. return inputString;
  198. }
  199.  
  200. function getUrlParameters() {
  201. var params = {};
  202. var search = window.location.search.substring(1);
  203. var urlParams = search.split('&');
  204.  
  205. for (var i = 0; i < urlParams.length; i++) {
  206. var param = urlParams[i].split('=');
  207. var paramName = decodeURIComponent(param[0]);
  208. var paramValue = decodeURIComponent(param[1] || '');
  209. if(paramName) {
  210. params[paramName] = paramValue;
  211. }
  212. }
  213.  
  214. return params;
  215. }
  216.  
  217. window.convertDateFormat = convertDateFormat;
  218.  
  219. window.gitlabUtil = {
  220. getUrlParameters: getUrlParameters
  221. }
  222. })();
  223.  
  224. // GitLab Viewer Publish and Deploy Project
  225. // 查看项目、部署项目、发布项目功能
  226. // 添加搜索设计门户资源功能
  227. (function() {
  228. 'use strict';
  229. let regs = [/^http:\/\/192\.168\.0\.200\/fe3project\//,
  230. /^http:\/\/192\.168\.0\.200\/frontend_pc\/project\//];
  231. let match = false;
  232.  
  233. for(let r = 0, lr = regs.length; r < lr; r++) {
  234. if(regs[r].test(location.href)) {
  235. match = true;
  236. break;
  237. }
  238. }
  239.  
  240. if(!match) {
  241. return;
  242. }
  243. /*
  244. const fontUrl = 'https://element.eleme.io/2.11/static/element-icons.535877f.woff';
  245.  
  246. // 添加样式规则,将字体应用到指定元素上
  247. GM_addStyle(`
  248. @font-face {
  249. font-family: element-icons;
  250. src: url(${fontUrl}) format("woff");
  251. }
  252. `);
  253.  
  254. GM_addStyle(GM_getResourceText('ElementCSS'));
  255. */
  256.  
  257. let epointCss = ".epoint-tool {position: fixed; bottom: 0%; right:10px; transform: translateY(-40%);}";
  258. epointCss += ".el-row { padding: 6px 0;} .el-dialog__body .el-tree{min-height: 420px; max-height: 500px;overflow: auto;}";
  259. epointCss += ".view-toolbar {padding-bottom: 10px;}";
  260. epointCss += ".deploy-body {height: 306px;}"
  261. epointCss += ".el-loading-spinner {margin-top: -60px;} .el-button--primary:focus {outline: 0 !important;}";
  262.  
  263. // 添加注入样式
  264. let extraStyleElement = document.createElement("style");
  265. extraStyleElement.innerHTML = epointCss;
  266. document.head.appendChild(extraStyleElement);
  267.  
  268. const MyComponent = {
  269. template: `<div class="epoint-wrap">
  270. <div class="epoint-tool">
  271. <el-row>
  272. <el-tooltip content="查看项目页面和模块结构" placement="left" effect="light">
  273. <el-button type="primary" icon="el-icon-search" round @click="viewProject">查看</el-button>
  274. </el-tooltip>
  275. </el-row>
  276. <el-row>
  277. <el-tooltip content="一键部署到170服务器" placement="left" effect="light">
  278. <el-button type="primary" icon="el-icon-s-unfold" round @click="doDeploy">部署</el-button>
  279. </el-tooltip>
  280. </el-row>
  281. <el-row>
  282. <el-tooltip content="一键发布到项目案例库" placement="left" effect="light">
  283. <el-button type="primary" icon="el-icon-upload" round @click="publish">发布</el-button>
  284. </el-tooltip>
  285. </el-row>
  286. <el-row>
  287. <el-tooltip content="进入 Code Pipeline 管理平台进行更多操作" placement="left" effect="light">
  288. <el-button type="primary" icon="el-icon-attract" round @click="manage">管理</el-button>
  289. </el-tooltip>
  290. </el-row>
  291. </div>
  292. <el-dialog
  293. title="目录结构"
  294. width="900px"
  295. :append-to-body="true"
  296. :visible.sync="dialogVisible"
  297. :before-close="handleClose">
  298. <div class="view-body">
  299. <div class="view-toolbar" v-if="projectFtpUrl"><el-button type="primary" size="small" @click="viewAll">查看全部</el-button></div>
  300. <div class="view-content">
  301. <el-tree
  302. class="filter-tree"
  303. :data="data"
  304. :props="defaultProps"
  305. node-key="id"
  306. default-expand-all
  307. @node-click="handleNodeClick"
  308. v-loading="loadingTree"
  309. element-loading-background="rgba(255, 255, 255, 1)"
  310. element-loading-text="拼命加载中......"
  311. ref="tree">
  312. </el-tree>
  313. </div>
  314. </div>
  315. </el-dialog>
  316. <el-dialog
  317. title="部署"
  318. width="420px"
  319. :visible.sync="depDialogVisible">
  320. <div class="deploy-body"
  321. v-loading="loading"
  322. element-loading-text="正在打包部署至 170 服务器,请耐心等待"
  323. element-loading-spinner="el-icon-loading">
  324. </div>
  325. </el-dialog>
  326. <el-dialog title="部署提示" width="640px" :visible.sync="dialogDeployVisible">
  327. <el-alert
  328. title="此操作将把 GitLab 资源打包部署至 170 服务器,已部署过的项目会进行覆盖部署,是否继续?"
  329. type="warning" :closable="false" style="margin-bottom: 20px;">
  330. </el-alert>
  331. <el-form :model="form" ref="form" :rules="rules">
  332. <el-form-item label="框架类型" :label-width="formLabelWidth" prop="frame">
  333. <el-select v-model="form.frame" placeholder="请框架类型">
  334. <el-option v-for="item in framework" :key="item.value" :label="item.label" :value="item.value"></el-option>
  335. </el-select>
  336. </el-form-item>
  337. </el-form>
  338. <div slot="footer" class="dialog-footer">
  339. <el-button @click="dialogDeployVisible = false">取 消</el-button>
  340. <el-button type="primary" @click="doDeploy">确 定</el-button>
  341. </div>
  342. </el-dialog>
  343. </div>`,
  344. data() {
  345. return {
  346. dialogVisible: false, // 查看目录结构弹窗
  347. data: [], // 目录结构树的数据结构
  348. defaultProps: {
  349. children: 'children',
  350. label: 'label'
  351. },
  352. loadingTree: false,
  353. depDialogVisible: false, // 部署中弹窗
  354. loading: false,
  355. projectLibUrl: 'http://192.168.118.60:9999/webapp/pages/caselib/create.html', // 项目案例库地址
  356. projectIsDeployed: false, // 项目是否部署过
  357. projectFtpUrl: '', // ftp路径
  358. projectEntryUrl: '', // 项目案例库发布表单的入口页面
  359. supportDeploy: true, // 项目是否支持部署
  360. dialogDeployVisible: false,
  361. formLabelWidth: '120px',
  362. form: {
  363. frame: ''
  364. },
  365. framework: [
  366. {label: '重构模板', value: 1},
  367. {label: 'f9x1.0', value: 2},
  368. {label: 'f9x2.0', value: 3},
  369. {label: 'f950', value: 4},
  370. {label: 'f950sp1', value: 5},
  371. {label: 'f950sp2', value: 6},
  372. {label: 'f950sp3', value: 7},
  373. {label: 'f951', value: 18},
  374. {label: 'f940', value: 8},
  375. {label: 'f941', value: 10},
  376. {label: 'f942', value: 9},
  377. {label: 'f934', value: 11},
  378. {label: 'f933', value: 12},
  379. {label: 'f932', value: 13},
  380. {label: 'f9211', value: 14},
  381. {label: '骨架', value: 15},
  382. {label: 'Vue', value: 16},
  383. {label: 'React', value: 17}
  384. ],
  385. rules: {
  386. frame: [
  387. { required: true, message: '请选择框架类型', trigger: 'change' }
  388. ]
  389. },
  390. clickNodeEntry: null
  391. };
  392. },
  393. methods: {
  394. handleClose(done) {
  395. done();
  396. /*
  397. this.$confirm('确认关闭?')
  398. .then(_ => {
  399. done();
  400. })
  401. .catch(_ => {});*/
  402. },
  403. handleNodeClick(data) {
  404. console.log(data);
  405. if(data.type === 'folder') {
  406. return false;
  407. }
  408. var self = this;
  409. var entry = data.entry;
  410. self.clickNodeEntry = entry;
  411. if(self.projectFtpUrl) {
  412. window.open('http://192.168.219.170' + self.projectFtpUrl + data.entry);
  413. self.clickNodeEntry = null;
  414. } else {
  415. if(this.supportDeploy === false) {
  416. this.$message({
  417. type: 'error',
  418. message: '当前项目暂时只支持查看,请耐心等待功能升级。'
  419. });
  420. self.clickNodeEntry = null;
  421. return false;
  422. }
  423.  
  424. this.$confirm('资源未部署,部署至 170 服务器后可查看,是否部署?')
  425. .then(_ => {
  426.  
  427. this.dialogDeployVisible = true;
  428. /*
  429. this.depDialogVisible = true;
  430. this.loading = true;
  431. // 部署
  432. this.getDeployInfo({ type: '1' }, (data)=> {
  433. this.loading = false;
  434. this.depDialogVisible = false;
  435.  
  436. this.data = data.custom.detail;
  437. this.projectFtpUrl = data.custom.ftpUrl;
  438.  
  439. this.$alert('部署成功!', '提示', {
  440. confirmButtonText: '确定',
  441. callback: action => {
  442. window.open('http://192.168.219.170' + self.projectFtpUrl + entry)
  443. }
  444. });
  445.  
  446. });*/
  447. })
  448. .catch(_ => {});
  449. }
  450. },
  451. // 部署
  452. doDeploy () {
  453. if(!this.isDownLoadPage()) {
  454. return;
  455. }
  456. if(!this.dialogDeployVisible) {
  457. this.dialogDeployVisible = true;
  458. return;
  459. }
  460.  
  461. let self = this;
  462.  
  463. this.$refs['form'].validate((valid) => {
  464. if (valid) {
  465. this.dialogDeployVisible = false;
  466. this.depDialogVisible = true;
  467. this.loading = true;
  468. // 部署
  469. this.getDeployInfo({ type: '1', frame: this.form.frame }, (data)=> {
  470. this.loading = false;
  471. this.depDialogVisible = false;
  472.  
  473. if(!data.custom.text){
  474.  
  475. this.data = data.custom.pageTreeData;
  476. this.projectFtpUrl = data.custom.projectRootPath;
  477. this.supportDeploy = data.custom.supportDeploy;
  478.  
  479. this.setProjectEntry();
  480. // 从部署按钮直接过来的
  481. if(!this.clickNodeEntry) {
  482. this.$message({
  483. type: 'success',
  484. message: '部署成功!'
  485. });
  486. // 打开查看弹窗
  487. this.viewProject();
  488. } else {// 从点击目录结构过来的,可以调整点击的树节点页面
  489. this.$alert('部署成功!', '提示', {
  490. confirmButtonText: '确定',
  491. callback: action => {
  492. window.open('http://192.168.219.170' + self.projectFtpUrl + self.clickNodeEntry);
  493. self.clickNodeEntry = null;
  494. }
  495. });
  496. }
  497.  
  498. } else {
  499. this.$message({
  500. type: 'error',
  501. message: data.custom.text
  502. });
  503. }
  504. });
  505. } else {
  506. console.log('error submit!!');
  507. return false;
  508. }
  509. });
  510.  
  511. /*
  512. this.$confirm('此操作将把 GitLab 资源打包部署至 170 服务器,已部署过的项目会进行覆盖部署,是否继续?', '提示', {
  513. confirmButtonText: '确定',
  514. cancelButtonText: '取消',
  515. type: 'warning'
  516. }).then(() => {
  517. }).catch(() => {
  518. this.$message({
  519. type: 'info',
  520. message: '已取消部署'
  521. });
  522. });*/
  523. },
  524. // 发布项目案例库
  525. publish () {
  526. this.$confirm('此操作将把项目发布至 <a href="'+ this.projectLibUrl +'" target="_blank">项目案例库</a>,已发布过的项目案例库会有重复项,是否继续?', '提示', {
  527. confirmButtonText: '确定',
  528. cancelButtonText: '取消',
  529. dangerouslyUseHTMLString: true,
  530. type: 'warning'
  531. }).then(() => {
  532. const themes = document.querySelectorAll('.badge-secondary');
  533. let keys = [];
  534.  
  535. for(let i = 0, l = themes.length; i < l; i++) {
  536. if(themes[0].innerText == '智能设备' && i > 0) {
  537. keys.push(themes[i].innerText);
  538. } else if (themes[0].innerText !== '智能设备' && i > 1) {
  539. keys.push(themes[i].innerText);
  540. }
  541. }
  542. // 存在更多主题的情况
  543. const moreKeyEl = document.querySelector('.gl-w-full .text-nowrap');
  544. if(moreKeyEl) {
  545. const moreKeyElContent = moreKeyEl.getAttribute('data-content');
  546. const regex = />([^<]+)</g;
  547. const matches = moreKeyElContent.match(regex);
  548. const results = matches.filter(function(match) {
  549. return match.length > 3;
  550. });
  551. const moreKeyData = results.map(function(match) {
  552. return match.substring(2, match.length - 2);
  553. });
  554.  
  555. if(moreKeyData && moreKeyData.length) {
  556. keys = keys.concat(moreKeyData);
  557. }
  558. }
  559.  
  560. // 组织项目案例库所需参数
  561. const projectName = document.querySelector('.home-panel-title').innerText;
  562. const projectBU = themes[0] ? themes[0].innerText : null;
  563. const projectKeys = keys.join(' ');
  564. const entryUrl = this.projectEntryUrl;
  565. let projectType = themes[1] ? themes[1].innerText : null;
  566.  
  567. if(themes[0]) {
  568. if(themes[0].innerText == '智能设备') {
  569. projectType = themes[1] ? themes[0].innerText : null;
  570. } else {
  571. projectType = themes[1] ? themes[1].innerText : null;
  572. }
  573. }
  574.  
  575. const destUrl = this.projectLibUrl + '?projectName=' + projectName + '&projectBU=' + projectBU + '&projectType=' + projectType + '&projectKeys=' + projectKeys + '&entryUrl=' + entryUrl + '&git=' + window.location.href;
  576.  
  577. this.$message({
  578. type: 'success',
  579. message: destUrl
  580. });
  581.  
  582. window.open(destUrl);
  583.  
  584. }).catch(() => {
  585. this.$message({
  586. type: 'info',
  587. message: '已取消发布'
  588. });
  589. });
  590. },
  591. // 查看项目
  592. viewProject () {
  593. if(!this.isDownLoadPage()) {
  594. return;
  595. }
  596. this.dialogVisible = true;
  597. this.loadingTree = true;
  598. let self = this;
  599.  
  600. // 发送ajax请求,查看是否进行过部署
  601. this.getDeployInfo((data)=> {
  602. // 有部署信息,直接赋值,
  603. if(data) {
  604. self.projectIsDeployed = true;
  605. self.data = data.custom.pageTreeData;
  606. self.projectFtpUrl = data.custom.projectRootPath;
  607. self.supportDeploy = data.custom.supportDeploy;
  608. self.loadingTree = false;
  609.  
  610. self.setProjectEntry();
  611.  
  612. } else {
  613. // 无部署信息,仅查看文件目录
  614. getZipResource((data)=> {
  615. self.data = data;
  616. self.loadingTree = false;
  617. });
  618. }
  619. });
  620.  
  621. },
  622. // 查看项目的所有页面
  623. viewAll() {
  624. window.open('http://192.168.219.170/code-pipeline/#/project/deploy-preview?rowguid=' + document.body.getAttribute('data-project-id'));
  625. },
  626. // 项目部署信息
  627. getDeployInfo(params, callback) {
  628. const projectId = document.body.getAttribute('data-project-id');
  629. const downloadBtn = document.querySelector('.gl-button.btn-sm.btn-confirm');
  630.  
  631. const sourceUrl = downloadBtn.getAttribute('href');
  632. const downloadUrl = window.location.origin + sourceUrl;
  633. const files = document.body.getAttribute('data-find-file').split('/');
  634. const name = document.body.getAttribute('data-project') + '-' + files[files.length - 1];
  635. const author = document.querySelector('.current-user .gl-font-weight-bold').innerText.trim();
  636. const projectName = document.querySelector('.sidebar-context-title').innerText.trim();
  637. const deployManOA = document.querySelector('.current-user>a').getAttribute('data-user');
  638. const projectGitUrl = 'http://192.168.0.200' + document.body.getAttribute('data-find-file').split('/-/')[0];
  639.  
  640. if(typeof params == 'function') {
  641. callback = params;
  642. params = null;
  643. }
  644.  
  645. if(projectId && projectId.length && sourceUrl) {
  646. fetch('http://192.168.219.170:3008/api/getDeployInfo', {
  647. method: 'POST',
  648. // 允许跨域请求
  649. mode: 'cors',
  650. headers: {
  651. 'Accept': 'application/json',
  652. 'Content-Type': 'application/json'
  653. },
  654. body: JSON.stringify({//post请求参数
  655. params: {
  656. "type": (params && params.type !== undefined) ? params.type : '0',// 0 代表查看, 1代表部署
  657. "name": name, // 项目路径英文名
  658. "deployMan": author, // 部署人姓名
  659. "deployManOA": deployManOA, // 部署人账号
  660. "projectName": projectName, // 项目名称
  661. "downloadUrl": downloadUrl, // 下载地址
  662. "projectId": projectId, // 主键,gitlab上的项目id
  663. "projectGitUrl": projectGitUrl,
  664. "frame": (params && params.frame) ? params.frame : undefined
  665. }
  666. })
  667. })
  668. .then(response => response.text())
  669. .then((result) => {
  670. var data = JSON.parse(result);
  671. callback && callback(data);
  672. })
  673. .catch(error => {
  674. this.depDialogVisible = false;
  675. this.dialogVisible = false;
  676. this.$message({
  677. type: 'error',
  678. message: '系统故障,请联系管理员。'
  679. });
  680. console.error(error);
  681. });
  682. } else {
  683. this.$message({
  684. type: 'error',
  685. message: '本页不支持查看和部署,请至仓库页。'
  686. });
  687. console.error('部署信息请求参数error');
  688. }
  689. },
  690. // 进入管理平台 code pipeline
  691. manage() {
  692. window.open('http://192.168.219.170/code-pipeline')
  693. },
  694. // 设置入口
  695. setProjectEntry(){
  696. const firstNode = findFirstFileNode(this.data);
  697. if(firstNode) {
  698. this.projectEntryUrl = 'http://192.168.219.170' + this.projectFtpUrl + firstNode.entry;
  699. }else {
  700. this.projectEntryUrl = null;
  701. }
  702. },
  703. isDownLoadPage() {
  704. const downloadBtn = document.querySelector('.gl-button.btn-sm.btn-confirm');
  705. const sourceUrl = downloadBtn && downloadBtn.getAttribute('href');
  706.  
  707. if(downloadBtn && sourceUrl) {
  708. return true;
  709. } else {
  710. this.$message({
  711. type: 'error',
  712. message: '本页面不支持查看和部署,请移至仓库页。'
  713. });
  714. return false;
  715. }
  716. }
  717. },
  718. mounted() {
  719. }
  720. };
  721.  
  722. const placeholder = document.createElement('div');
  723.  
  724. // 创建 Vue 实例并挂载到页面
  725. const vueInstance = new Vue({
  726. el: placeholder,
  727. components: {
  728. MyComponent
  729. },
  730. methods: {
  731. },
  732. template: `<my-component></my-component>`
  733. });
  734.  
  735. // 开始在项目仓库页添加搜索设计门户按钮 start
  736. const panel = document.querySelector('.project-home-panel');
  737. let vueInstance2;
  738. const description = document.querySelector('.read-more-container');
  739. const descriptionText = description && description.innerText;
  740. let haveDesignBackupUrl = /casetemplatesdetail\?guid=(?!$)/.test(descriptionText);
  741.  
  742. if(panel && !haveDesignBackupUrl) {
  743. const btnPlaceholder = document.createElement('div');
  744. btnPlaceholder.setAttribute('class', 'epoint-portal-search');
  745. // 创建 Vue 实例并挂载到页面
  746. vueInstance2 = new Vue({
  747. el: btnPlaceholder,
  748. data: function() {
  749. return {
  750. proName: document.querySelector('.home-panel-title').innerText.trim().substring(0, 4)
  751. };
  752. },
  753. methods: {
  754. manage() {
  755. window.open('https://oa.epoint.com.cn/epointoa9/frame/fui/pages/themes/aide/aide?pageId=aide&redirect=https://oa.epoint.com.cn/interaction-design-portal/portal/pages/casestemplates/casetemplateslist?projectname' + this.proName)
  756. }
  757. },
  758. template: `<div style="margin-top:4px;">
  759. <el-tooltip content="新点设计门户中查找此项目" placement="top" effect="light">
  760. <el-button type="primary" icon="el-icon-search" size="small" @click="manage">查找UI备份地址</el-button>
  761. </el-tooltip>
  762. </div>`
  763. });
  764. // 开始在项目仓库页添加搜索设计门户按钮 end
  765. }
  766.  
  767.  
  768. // 等待页面加载完成
  769. window.addEventListener('load', function() {
  770. // 将占位元素追加到 body 元素中
  771. document.body.appendChild(vueInstance.$el);
  772.  
  773. panel && !haveDesignBackupUrl && panel.appendChild(vueInstance2.$el);
  774. });
  775.  
  776. // 将文件条目组织成嵌套结构
  777. function organizeFileEntries(fileEntries) {
  778. const root = {
  779. label: document.querySelector('.home-panel-title').innerText || document.getElementById('project_name_edit').value,
  780. type: 'folder',
  781. children: []
  782. };
  783.  
  784. // 创建嵌套结构
  785. fileEntries.forEach(entry => {
  786. const pathSegments = entry.name.split('/');
  787. let currentFolder = root;
  788.  
  789. // 遍历路径中的每个部分,创建相应的文件夹节点
  790. for (let i = 0; i < pathSegments.length - 1; i++) {
  791. const folderName = pathSegments[i];
  792. let folder = currentFolder.children.find(child => child.label === folderName);
  793.  
  794. if(isExcludeFolder(entry.name)) {
  795. continue;
  796. }
  797.  
  798. if (!folder) {
  799. folder = {
  800. label: folderName,
  801. type: 'folder',
  802. children: []
  803. };
  804. currentFolder.children.push(folder);
  805. }
  806.  
  807. currentFolder = folder;
  808. }
  809.  
  810. // 创建文件节点并添加到相应的文件夹中
  811. const fileName = pathSegments[pathSegments.length - 1];
  812.  
  813. if(fileName && fileName.length && isIncludeFile(fileName) && !isExcludeFolder(entry.name)) {
  814. const fileNode = {
  815. label: fileName,
  816. type: 'file',
  817. entry: entry.name
  818. };
  819. currentFolder.children.push(fileNode);
  820. }
  821. });
  822.  
  823. return [root];
  824. }
  825. // 是否排除的文件夹
  826. function isExcludeFolder(entry) {
  827. if(entry.indexOf('css') > -1 ||
  828. entry.indexOf('scss') > -1 ||
  829. entry.indexOf('js') > -1 ||
  830. entry.indexOf('images') > -1 ||
  831. entry.indexOf('fui') > -1 ||
  832. entry.indexOf('lib') > -1 ||
  833. entry.indexOf('test') > -1 ||
  834. entry.indexOf('font') > -1 ||
  835. entry.indexOf('frame/fui') > -1) {
  836. return true;
  837. } else {
  838. return false;
  839. }
  840. }
  841. // 是否包含的文件
  842. function isIncludeFile(fileName) {
  843. if(fileName.indexOf('.html') > -1) {
  844. return true;
  845. } else {
  846. return false;
  847. }
  848. }
  849.  
  850. function getZipResource(callback) {
  851. const downloadUrl = window.location.origin + document.querySelector('.gl-button.btn-sm.btn-confirm').getAttribute('href');
  852.  
  853. fetch(downloadUrl)
  854. .then(response => response.arrayBuffer())
  855. .then(data => {
  856. // 将 ZIP 文件的二进制数据传递给 JSZip 进行解析
  857. return JSZip.loadAsync(data);
  858. })
  859. .then(zip => {
  860. // 获取 ZIP 文件中的所有条目(文件和目录)
  861. const zipEntries = Object.values(zip.files);
  862. const treeData = organizeFileEntries(zipEntries)
  863.  
  864. callback && callback(treeData);
  865.  
  866. })
  867. .catch(error => {
  868. console.error(error);
  869. });
  870. }
  871.  
  872. // 树结构第一个节点数据
  873. function findFirstFileNode(tree) {
  874. // 遍历树的节点
  875. for (let i = 0; i < tree.length; i++) {
  876. const node = tree[i];
  877.  
  878. // 如果节点的类型为 file,则返回该节点
  879. if (node.type === 'file') {
  880. return node;
  881. }
  882.  
  883. // 如果节点有子节点,则递归调用该函数查找子节点中的第一个 file 节点
  884. if (node.children && node.children.length > 0) {
  885. const fileNode = findFirstFileNode(node.children);
  886. if (fileNode) {
  887. return fileNode;
  888. }
  889. }
  890. }
  891.  
  892. // 如果没有找到 file 节点,则返回 null
  893. return null;
  894. }
  895. })();
  896.  
  897. // 修改项目描述的长度
  898. (function() {
  899. 'use strict';
  900. let regs = [/^http:\/\/192\.168\.0\.200\/fe3project\//,
  901. /^http:\/\/192\.168\.0\.200\/frontend_pc\/project\//];
  902. let match = false;
  903.  
  904. for(let r = 0, lr = regs.length; r < lr; r++) {
  905. if(regs[r].test(location.href)) {
  906. match = true;
  907. break;
  908. }
  909. }
  910.  
  911. if(!match) {
  912. return;
  913. }
  914.  
  915. let tryTimes = 6;
  916. let changed = false;
  917.  
  918. // 增加项目描述的输入长度
  919. function modifyTextareaLen() {
  920. if(tryTimes > 0 && !changed) {
  921. setTimeout(() => {
  922. const textarea = document.getElementById('project_description');
  923. tryTimes--;
  924. if(textarea) {
  925. textarea.setAttribute("maxlength", "1000");
  926.  
  927. jQuery(textarea).blur(function(event) {
  928. this.value = this.value.replace(/,/g, ',').replace(/&isfwqfb=1/g, '');
  929. this.value = convertDateFormat(this.value);
  930. jQuery(this).trigger('change');
  931. });
  932. changed = true;
  933. } else {
  934. modifyTextareaLen();
  935. }
  936. }, 1000);
  937. }
  938. }
  939.  
  940. window.onload = function() {
  941. modifyTextareaLen();
  942. }
  943. })();
  944.  
  945. // 设计门户增加通往前端仓库的跳板
  946. (function() {
  947. 'use strict';
  948.  
  949. let regs = [/^https:\/\/oa\.epoint\.com\.cn\/interaction-design-portal\/portal\/pages\/casestemplates\/casetemplatesdetail/,
  950. /^https:\/\/oa\.epoint\.com\.cn\/interaction-design-portal\/portal\/pages\/generalpagetemplates\/generalpagetemplatesdetail/];
  951. let match = false;
  952.  
  953. for(let r = 0, lr = regs.length; r < lr; r++) {
  954. if(regs[r].test(location.href)) {
  955. match = true;
  956. break;
  957. }
  958. }
  959.  
  960. if(!match) {
  961. return;
  962. }
  963.  
  964. function addStyle() {
  965. // 注入样式:增加按钮
  966. 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}";
  967.  
  968.  
  969. // 添加注入样式
  970. let extraStyleElement = document.createElement("style");
  971. extraStyleElement.innerHTML = injectStyle;
  972. document.head.appendChild(extraStyleElement);
  973. }
  974.  
  975. function getUrlParameters() {
  976. var params = {};
  977. var search = window.location.search.substring(1);
  978. var urlParams = search.split('&');
  979.  
  980. for (var i = 0; i < urlParams.length; i++) {
  981. var param = urlParams[i].split('=');
  982. var paramName = decodeURIComponent(param[0]);
  983. var paramValue = decodeURIComponent(param[1] || '');
  984. params[paramName] = paramValue;
  985. }
  986.  
  987. return params;
  988. }
  989.  
  990. window.onload = ()=> {
  991. addStyle();
  992. const $content = jQuery('.content');
  993.  
  994. $content.append('<div class="front-proto">前端原型</div>');
  995.  
  996. const $frontBtn = jQuery('.front-proto', $content);
  997.  
  998. $frontBtn.on('click', ()=> {
  999. window.open('http://192.168.0.200/?name=' + getUrlParameters().guid);
  1000. });
  1001. };
  1002. })();
  1003.  
  1004. // 前端项目案例库增加获取参数的能力
  1005. // 前端项目案例库增加部署能力
  1006. (function() {
  1007. 'use strict';
  1008.  
  1009. let regs = [/^http:\/\/192\.168\.118\.60:9999\/webapp\/pages\/caselib\/create\.html/,
  1010. /^http:\/\/192\.168\.201\.159:9999\/webapp\/pages\/default\/onlinecase.html/];
  1011. let match = false;
  1012.  
  1013. for(let r = 0, lr = regs.length; r < lr; r++) {
  1014. if(regs[r].test(location.href)) {
  1015. match = true;
  1016. break;
  1017. }
  1018. }
  1019.  
  1020. if(!match) {
  1021. return;
  1022. }
  1023.  
  1024. const fontUrl = 'https://element.eleme.io/2.11/static/element-icons.535877f.woff';
  1025.  
  1026. // 添加样式规则,将字体应用到指定元素上
  1027. GM_addStyle(`
  1028. @font-face {
  1029. font-family: element-icons;
  1030. src: url(${fontUrl}) format("woff");
  1031. }
  1032. `);
  1033.  
  1034. GM_addStyle(GM_getResourceText('ElementCSS'));
  1035.  
  1036. const businessType = [
  1037. { Value: '7a20e23c-30b8-47e2-8d8d-f2691c9c63c4', Text: '政务服务' },
  1038. { Value: 'c12150bb-b358-452f-87f0-8a2254df87cb', Text: '政务协同' },
  1039. { Value: '3845804e-de68-421c-9402-7b238cfb5a70', Text: '大数据' },
  1040. { Value: '3c28ee56-f24d-4843-b9a2-93e6b96264f4', Text: '电子交易' },
  1041. { Value: '673b5918-51bc-4f1a-ab73-fca86e54d7d1', Text: '数字建设' },
  1042. { Value: '6d9e7d84-7de3-4e0f-bd4f-ed4722ed25b5', Text: '建筑企业' },
  1043. { Value: 'c22f8d2f-518d-4381-b88c-1da68536ed3a', Text: '公共安全' },
  1044. { Value: 'c5810829-1b21-4b22-85cd-390b1edd9614', Text: '智能设备' },
  1045. { Value: '080c7560-c261-428b-a45d-b86b57b47ffb', Text: '中央研究院' }
  1046. ];
  1047.  
  1048. const projectType = [
  1049. { Value: 'dca44f63-be3f-4e9c-b78f-d786571c22c9', Text: '网站' },
  1050. { Value: 'c7861460-163b-4060-80ec-d60604c50435', Text: '业务系统' },
  1051. { Value: '49accc71-6f7d-43f3-b726-58decf58b6fa', Text: '智能设备' },
  1052. { Value: '90209c65-1a55-4d8c-a836-2e5c6b834ada', Text: '大屏可视化' },
  1053. { Value: 'fb0415fb-65ee-42c1-895a-dca042c2568e', Text: '中屏可视化' },
  1054. { Value: '2b83f9b1-ec78-4819-a400-d7d49ea1ecc5', Text: '其他' }
  1055. ];
  1056.  
  1057. let $businesstype;
  1058. let $projecttype;
  1059.  
  1060. function getUrlParameters() {
  1061. var params = {};
  1062. var search = window.location.search.substring(1);
  1063. var urlParams = search.split('&');
  1064.  
  1065. for (var i = 0; i < urlParams.length; i++) {
  1066. var param = urlParams[i].split('=');
  1067. var paramName = decodeURIComponent(param[0]);
  1068. var paramValue = decodeURIComponent(param[1] || '');
  1069. if(paramName) {
  1070. params[paramName] = paramValue;
  1071. }
  1072. }
  1073.  
  1074. return params;
  1075. }
  1076.  
  1077. function initForm (params) {
  1078. if(typeof params === 'object') {
  1079. document.getElementsByName('Title')[0].value = params.projectName ? params.projectName : '';
  1080. document.getElementsByName('KeyWords')[0].value = params.projectKeys ? params.projectKeys : '';
  1081. document.getElementsByName('Entry')[0].value = params.entryUrl ? params.entryUrl : '';
  1082. document.getElementsByName('SourceCode')[0].value = params.git ? params.git : '';
  1083. }
  1084. }
  1085.  
  1086. let setSuccess = false;
  1087. let setTimes = 5;
  1088.  
  1089. function initSelect(params) {
  1090. if(typeof params !== 'object') {
  1091. return;
  1092. }
  1093.  
  1094. if(setTimes > 0 && !setSuccess) {
  1095. setTimeout(()=> {
  1096. setTimes--;
  1097.  
  1098. businessType.forEach((item)=> {
  1099. if(params.projectBU) {
  1100. if(item.Text === params.projectBU.trim()) {
  1101. $businesstype.val(item.Value);
  1102. } else if( params.projectBU.trim() == '一网统管' || params.projectBU.trim() == '一网协同' || params.projectBU.trim() == '一网通办' ) {
  1103. $businesstype.val('7a20e23c-30b8-47e2-8d8d-f2691c9c63c4');
  1104. }
  1105. $businesstype.trigger("chosen:updated");
  1106. }
  1107. });
  1108.  
  1109. projectType.forEach((item)=> {
  1110. if(params.projectType && item.Text === params.projectType.trim()) {
  1111. $projecttype.val(item.Value);
  1112. $projecttype.trigger("chosen:updated");
  1113. }
  1114. });
  1115.  
  1116. setSuccess = true;
  1117.  
  1118. }, 1000);
  1119. } else {
  1120. initSelect(params);
  1121. }
  1122. }
  1123.  
  1124. function addRelatedDom() {
  1125. const sourceInput = document.getElementsByName('SourceCode')[0];
  1126. const $sourceInput = jQuery(sourceInput);
  1127.  
  1128. $sourceInput.after('<a class="btn" style="cursor: pointer;margin-left:10px;" id="deploy">部署</a><a class="btn hidden" style="cursor: pointer;margin-left:10px" id="view">查看</a>')
  1129. }
  1130.  
  1131. function addStyle() {
  1132. let epointCss = ".el-loading-spinner {margin-top: -50px;} .el-button--primary:focus {outline: 0 !important;}";
  1133. // 添加注入样式
  1134. let extraStyleElement = document.createElement("style");
  1135. extraStyleElement.innerHTML = epointCss;
  1136. document.head.appendChild(extraStyleElement);
  1137. }
  1138.  
  1139. window.onload = ()=> {
  1140. const params = getUrlParameters();
  1141.  
  1142. // 有参数,进行填充表单
  1143. if(params && params.git) {
  1144. $businesstype = jQuery('#businesstype');
  1145. $projecttype = jQuery('#projecttype');
  1146.  
  1147. initForm(params);
  1148. initSelect(params);
  1149.  
  1150. return false;
  1151. }
  1152.  
  1153. // 没有url参数填充,则做部署功能展示
  1154. addRelatedDom();
  1155. addStyle();
  1156.  
  1157. const placeholder = document.createElement('div');
  1158.  
  1159. // 创建 Vue 实例并挂载到页面
  1160. const vueInstance = new Vue({
  1161. el: placeholder,
  1162. data() {
  1163. return {
  1164. framework: [
  1165. {label: '重构模板', value: 1},
  1166. {label: 'f9x1.0', value: 2},
  1167. {label: 'f9x2.0', value: 3},
  1168. {label: 'f950', value: 4},
  1169. {label: 'f950sp1', value: 5},
  1170. {label: 'f950sp2', value: 6},
  1171. {label: 'f950sp3', value: 7},
  1172. {label: 'f951', value: 18},
  1173. {label: 'f940', value: 8},
  1174. {label: 'f941', value: 10},
  1175. {label: 'f942', value: 9},
  1176. {label: 'f934', value: 11},
  1177. {label: 'f933', value: 12},
  1178. {label: 'f932', value: 13},
  1179. {label: 'f9211', value: 14},
  1180. {label: '骨架', value: 15},
  1181. {label: 'Vue', value: 16},
  1182. {label: 'React', value: 17}
  1183. ],
  1184. data: [], // 目录结构树
  1185. defaultProps: {
  1186. children: 'children',
  1187. label: 'label'
  1188. },
  1189. loadingTree: false,
  1190. dialogVisible: false,
  1191. dialogDeployVisible: false,
  1192. showDeployPath: false,
  1193. depDialogVisible: false,
  1194. loading: false,
  1195. formLabelWidth: '120px',
  1196. supportDeploy: false,
  1197. form: {
  1198. frame: '',
  1199. deployPath: ''
  1200. },
  1201. rules: {
  1202. frame: [
  1203. { required: true, message: '请选择框架类型', trigger: 'change' }
  1204. ],
  1205. deployPath: [
  1206. { required: true, message: '请输入部署到 170/showcase 下的目标路径名称', trigger: 'change' }
  1207. ]
  1208. }
  1209. }
  1210. },
  1211. methods: {
  1212. // 部署
  1213. doDeploy() {
  1214. let self = this;
  1215.  
  1216. this.$refs['form'].validate((valid) => {
  1217. if (valid) {
  1218. this.dialogDeployVisible = false;
  1219. this.depDialogVisible = true;
  1220. this.loading = true;
  1221. // 部署
  1222. this.getDeployInfo({ type: '1', frame: this.form.frame, deployPath: this.form.deployPath }, (data)=> {
  1223. this.loading = false;
  1224. this.depDialogVisible = false;
  1225.  
  1226. if(!data.custom.text){
  1227.  
  1228. this.data = data.custom.pageTreeData;
  1229. this.projectFtpUrl = data.custom.projectRootPath;
  1230. this.supportDeploy = data.custom.supportDeploy;
  1231.  
  1232. this.$message({
  1233. type: 'success',
  1234. message: '部署成功!'
  1235. });
  1236. // 打开查看弹窗
  1237. this.viewProject();
  1238. this.toggleViewButton(true);
  1239.  
  1240. } else {
  1241. this.$message({
  1242. type: 'error',
  1243. message: data.custom.text
  1244. });
  1245. }
  1246. });
  1247. } else {
  1248. console.log('error submit!!');
  1249. return false;
  1250. }
  1251. });
  1252. },
  1253. // 项目部署
  1254. getDeployInfo(params, callback) {
  1255. const author = document.querySelector('#account a').innerText.trim().substring(4);
  1256. const projectName = document.getElementsByName('Title')[0].value.trim();
  1257. const projectGitUrl = document.getElementsByName('SourceCode')[0].value.trim();
  1258.  
  1259. if(typeof params == 'function') {
  1260. callback = params;
  1261. params = null;
  1262. }
  1263. if(author && author.length && projectGitUrl) {
  1264. fetch('http://192.168.219.170:3008/api/getDeployInfo', {
  1265. method: 'POST',
  1266. // 允许跨域请求
  1267. mode: 'cors',
  1268. headers: {
  1269. 'Accept': 'application/json',
  1270. 'Content-Type': 'application/json'
  1271. },
  1272. body: JSON.stringify({//post请求参数
  1273. params: {
  1274. "type": (params && params.type !== undefined) ? params.type : '0',// 0 代表查看, 1代表部署
  1275. "deployMan": author, // 部署人姓名
  1276. "projectName": projectName, // 项目名称
  1277. "projectGitUrl": projectGitUrl,
  1278. "frame": (params && params.frame) ? params.frame : undefined,
  1279. "deployPath": (params && params.deployPath) ? params.deployPath : undefined // 部署的目标目录
  1280. }
  1281. })
  1282. })
  1283. .then(response => response.text())
  1284. .then((result) => {
  1285. var data = JSON.parse(result);
  1286. callback && callback(data);
  1287. })
  1288. .catch(error => {
  1289. this.depDialogVisible = false;
  1290. this.dialogVisible = false;
  1291. this.$message({
  1292. type: 'error',
  1293. message: '系统故障,请联系管理员。'
  1294. });
  1295. console.error(error);
  1296. });
  1297. } else {
  1298. this.$message({
  1299. type: 'error',
  1300. message: '本页面不支持查看和部署,请先登录。'
  1301. });
  1302. console.error('部署信息请求参数error');
  1303. }
  1304. },
  1305. // 查看项目
  1306. viewProject () {
  1307. this.dialogVisible = true;
  1308. let self = this;
  1309.  
  1310. if(this.data) {
  1311. this.loadingTree = false;
  1312. } else {
  1313. this.loadingTree = true;
  1314. // 发送ajax请求,查看是否进行过部署
  1315. this.getDeployInfo((data)=> {
  1316. // 有部署信息,直接赋值,
  1317. if(data) {
  1318. self.projectIsDeployed = true;
  1319. self.data = data.custom.pageTreeData;
  1320. self.projectFtpUrl = data.custom.projectRootPath;
  1321. self.supportDeploy = data.custom.supportDeploy;
  1322. self.loadingTree = false;
  1323.  
  1324. self.toggleViewButton(true);
  1325.  
  1326. }
  1327. });
  1328. }
  1329. },
  1330. handleNodeClick(data) {
  1331. console.log(data);
  1332. if(data.type === 'folder') {
  1333. return false;
  1334. }
  1335. var self = this;
  1336. var entry = data.entry;
  1337. self.clickNodeEntry = entry;
  1338. if(self.projectFtpUrl) {
  1339. window.open('http://192.168.219.170' + self.projectFtpUrl + data.entry);
  1340. self.clickNodeEntry = null;
  1341. } else {
  1342. if(this.supportDeploy === false) {
  1343. this.$message({
  1344. type: 'error',
  1345. message: '当前项目暂时只支持查看,请耐心等待功能升级。'
  1346. });
  1347. self.clickNodeEntry = null;
  1348. return false;
  1349. }
  1350.  
  1351. this.$confirm('资源未部署,部署至 170 服务器后可查看,是否部署?')
  1352. .then(_ => {
  1353.  
  1354. this.dialogDeployVisible = true;
  1355. })
  1356. .catch(_ => {});
  1357. }
  1358. },
  1359. // svn 需要制定目录名称,showDeployPath
  1360. showDialog(showDeployPath) {
  1361. this.showDeployPath = showDeployPath;
  1362. this.dialogDeployVisible = true;
  1363. },
  1364. // 查看按钮显影控制
  1365. toggleViewButton(show) {
  1366. const $viewBtn = jQuery('#view');
  1367.  
  1368. if(!$viewBtn.length) {
  1369. return;
  1370. }
  1371.  
  1372. if(show) {
  1373. $viewBtn.removeClass('hidden');
  1374. } else {
  1375. $viewBtn.addClass('hidden');
  1376. }
  1377. }
  1378. },
  1379. mounted() {
  1380. const $btnDeloy = jQuery('#deploy');
  1381. const $btnView = jQuery('#view');
  1382.  
  1383. // 绑定vue组件外的事件
  1384. $btnDeloy.on('click', function() {
  1385. // 源码地址和项目名称判断
  1386. const sourceInput = document.getElementsByName('SourceCode');
  1387. const projectNameInput = document.getElementsByName('Title');
  1388.  
  1389. let sourceInputVal = sourceInput[0].value.trim(),
  1390. projectNameInputVal = projectNameInput[0].value.trim();
  1391.  
  1392. const gitpattern = /^http:\/\/192\.168/;
  1393. const svnpattern = /^svn:\/\/192\.168/;
  1394. const sourcepattern = /^(svn:\/\/192\.168|http:\/\/192\.168)/;
  1395.  
  1396. if (!sourceInputVal) {
  1397. vueInstance.$message({
  1398. type: 'error',
  1399. message: '请输入源码地址!'
  1400. });
  1401. return
  1402. }
  1403.  
  1404. if (!sourcepattern.test(sourceInputVal)) {
  1405. vueInstance.$message({
  1406. type: 'error',
  1407. message: '请输入准确的源码 gitlab 或 svn 地址!'
  1408. });
  1409. return
  1410. }
  1411.  
  1412. if (svnpattern.test(sourceInputVal) && !projectNameInputVal) {
  1413. vueInstance.$message({
  1414. type: 'error',
  1415. message: ' svn 仓库地址部署需要填写项目(案例)名称!'
  1416. });
  1417. return
  1418. }
  1419.  
  1420. vueInstance.showDialog(svnpattern.test(sourceInputVal));
  1421. });
  1422. // 查看项目
  1423. $btnView.on('click', function() {
  1424. vueInstance.viewProject();
  1425. });
  1426.  
  1427. },
  1428. template: `<div id="my-form">
  1429. <el-dialog title="部署提示" width="640px" :visible.sync="dialogDeployVisible">
  1430. <el-alert
  1431. title="此操作将把 GitLab / SVN 资源打包部署至 170 服务器,已部署过的项目会进行覆盖部署,是否继续?"
  1432. type="warning" :closable="false" style="margin-bottom: 20px;">
  1433. </el-alert>
  1434. <el-form :model="form" ref="form" :rules="rules">
  1435. <el-form-item label="框架类型" :label-width="formLabelWidth" prop="frame">
  1436. <el-select v-model="form.frame" placeholder="请框架类型" style="width:380px;">
  1437. <el-option v-for="item in framework" :key="item.value" :label="item.label" :value="item.value"></el-option>
  1438. </el-select>
  1439. </el-form-item>
  1440. <el-form-item label="部署路径" :label-width="formLabelWidth" prop="deployPath" v-if="showDeployPath">
  1441. <el-input v-model="form.deployPath" placeholder="请输入部署至服务器 170/showcase 下的目标路径名称" style="width:380px;"></el-input>
  1442. </el-form-item>
  1443. </el-form>
  1444. <div slot="footer" class="dialog-footer">
  1445. <el-button @click="dialogDeployVisible = false">取 消</el-button>
  1446. <el-button type="primary" @click="doDeploy">确 定</el-button>
  1447. </div>
  1448. </el-dialog>
  1449. <el-dialog
  1450. title="部署"
  1451. width="420px"
  1452. :visible.sync="depDialogVisible">
  1453. <div class="deploy-body"
  1454. style="height: 306px;"
  1455. v-loading="loading"
  1456. element-loading-text="正在打包部署至 170 服务器,请耐心等待"
  1457. element-loading-spinner="el-icon-loading">
  1458. </div>
  1459. </el-dialog>
  1460. <el-dialog
  1461. title="目录结构"
  1462. width="900px"
  1463. :append-to-body="true"
  1464. :visible.sync="dialogVisible">
  1465. <el-tree
  1466. class="filter-tree"
  1467. :data="data"
  1468. :props="defaultProps"
  1469. node-key="id"
  1470. default-expand-all
  1471. @node-click="handleNodeClick"
  1472. v-loading="loadingTree"
  1473. element-loading-background="rgba(255, 255, 255, 1)"
  1474. element-loading-text="拼命加载中......"
  1475. ref="tree">
  1476. </el-tree>
  1477. </el-dialog>
  1478. </div>`
  1479. });
  1480.  
  1481. // 将占位元素追加到 body 元素中
  1482. jQuery('.form-container').after(vueInstance.$el);
  1483. };
  1484. })();
  1485.  
  1486. // 新建项目提示
  1487. (function() {
  1488. 'use strict';
  1489. let regs = [/^http:\/\/192\.168\.0\.200\/projects\/new/];
  1490. let match = false;
  1491.  
  1492. for(let r = 0, lr = regs.length; r < lr; r++) {
  1493. if(regs[r].test(location.href)) {
  1494. match = true;
  1495. break;
  1496. }
  1497. }
  1498.  
  1499. if(!match) {
  1500. return;
  1501. }
  1502.  
  1503. // 等待页面加载完成
  1504. window.addEventListener('load', function() {
  1505. const $projectName = jQuery('#project_name');
  1506. const $projectDescription = jQuery('#project_description');
  1507. const $check = jQuery('#project_visibility_level_10');
  1508.  
  1509. // 页面参数
  1510. const p = gitlabUtil.getUrlParameters();
  1511. const projectName = p.projectName;
  1512. const projectBu = p.bu;
  1513. const projectType = p.projectType;
  1514. const frame = p.frame;
  1515. let uiUrl = p.uiUrl;
  1516. if(uiUrl) {
  1517. uiUrl += '=' + location.href.substr(location.href.length - 50, 36);
  1518. }
  1519.  
  1520. if($projectName && $projectName.length) {
  1521. $projectName.attr('placeholder', '以需求或项目管理系统中的项目名称为准');
  1522. if(projectName) {
  1523. $projectName.val(projectName);
  1524. $projectName.trigger('change');
  1525. }
  1526. }
  1527.  
  1528. if($projectDescription && $projectDescription.length) {
  1529. let date = new Date();
  1530. const imgEl = document.querySelector('.header-user-avatar');
  1531. let name = imgEl ? imgEl.getAttribute('alt') : '张三';
  1532. $projectDescription.attr('placeholder', 'YYYY-M-D,张三,UI:https://oa.epoint.com.cn/interaction-design-portal/portal/pages/casestemplates/casetemplatesdetail?guid=');
  1533. $projectDescription.val(date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ','+ name +',UI:' + (uiUrl ? uiUrl : 'https://oa.epoint.com.cn/interaction-design-portal/portal/pages/casestemplates/casetemplatesdetail?guid='));
  1534. $projectDescription.prev('.label-bold').find('span').text(' (UI备份地址完善后会自动获取项目名称、业务条线和项目类型)')
  1535.  
  1536. // 增加主题表单
  1537. var themeHtml = '<div class="row">\
  1538. <div class="form-group col-md-9">\
  1539. <label class="label-bold" for="project_topics">主题<i style="color:red; font-style:normal; font-weight: 400; padding-left: 5px;">业务系统类型关键字添加框架版本</i></label>\
  1540. <input value="" maxlength="2000" class="form-control gl-form-input" placeholder="业务条线(政务服务|一网协同...),项目类型(网站|业务系统|中屏可视化...),关键字(f950sp2|gojs|vue...)" size="2000" type="text" name="project[topics]" id="project_topics" data-is-dirty-submit-input="true" data-dirty-submit-original-value="">\
  1541. <p class="form-text text-muted">用逗号分隔主题。</p>\
  1542. <p class="form-text text-muted">业务条线:政务服务、大数据、一网统管、一网协同、智能设备、电子交易、数字建设、公共安全、支撑产品;</p>\
  1543. <p class="form-text text-muted">项目类型:网站、业务系统、智能设备、大屏可视化、中屏可视化、在线表单;</p>\
  1544. <p class="form-text text-muted">关键字:f942f951f950sp2f9x2.0f9x1.0、骨架、vuereact、单页、gojselementAntDesign、全景、关系图、流程图、响应式、svg、视频、miniuiegisegis3d、高德、百度、超图、three、主题、瀑布流、上传、自定义用户控件等;</p>\
  1545. </div></div>';
  1546.  
  1547. $projectDescription.closest('.form-group').after(themeHtml);
  1548.  
  1549. var $theme = jQuery('#project_topics');
  1550.  
  1551. // 规避中文,
  1552. $projectDescription.blur(function(event) {
  1553. this.value = this.value.replace(/,/g, ',').replace(/&isfwqfb=1/g, '');
  1554. // 日期转换
  1555. this.value = convertDateFormat(this.value);
  1556.  
  1557. // 检测主题是否为空,且是否已输入uiUrl
  1558. if ($theme.val().trim() == '') {
  1559. var regex = /guid=([0-9a-fA-F-]{36})/; // 匹配整个URL字符串中的guid后的36位字符
  1560.  
  1561. var match = this.value.match(regex);
  1562.  
  1563. if (match) {
  1564. var extractedGuid = match[1];
  1565. console.log(extractedGuid);
  1566. // 通过 guid 请求数据
  1567.  
  1568. // 获取项目信息
  1569. GM_xmlhttpRequest({
  1570. method: 'GET',
  1571. url: 'https://oa.epoint.com.cn/interaction-design-portal/rest/portal/pages/casestemplates/casestemplatesdetailaction/page_load?guid='+ extractedGuid +'&isCommondto=true',
  1572. headers: {
  1573. 'Content-Type': 'application/json; charset=utf-8'
  1574. },
  1575. onload: function(res) {
  1576. const data = JSON.parse(res.response);
  1577. let proBu = data.custom.casetemplatedata.stripline;
  1578. const proType = data.custom.casetemplatedata.type;
  1579. const proName = data.custom.casetemplatedata.project;
  1580. switch(proBu) {
  1581. case '政务BG':
  1582. proBu = '政务服务';
  1583. break;
  1584. case '建设BG':
  1585. proBu = '数字建设';
  1586. break;
  1587. case '交易BG':
  1588. proBu = '电子交易';
  1589. break;
  1590. case '智能设备BU':
  1591. proBu = '智能设备';
  1592. break;
  1593. }
  1594. proName && $projectName.val(proName);
  1595. proBu && proType && $theme.val(proBu + ', ' + proType);
  1596. $projectName.trigger('change');
  1597. }
  1598. });
  1599. }
  1600. }
  1601. });
  1602.  
  1603.  
  1604. if(projectBu) {
  1605. $theme.val(projectBu);
  1606. }
  1607.  
  1608. if(projectType) {
  1609. $theme.val($theme.val() + ', ' + projectType);
  1610. }
  1611.  
  1612. if(frame) {
  1613. $theme.val($theme.val() + ', ' + frame);
  1614. }
  1615.  
  1616. $theme.blur(function(event) {
  1617. this.value = this.value.replaceAll(',', ',');
  1618.  
  1619. });
  1620. }
  1621.  
  1622. if($check && $check.length) {
  1623. $check.trigger('click');
  1624. }
  1625. });
  1626.  
  1627. })();
  1628.  
  1629. // 通过 oa 首页中转,跳过跨站访问的限制
  1630. (function(){
  1631. 'use strict';
  1632. let regs = [/^https:\/\/oa\.epoint\.com\.cn\/epointoa9\/frame\/fui\/pages\/themes\/aide/];
  1633. let match = false;
  1634.  
  1635. for(let r = 0, lr = regs.length; r < lr; r++) {
  1636. if(regs[r].test(location.href)) {
  1637. match = true;
  1638. break;
  1639. }
  1640. }
  1641.  
  1642. if(!match) {
  1643. return;
  1644. }
  1645.  
  1646. let redirectUlr = gitlabUtil.getUrlParameters().redirect;
  1647.  
  1648. if(redirectUlr) {
  1649. window.location.href = redirectUlr;
  1650. }
  1651. })();
  1652.  
  1653. // 设计门户瀑布流页面,查找对应项目
  1654. (function(){
  1655. 'use strict';
  1656. let regs = [/^https:\/\/oa\.epoint\.com\.cn\/interaction-design-portal\/portal\/pages\/casestemplates\/casetemplateslist/];
  1657. let match = false;
  1658.  
  1659. for(let r = 0, lr = regs.length; r < lr; r++) {
  1660. if(regs[r].test(location.href)) {
  1661. match = true;
  1662. break;
  1663. }
  1664. }
  1665.  
  1666. if(!match) {
  1667. return;
  1668. }
  1669.  
  1670. let projectName = location.href.split('projectname');
  1671.  
  1672. window.addEventListener('load', function() {
  1673. if(projectName[1]) {
  1674. projectName = decodeURIComponent(projectName[1]);
  1675.  
  1676. const $input = jQuery('#search-input');
  1677. const $searchBtn = jQuery('.search-icon');
  1678.  
  1679. setTimeout(() => {
  1680. $input.val(projectName);
  1681. $searchBtn.trigger('click');
  1682. }, 1000);
  1683. }
  1684. });
  1685. })();
  1686.  
  1687. // 邮件详情页可以创建项目
  1688. (function() {
  1689. 'use strict';
  1690. let regs = [/^https:\/\/oa\.epoint\.com\.cn\:8080\/OA9\/oa9\/mail\/mailreceivedetail/];
  1691. let match = false;
  1692.  
  1693. for(let r = 0, lr = regs.length; r < lr; r++) {
  1694. if(regs[r].test(location.href)) {
  1695. match = true;
  1696. break;
  1697. }
  1698. }
  1699.  
  1700. if(!match) {
  1701. return;
  1702. }
  1703.  
  1704. const fontUrl = 'https://element.eleme.io/2.11/static/element-icons.535877f.woff';
  1705.  
  1706. // 添加样式规则,将字体应用到指定元素上
  1707. GM_addStyle(`
  1708. @font-face {
  1709. font-family: element-icons;
  1710. src: url(${fontUrl}) format("woff");
  1711. }
  1712. `);
  1713.  
  1714. GM_addStyle(GM_getResourceText('ElementCSS'));
  1715.  
  1716. // 在邮件签收情况后添加按钮-新建项目
  1717. function initCreate() {
  1718. const title = document.querySelector('#mail-detail-container .dtt');
  1719. let vueInstance;
  1720. let gitUrl = 'http://192.168.0.200/projects/new?namespace_id=4817';
  1721.  
  1722. if(title) {
  1723. const btnPlaceholder = document.createElement('div');
  1724. const tds = jQuery('#mailcontent table').find("td[colspan='3']");
  1725. const projectName = tds.eq(0).text().replace('(临时)', '').replace('(临时)', '');
  1726. const isFrontEndEmail = jQuery('#mailcontent table').length;
  1727. const bu = '政务服务';
  1728. const projectType = tds.eq(4).text();
  1729. const frame = tds.eq(9).text();
  1730. const demandUrl = tds.eq(1).text();
  1731. let uiUrl = tds.eq(7).text();
  1732. const backGuid = extractGuidFromUrl(uiUrl);
  1733. uiUrl = uiUrl ? removeQueryStringParameter(uiUrl, 'isfwqfb') : '';
  1734.  
  1735. if(!isFrontEndEmail) {
  1736. return;
  1737. }
  1738.  
  1739. // 创建 Vue 实例并挂载到页面
  1740. vueInstance = new Vue({
  1741. el: btnPlaceholder,
  1742. data: function() {
  1743. return {
  1744. proBu: bu,
  1745. proType: projectType
  1746. };
  1747. },
  1748. methods: {
  1749. create: function() {
  1750. window.open(gitUrl + '&projectName=' + projectName + '&bu=' + this.proBu + '&projectType=' + this.proType + '&frame=' + frame + '&uiUrl=' + uiUrl + '#blank_project');
  1751. },
  1752. search: function() {
  1753. window.open('http://192.168.0.200/?name=' + projectName);
  1754. }
  1755. },
  1756. mounted: function() {
  1757. let self = this;
  1758. // 获取项目信息
  1759. GM_xmlhttpRequest({
  1760. method: 'GET',
  1761. url: 'https://oa.epoint.com.cn/interaction-design-portal/rest/portal/pages/casestemplates/casestemplatesdetailaction/page_load?guid='+ backGuid +'&isCommondto=true',
  1762. headers: {
  1763. 'Content-Type': 'application/json; charset=utf-8'
  1764. },
  1765. onload: function(res) {
  1766. const data = JSON.parse(res.response);
  1767. self.proBu = data.custom.casetemplatedata.stripline;
  1768. self.proType = data.custom.casetemplatedata.type;
  1769. switch(self.proBu) {
  1770. case '政务BG':
  1771. self.proBu = '政务服务';
  1772. break;
  1773. case '建设BG':
  1774. self.proBu = '数字建设';
  1775. break;
  1776. case '交易BG':
  1777. self.proBu = '电子交易';
  1778. break;
  1779. case '智能设备BU':
  1780. self.proBu = '智能设备';
  1781. break;
  1782. }
  1783. }
  1784. });
  1785. },
  1786. template: `<div style="display:inline-block; margin-left: 5px; vertical-align: top;">
  1787. <el-tooltip content="去 GitLab 创建项目" placement="top" effect="light">
  1788. <el-button type="primary" style="vertical-align: top;" icon="el-icon-folder-add" circle size="mini" id="create-project" @click="create"></el-button>
  1789. </el-tooltip>
  1790. <el-tooltip content="去 GitLab 查找项目" placement="top" effect="light">
  1791. <el-button type="primary" style="vertical-align: top;" icon="el-icon-search" circle size="mini" @click="search"></el-button>
  1792. </el-tooltip>
  1793. </div>`
  1794. });
  1795.  
  1796. // 将占位元素追加到 邮件标题后 元素中
  1797. // f9 框架会冲突
  1798. title.appendChild(vueInstance.$el);
  1799. // jQuery(title).append(vueInstance.$el.outerHTML);
  1800.  
  1801. /*
  1802. jQuery('#create-project').on('click', function() {
  1803. window.open(gitUrl);
  1804. });*/
  1805. }
  1806. }
  1807.  
  1808. function extractGuidFromUrl(url) {
  1809. const regex = /[?&]guid=([^&]+)/;
  1810. const match = url.match(regex);
  1811.  
  1812. if (match) {
  1813. return match[1]; // 第一个捕获组中的值即为 guid
  1814. } else {
  1815. return null; // 如果没有匹配到 guid,则返回 null
  1816. }
  1817. }
  1818.  
  1819. function removeQueryStringParameter(url, parameterName) {
  1820. const urlParts = url.split('?');
  1821.  
  1822. if (urlParts.length === 2) {
  1823. const baseUrl = urlParts[0];
  1824. const queryString = urlParts[1];
  1825.  
  1826. const parameters = queryString.split('&').filter(param => {
  1827. const paramName = param.split('=')[0];
  1828. return paramName !== parameterName;
  1829. });
  1830.  
  1831. if (parameters.length > 0) {
  1832. return baseUrl + '?' + parameters.join('&');
  1833. } else {
  1834. return baseUrl;
  1835. }
  1836. }
  1837.  
  1838. return url;
  1839. }
  1840.  
  1841. window.addEventListener('load', function() {
  1842. setTimeout(function() {
  1843. initCreate();
  1844. }, 500);
  1845. });
  1846.  
  1847. })();