GitLab Assistant

GitLab Viewer Publish and Deploy Project!

当前为 2024-04-18 提交的版本,查看 最新版本

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