GitLab Assistant

GitLab Viewer Publish and Deploy Project!

当前为 2023-10-12 提交的版本,查看 最新版本

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