GitLab Assistant

GitLab Viewer Publish and Deploy Project!

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

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