GitLab Assistant

GitLab Viewer Publish and Deploy Project!

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

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