Merge pull request '修改cloudcloudIDE加载时的logo,升级插件版本' (#432) from tongChong/forgeplus-react:feature_IDE into gitlink_server

This commit is contained in:
tongChong 2022-08-17 16:06:45 +08:00
commit 2b67a187ea
85 changed files with 42768 additions and 153 deletions

View File

@ -83,6 +83,11 @@ module.exports = {
devtoolModuleFilenameTemplate: (info) =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, "/"),
},
externals: {
"react": "React",
"react-dom": "ReactDOM",
"alex": "Alex",
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"

View File

@ -71,6 +71,11 @@ module.exports = {
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, "/"),
},
externals: {
'alex': 'Alex',
"react": "React",
"react-dom": "ReactDOM",
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"

137
package-lock.json generated
View File

@ -4,6 +4,15 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@ahooksjs/use-request": {
"version": "2.8.15",
"resolved": "https://registry.npmjs.org/@ahooksjs/use-request/-/use-request-2.8.15.tgz",
"integrity": "sha512-xhVaM4fyIiAMdVFuuU5i3CFUdFa/IblF+fvITVMFaUEO3w/V5tVCAF6WIA3T03n1/RPuzRkA7Ao1PFtSGtGelw==",
"requires": {
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1"
}
},
"@ant-design/colors": {
"version": "3.2.2",
"resolved": "https://registry.npm.taobao.org/@ant-design/colors/download/@ant-design/colors-3.2.2.tgz?cache=0&sync_timestamp=1612935637470&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Fcolors%2Fdownload%2F%40ant-design%2Fcolors-3.2.2.tgz",
@ -415,6 +424,11 @@
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.0.0.tgz",
"integrity": "sha512-q95SP4FdkmF0CwO0F2q0H6ZgudsApaY/yCtAQNRn1gduef5fGpyEphzy0YCq/N0UFvDSnLg5V8jFK/YGXlDiCw=="
},
"@types/js-cookie": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
"integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA=="
},
"@types/jss": {
"version": "9.5.8",
"resolved": "https://registry.npmjs.org/@types/jss/-/jss-9.5.8.tgz",
@ -817,6 +831,23 @@
}
}
},
"ahooks": {
"version": "2.10.14",
"resolved": "https://registry.npmjs.org/ahooks/-/ahooks-2.10.14.tgz",
"integrity": "sha512-axWa7VoAgu7bxA56dDl0CXW4rvaQmDBiov/d3tAy0x1YNYywYMKokL8TdLgJ5zO/oXGiWmG7BxlGOQGkqE/zkQ==",
"requires": {
"@ahooksjs/use-request": "^2.8.14",
"@types/js-cookie": "^2.2.6",
"dayjs": "^1.9.1",
"intersection-observer": "^0.7.0",
"js-cookie": "^2.2.1",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1",
"resize-observer-polyfill": "^1.5.1",
"screenfull": "^5.0.0"
}
},
"ajv": {
"version": "6.12.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
@ -4516,6 +4547,11 @@
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.12.0.tgz",
"integrity": "sha512-qJgn99xxKnFgB1qL4jpxU7Q2t0LOn1p8KMIveef3UZD7kqjT3tpFNNdXJelEHhE+rUgffriXriw/sOSU+cS1Hw=="
},
"dayjs": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz",
"integrity": "sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A=="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
@ -4854,7 +4890,7 @@
},
"dom-closest": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz",
"resolved": "https://registry.npm.taobao.org/dom-closest/download/dom-closest-0.2.0.tgz",
"integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=",
"requires": {
"dom-matches": ">=1.0.1"
@ -4898,7 +4934,7 @@
},
"dom-matches": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz",
"resolved": "https://registry.npm.taobao.org/dom-matches/download/dom-matches-2.0.0.tgz",
"integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw="
},
"dom-scroll-into-view": {
@ -5150,7 +5186,7 @@
},
"enquire.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
"resolved": "https://registry.npm.taobao.org/enquire.js/download/enquire.js-2.1.6.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fenquire.js%2Fdownload%2Fenquire.js-2.1.6.tgz",
"integrity": "sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ="
},
"entities": {
@ -5675,7 +5711,7 @@
},
"eventlistener": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/eventlistener/-/eventlistener-0.0.1.tgz",
"resolved": "https://registry.npm.taobao.org/eventlistener/download/eventlistener-0.0.1.tgz",
"integrity": "sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg="
},
"events": {
@ -7209,8 +7245,7 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"optional": true
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"aproba": {
"version": "1.2.0",
@ -7231,14 +7266,12 @@
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"optional": true
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -7253,20 +7286,17 @@
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"optional": true
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"optional": true
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"optional": true
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"core-util-is": {
"version": "1.0.2",
@ -7383,8 +7413,7 @@
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"optional": true
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
"version": "1.3.5",
@ -7396,7 +7425,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -7411,7 +7439,6 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -7419,14 +7446,12 @@
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"optional": true
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"minipass": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -7445,7 +7470,6 @@
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
"integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
@ -7507,8 +7531,7 @@
"npm-normalize-package-bin": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
"optional": true
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
},
"npm-packlist": {
"version": "1.4.8",
@ -7536,8 +7559,7 @@
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"optional": true
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"object-assign": {
"version": "4.1.1",
@ -7549,7 +7571,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"optional": true,
"requires": {
"wrappy": "1"
}
@ -7627,8 +7648,7 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"optional": true
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
@ -7664,7 +7684,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -7684,7 +7703,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -7728,14 +7746,12 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"optional": true
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
}
}
},
@ -8009,7 +8025,7 @@
},
"hammerjs": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
"resolved": "https://registry.npm.taobao.org/hammerjs/download/hammerjs-2.0.8.tgz",
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
},
"handle-thing": {
@ -8823,7 +8839,7 @@
},
"image-size": {
"version": "0.5.5",
"resolved": "http://173.15.15.82:8081/repository/npm-all/image-size/-/image-size-0.5.5.tgz",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
"optional": true
},
@ -8842,7 +8858,7 @@
},
"immutable": {
"version": "3.7.6",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
"resolved": "https://registry.npm.taobao.org/immutable/download/immutable-3.7.6.tgz",
"integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks="
},
"import-fresh": {
@ -9056,6 +9072,11 @@
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
"integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw=="
},
"intersection-observer": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.7.0.tgz",
"integrity": "sha512-Id0Fij0HsB/vKWGeBe9PxeY45ttRiBmhFyyt/geBdDHBYNctMRTE3dC1U3ujzz3lap+hVXlEcVaB56kZP/eEUg=="
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@ -9931,6 +9952,11 @@
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
"integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ=="
},
"js-cookie": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
"integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ=="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -10287,9 +10313,15 @@
}
},
"less": {
<<<<<<< HEAD
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz",
"integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==",
=======
"version": "3.13.1",
"resolved": "http://173.15.15.82:8081/repository/npm-all/less/-/less-3.13.1.tgz",
"integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==",
>>>>>>> 5fbac9d61caef2b1bf07294acae99fc74a2ebe1a
"requires": {
"copy-anything": "^2.0.1",
"errno": "^0.1.1",
@ -10302,10 +10334,17 @@
"tslib": "^1.10.0"
},
"dependencies": {
<<<<<<< HEAD
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
=======
"make-dir": {
"version": "2.1.0",
"resolved": "http://173.15.15.82:8081/repository/npm-all/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
>>>>>>> 5fbac9d61caef2b1bf07294acae99fc74a2ebe1a
"optional": true,
"requires": {
"pify": "^4.0.1",
@ -10322,7 +10361,7 @@
},
"less-loader": {
"version": "4.1.0",
"resolved": "http://173.15.15.82:8081/repository/npm-all/less-loader/-/less-loader-4.1.0.tgz",
"resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz",
"integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==",
"requires": {
"clone": "^2.1.1",
@ -10332,7 +10371,7 @@
"dependencies": {
"pify": {
"version": "3.0.0",
"resolved": "http://173.15.15.82:8081/repository/npm-all/pify/-/pify-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="
}
}
@ -10479,7 +10518,7 @@
},
"lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"resolved": "https://registry.npm.taobao.org/lodash.throttle/download/lodash.throttle-4.1.1.tgz",
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
},
"lodash.uniq": {
@ -16167,6 +16206,11 @@
"ajv-keywords": "^3.4.1"
}
},
"screenfull": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz",
"integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA=="
},
"scroll": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/scroll/-/scroll-2.0.3.tgz",
@ -16400,6 +16444,15 @@
"safe-buffer": "^5.0.1"
}
},
"sha1": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz",
"integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==",
"requires": {
"charenc": ">= 0.0.1",
"crypt": ">= 0.0.1"
}
},
"shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",

View File

@ -6,6 +6,7 @@
"@monaco-editor/react": "^2.3.0",
"@novnc/novnc": "^1.1.0",
"actioncable": "^5.2.4-3",
"ahooks": "^2.10.14",
"antd": "^3.26.15",
"array-flatten": "^2.1.2",
"autoprefixer": "7.1.6",
@ -110,6 +111,7 @@
"sass-loader": "7.3.1",
"save-dev": "0.0.1-security",
"scroll-into-view": "^1.14.2",
"sha1": "^1.1.1",
"showdown": "^1.9.1",
"showdown-katex": "^0.8.0",
"slick-carousel": "^1.8.1",

View File

@ -1,31 +1,38 @@
<!DOCTYPE html>
<html lang="zh-CN" class="notranslate translated-ltr" translate="no">
<head>
<meta charset="utf-8">
<meta name=”Keywords” Content=”trustie,trustieforge,forge,确实让创建更美好,协同开发平台″>
<meta name="Keywords" Content="gitLink,GitLink,gitlink,trustie,trustieforge,forge,确实让创建更美好,协同开发平台">
<meta name=”Keywords” Content=”TrustieOpenSourceProject″>
<meta name=”Keywords” Content=”issue,bug,tracker,软件工程,课程实践″>
<meta name=”Description” Content=”持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动”>
<meta name="theme-color" content="#000000">
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link href="https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.13/bundle/alex.all.global.min.css" rel="stylesheet"/>
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/merge.css">
<%= htmlWebpackPlugin.tags.headTags %>
</head>
<body>
<!--用于markdown转html -->
<div id="md_div" style="display: none;"></div>
<div id="root" class="page -layout-v -fit widthunit"></div>
<div id="picture_display" style="display: none;"></div>
<script src="%PUBLIC_URL%js/react.development.js"></script>
<script src="%PUBLIC_URL%js/react-dom.development.js"></script>
<script src="%PUBLIC_URL%js/jquery-1.8.3.min.js"></script>
<script src="%PUBLIC_URL%js/js_min_all.js"></script>
<script src="%PUBLIC_URL%js/codemirror/codemirror.js"></script>
<script src="%PUBLIC_URL%js/editormd/editormd.min.js"></script>
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>
<script src="https://gw.alipayobjects.com/os/lib/moment/2.29.4/moment.js"></script>
<script src="https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.13/bundle/alex.all.global.min.js"></script>
<%= htmlWebpackPlugin.tags.bodyTags %>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@ if (isDev) {
}
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'student'
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
}
window._debugType = debugType;
export function initAxiosInterceptors(props) {

View File

@ -118,7 +118,7 @@ export function timeAgo(backDate) {
var days = Math.floor(time / (1000 * 60 * 60 * 24));
var hours = Math.floor((time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((time % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((time % (1000 * 60 * 60)) / 1000);
// var seconds = Math.floor((time % (1000 * 60 * 60)) / 1000);
if (time <= 0) {
return "刚刚";
}
@ -129,10 +129,10 @@ export function timeAgo(backDate) {
return hours + "小时前";
}
if (minutes) {
return minutes + "分前";
}
if (seconds) {
return seconds + "秒前";
return minutes + "分钟前";
}
// if (seconds) {
// return seconds + "秒前";
// }
return "刚刚";
}

View File

@ -25,4 +25,18 @@ export function IEVersion(){
}else{
return -1;//不是ie浏览器
}
}
}
export function windowsOrMac(){
let agent = navigator.userAgent.toLowerCase();
let isMac = /macintosh|mac os x/i.test(navigator.userAgent);
if(agent.indexOf("win32") >= 0 || agent.indexOf("wow32") >= 0){
return 'win32';
}
if(agent.indexOf("win64") >= 0 || agent.indexOf("wow64") >= 0){
return 'win64';
}
if(isMac){
return 'mac';
}
}

View File

@ -11,14 +11,28 @@ export function getImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
path && !path.startsWith('/') && !path.startsWith('http') && (path = '/'.concat(path));
path && !path.startsWith('/') && !path.startsWith('http') && (path = '/'.concat(path));
const local = 'https://testforgeplus.trustie.net';
if (isDev) {
return `${local}/${path}`
return `${local}${path}`
}
return `${path}`;
}
export function getImageUrlAbsolute(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
path && !path.startsWith('/') && !path.startsWith('http') && (path = '/'.concat(path));
const local = 'https://testforgeplus.trustie.net';
const prod = window.location.origin;
if (isDev) {
return `${local}${path}`
}else{
return `${prod}${path}`;
}
}
export function numFormat(num, digits){
let d = digits || 1;
var si = [

View File

@ -4,7 +4,7 @@
export {
getUploadLogoActionUrl as getUploadLogoActionUrl,numFormat as numFormat,
getImageUrl as getImageUrl,getImage as getImage, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
getImageUrl as getImageUrl,getImageUrlAbsolute as getImageUrlAbsolute,getImage as getImage, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrltwo as getUploadActionUrltwo, getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
, getTaskUrlById as getTaskUrlById, TEST_HOST, htmlEncode as htmlEncode, getupload_git_file as getupload_git_file, getcdnImageUrl as getcdnImageUrl,
turnbar,returnbar
@ -78,3 +78,5 @@ export { default as ImageLayer2 } from './hooks/ImageLayer2'
// 外部
export { CNotificationHOC as CNotificationHOC } from '../modules/courses/common/CNotificationHOC'
export { IEVersion as IEVersion , windowsOrMac as windowsOrMac} from './IEVersion'

View File

@ -135,7 +135,7 @@ class Activity extends Component{
const fourth_per =issues_count > 0 ?`${parseFloat(open_issues_count/issues_count).toFixed(2)*100}%` :"50%";
return(
<div className="main">
<div className="main mt20">
<div className="normalBox">
<div class="normalBox-title">概览</div>

View File

@ -3,29 +3,22 @@ import { AlignCenter } from '../layout';
import { Button } from 'antd';
import Modals from '../PublicModal/Index';
function DeleteBox({
visible ,
onCancel ,
onSuccess ,
title ,
subTitle,
content
}) {
function DeleteBox({visible, onCancel, onSuccess, title, subTitle, content}) {
return(
<Modals
title={title}
<Modals
title={title}
btn={
<div>
<Button size={'large'} onClick={onCancel}>取消</Button>
<Button type={"danger"} size={"large"} onClick={onSuccess}>确认删除</Button>
</div>
}
onCancel={onCancel}
}
onCancel={onCancel}
visible={visible}
>
<div className="desc">
<AlignCenter className="descMain"><i className="iconfont icon-shanchu_tc_icon mr10"></i>{content}</AlignCenter>
<p className="task-hide-2" style={{WebkitLineClamp:5}}>删除后未来事件将不会推送至此Webhook地址<span title={subTitle}>{subTitle}</span></p>
<AlignCenter className="descMain"><i className="iconfont icon-shanchu_tc_icon mr10 font-36" style={{color: '#ca0002'}}></i>{content}</AlignCenter>
<p className="task-hide-2" style={{WebkitLineClamp:5}}><span title={subTitle}>{subTitle}</span></p>
</div>
</Modals>
)

View File

@ -1,7 +1,7 @@
import React from 'react';
import React, {useEffect} from 'react';
import { WhiteBack } from '../Component/layout';
import './ops.scss';
import devops from '../Images/devops.png';
import { Route, Switch } from 'react-router-dom';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
@ -32,11 +32,49 @@ const Params = Loadable({
})
export default ((props)=>{
const {jianmu_devops, isManager, project} = props;
useEffect(() => {
window.addEventListener("message", iframeHeight, false);
return () => {
window.removeEventListener("message", iframeHeight, false);
}
})
function iframeHeight(e){
if (e && e.data && typeof(e.data) === "string") {
let myHeight = JSON.parse(e.data);
if (document.querySelector("#devopsIframe")) {
document.querySelector("#devopsIframe").height = myHeight.height;
}
}
}
function iframeLoad() {
try {
let myIframe = document.getElementById("devopsIframe");
if (myIframe.contentDocument) {
myIframe.height = myIframe.contentDocument.querySelector('.el-main').clientHeight + 260;
}
} catch (err) {
console.error(err);
}
}
return(
<WhiteBack className="opsPanel">
<Switch {...props}>
<WhiteBack className={`opsPanel ${isManager ? 'main' : ''}`}>
{/* 嵌入devops */}
{jianmu_devops && project && isManager && <iframe title={`devopsIframe`} src={`${project.jianmu_devops_url}/oauth2/authorize?code=${jianmu_devops}`} id={`devopsIframe`} frameBorder="0" name={`devopsIframe`} width="100%" onLoad={iframeLoad} height={'auto'}></iframe>}
{!isManager && <div className='nullJurisdictionBox'>
<div className='jurTil font-16'>引擎配置</div>
<div className='jurCont mt25'>
<img src={devops} alt="" width={110}/>
<div className='font-18 mt30'>暂无权限仅仓库管理员可访问</div>
</div>
</div>}
{/* 旧引擎页面 */}
{/* <Switch {...props}>
<Route path="/:owner/:projectsId/devops/params"
render={
(p) => (<Params {...props} {...p}/>)
@ -52,7 +90,6 @@ export default ((props)=>{
(p) => (<New {...props} {...p}/>)
}
></Route>
<Route path="/:owner/:projectsId/devops/list/:branch"
render={
(p) => (<Stucture {...props} {...p}/>)
@ -63,14 +100,14 @@ export default ((props)=>{
(p) => (<New {...props} {...p}/>)
}
></Route>
{/* 原本的两种合为一个 */}
//
<Route path="/:owner/:projectsId/devops"
render={
// (p) =>{return( p.location && p.location.state && p.location.state.open_devops?<Dispose {...props} {...p}/>:<About {...props} {...p}/>)}
(p) =>{return(<About {...props} {...p}/>)}
}
></Route>
</Switch>
</Switch> */}
</WhiteBack>
)
})

View File

@ -2,6 +2,7 @@
{
margin:20px auto;
width: 1200px;
min-height: 500px;
// 开始激活页面
.activatePanel{
display: flex;
@ -521,4 +522,22 @@
border:1px solid #999;
color:#999 ;
}
}
.nullJurisdictionBox{
color:#333333;
.jurTil{
width:1200px;
padding: 15px 16px;
background-color:#fafcff;
border:1px solid rgba(42, 97, 255, 0.23);
border-radius:3px 3px 0px 0px;
}
.jurCont{
width:1200px;
height:317px;
padding-top: 45px;
background-color:#fafcff;
border-radius:4px 4px 0px 0px;
text-align: center;
}
}

BIN
src/forge/Images/devops.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/forge/Images/img1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -333,6 +333,8 @@ function CoderDepot(props){
}
}
let n = fileInfo && fileInfo.name;
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
@ -443,6 +445,7 @@ function CoderDepot(props){
issuesFlag &&
<a onClick={createIssue}>+ 疑修</a>
}
</div>
{ fileOperate &&
<Dropdown

View File

@ -14,7 +14,7 @@ function CoderDepotPath({treeValuePath , returnUlr , returnMain , getPathUrl , i
</a>
{treeValuePath.map((item, key) => {
return (
<React.Fragment>
<React.Fragment key={key}>
{
key === treeValuePath.length-1 ?
<span className="color-grey-6 subFileName" key={key}>{returnbar(item)}</span>

View File

@ -150,6 +150,10 @@ const Invite = Loadable({
loader: () => import('../Invite/Index'),
loading: Loading,
});
const Review = Loadable({
loader: () => import('../Newfile/codeReview'),
loading: Loading,
});
/**
* permissionManager:管理员Reporter报告人员(只有读取权限)Developer开发人员除不能设置仓库信息外
*/
@ -209,7 +213,9 @@ class Detail extends Component {
defaultBranch: undefined,
// 非本平台项目
platform: false
platform: false,
// devops工作流code
jianmu_devops: undefined
}
}
@ -253,7 +259,8 @@ class Detail extends Component {
this.setState({
project: result.data,
open_devops: result.data.open_devops,
platform: result.data.platform && result.data.platform !== 'educoder'
platform: result.data.platform && result.data.platform !== 'educoder',
jianmu_devops: result.data.jianmu_devops && encodeURIComponent(result.data.jianmu_devops)
})
if (result.data.type !== 0 && result.data.mirror_status === 1) {
@ -661,6 +668,7 @@ class Detail extends Component {
urlFlag={urlFlag}
showNotification={this.props.showNotification}
current_user={current_user}
isManager={isManager}
/>
}
</div>
@ -817,6 +825,12 @@ class Detail extends Component {
(props) => (<UpdateMerge {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
{/* review */}
<Route path="/:owner/:projectsId/pulls/:mergeId/review"
render={
(props) => (<Review {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/pulls/:mergeId"
render={
(props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />)
@ -882,6 +896,7 @@ class Detail extends Component {
(props) => (<Invite {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/:subIndex"
render={
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)

View File

@ -122,7 +122,7 @@
max-width: 1200px;
margin: 0 auto;
.panelmenu{
padding-top:30px;
padding-top:20px;
.depotBtn{
.mr-5{
margin-right: -5px;

View File

@ -5,7 +5,7 @@ import { numFormat } from 'educoder';
import QuitBox from './quit';
import axios from 'axios';
function DetailBanner({ history,list , owner , projectsId ,showNotification , url , pathname , state , urlFlag , projectDetail , platform ,open_devops ,current_user }){
function DetailBanner({ history,list , owner , projectsId ,showNotification , url , pathname , state , urlFlag , projectDetail , platform ,open_devops ,current_user, isManager }){
const [ menuName , setMenuName ] = useState(undefined);
const [ visible , setVisible ] = useState(false);
useEffect(()=>{
@ -32,15 +32,15 @@ function DetailBanner({ history,list , owner , projectsId ,showNotification , ur
}
}).catch(error=>{})
}
return(
<div className="f-wrap-between mt25">
<QuitBox visible={visible} onCancel={()=>setVisible(false)} name={projectDetail && projectDetail.name} onSuccess={onSuccess}/>
{
menuName && projectDetail ?
menuName && projectDetail ?
<ul className="headerMenu-wrapper">
{
Array.isArray(menuName)&& menuName.map((item,key)=>{
Array.isArray(menuName)&& menuName.map((item,key)=>{
return(
<React.Fragment key={item.menu_name}>
{
@ -83,8 +83,9 @@ function DetailBanner({ history,list , owner , projectsId ,showNotification , ur
</Link>
</li>:""
}
{/* 引擎仅对当前仓库管理员可见 */}
{
item.menu_name === "devops" ?
item.menu_name === "devops" ?
<li className={pathname==="devops" ? "active" : ""}>
{/* <Link to={{ pathname: `/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}> */}
<Link to={{ pathname: `/${owner}/${projectsId}/devops`, state:{...state,open_devops} }}>

View File

@ -293,6 +293,12 @@ class MessageCount extends Component {
)
}
codeReview=()=>{
const {history,match}=this.props;
const { projectsId, mergeId , owner } =match.params;
history.push(`/${owner}/${projectsId}/pulls/${mergeId}/review`);
}
render() {
const { projectsId, mergeId , owner } = this.props.match.params;
@ -449,6 +455,18 @@ class MessageCount extends Component {
编辑
</Button>
)}
{operate && (
<Button
type="blue"
ghost
className="ml20"
onClick={this.codeReview}
>
代码评审
</Button>
)}
{operate && (
<Button
type="danger"

View File

@ -117,6 +117,11 @@ form .ant-cascader-picker, form .ant-select {
border:1px solid #28BD6C;
color: #28BD6C;
}
/* 绿色按钮-type="blue" */
.ant-btn.ant-btn-blue{
border:1px solid #466aff;
color: #466aff;
}
.copyTab{
min-width: 370px;
padding:0px 18px 22px 18px;

View File

@ -247,7 +247,7 @@ class merge extends Component {
</Menu>
);
return (
<div className="main" style={{padding:"0px"}}>
<div className="main mt20" style={{padding:"0px"}}>
<div className="topWrapper" style={{borderBottom:"none",padding:"20px"}}>
<div className="target-detail-search">
<Search

View File

@ -0,0 +1,31 @@
const IPluginAPI = {
commands: {}
}
export class Plugin {
PLUGIN_ID = 'ChangeThemePlugin';
_commands = IPluginAPI['commands'] || null;
get commands() {
return this._commands;
}
activate({ context, commands }) {
this._commands = commands;
context.subscriptions.push(
commands.registerCommand(
'ChangeThemePlugin.changeTheme',
(value) => {
commands.executeCommand(
'alex.setDefaultPreference',
'general.theme',
value
);
}
)
);
}
}
export default new Plugin();

View File

@ -0,0 +1,283 @@
import React, { Fragment, useState, useMemo, useEffect, useRef } from 'react';
import { Switch ,Checkbox ,Tooltip,Popover,Radio,Icon} from 'antd';
import { EditorRenderer } from 'alex';
import { useLocalStorageState } from 'ahooks';
import html from './extensions/alex-ext-public.html-language-features-worker.js';
import css from './extensions/alex-ext-public.css-language-features-worker.js';
import json from './extensions/alex-ext-public.typescript-language-features-worker.js';
import typescript from './extensions/alex-ext-public.json-language-features-worker.js';
import markdown from './extensions/alex-ext-public.markdown-language-features-worker.js';
import ideTheme from './extensions/alex-ext-public.ide-dark-theme.js';
import CodeBlame from './extensions/alex-ext-public.editor-plugin-blame.js';
import { getUrl, returnbar } from 'educoder';
import changeThemePlugin from './changeTheme';
import IDEPlugin, { ExtensionCommand } from './ide-plugin';
// import { FileOperationArea } from './components/file-operation-area';
import { repoService } from './codeReview/mock/repo.service';
function CloudIDE({ download_url, params: { owner, projectsId, branchName }, filepath }) {
function FileContentStore() {
const [loading, setLoading] = useState(true);
const [showIde, setShowIde] = useState(false);
const [fileSize, setFileSize] = useState('0');
const [fileName, setFileName] = useState('');
return {
loading,
setLoading,
showIde,
setShowIde,
fileSize,
setFileSize,
fileName,
setFileName,
};
}
const { fileSize, fileName } =
FileContentStore();
const fileCache = new Map();
const copyFile = () => {
console.log('复制文件内容');
};
const [settingTipVisible, setSettingTipVisible] = useLocalStorageState(
'code.file.operation.tip',
true
);
const [lines, setLines] = useState(0);
const [sloc, setSloc] = useState(0);
const [encoding, setEncoding] = useState('UTF-8');
// const [showMdPreview, setShowPreview] = useState(false);
const [fileString, setFileString] = useState('');
const curFilepath = useMemo(() => {
// pathreadme
//
// const needLoadFile = treeEntry
// ? treeEntry.render === FileShowType.text &&
// treeEntry.size < ReadFileSizeLimit
// : !!fileName;
// return needLoadFile ? path || fileName : '';
}, [branchName, fileName]);
const [blameChecked, setBlameChecked] = useState(false);
//
// useUnmount(() => {
// fileCache.clear()
// })
// editor readFile
//
// editor api
useEffect(() => {
setBlameChecked(false);
const fileKey = `${branchName}-${curFilepath}`;
const fileInfo = fileCache.get(fileKey);
if (fileInfo) {
setFileString(fileInfo.fileString);
setLines(fileInfo.lines);
setSloc(fileInfo.sloc);
}
}, [curFilepath]);
//
const [pluginActivated, setPluginActivated] = useState(false);
const plugin = useRef(
new IDEPlugin(
() => setPluginActivated(true),
(commitId) => window.open(`/${owner}/${projectsId}/commits/${commitId}`)
)
);
// blame
const onBlame = async (e) => {
setBlameChecked(e.target.checked);
console.log('onBlame:', e.target.checked);
if (!e.target.checked) {
plugin.current.commands.executeCommand(ExtensionCommand.toggleBlame);
} else {
const res = await repoService.getCodeBlame(projectsId, owner, { sha: branchName, filepath })
plugin.current.commands.executeCommand(ExtensionCommand.toggleBlame, res);
}
};
const [wordWrap, setWordWrap] = useState(false);
const onWordWrapChange = (value) => {
const preference = value ? 'on' : 'off';
localStorage.setItem('code.file.viewer.worlWrap', preference);
plugin.current.setPerference('editor.wordWrap', preference, true);
setWordWrap(value);
};
async function changeStyle(v) {
const commands = changeThemePlugin.commands;
if (commands) {
await commands.executeCommand(
'ChangeThemePlugin.changeTheme',
v ? 'opensumi-light' : 'opensumi-dark'
);
}
}
const content = (
<Fragment>
<div className="flex align-center">
<span className="c-65">{'编码方式'}</span>
<span>
<Radio.Group
onChange={(e) => {
setEncoding(e.target.value);
}}
value={encoding}
size="small"
>
<Radio value="UTF-8">UTF-8</Radio>
<Radio value="GBK">GBK</Radio>
</Radio.Group>
</span>
</div>
<div className="flex align-center">
<span className="c-65">{'自动换行'}</span>
<span>
<Radio.Group
onChange={(e) => {
onWordWrapChange(e.target.value);
}}
value={wordWrap}
size="small"
>
<Radio value={false}>{'不自动换行'}</Radio>
<Radio value={true}>{'自动换行'}</Radio>
</Radio.Group>
</span>
</div>
</Fragment>
);
return (
<Fragment>
<div className="ide-tool-bar">
{onBlame ? (
<Checkbox onChange={onBlame} checked={!!blameChecked}>
Blame
</Checkbox>
) : null}
<Switch checkedChildren="light" unCheckedChildren="dark" defaultChecked onChange={changeStyle}></Switch>
<Tooltip
title={'更改阅读设置'}
defaultVisible={settingTipVisible}
onVisibleChange={setSettingTipVisible}
>
<Popover
content={content}
title={null}
trigger="click"
placement="bottomRight"
arrowPointAtCenter={true}
>
{/* <Icon type="ellipsis" /> */}
<Icon className="read-more" type="read" />
</Popover>
</Tooltip>
</div>
{/* <FileOperationArea
lines={lines}
sloc={sloc}
size={fileSize}
refName={branchName}
path={curFilepath}
encoding={encoding}
setEncoding={setEncoding}
onFileCopy={copyFile}
blameChecked={blameChecked}
onBlame={pluginActivated && onBlame}
wordWrap={wordWrap}
setWordWrap={onWordWrapChange}
/> */}
<EditorRenderer
appConfig={{
//
plugins: [changeThemePlugin, plugin.current],
workspaceDir: `${owner}/${projectsId}`,
defaultPreferences: {
// opensumi-dark
// 'general.theme': 'opensumi-light',
'general.theme': 'opensumi-light',
//
'editor.forceReadOnly': true,
//
'editor.scrollBeyondLastLine': false,
},
extensionMetadata: [
html,
css,
json,
typescript,
markdown,
ideTheme,
CodeBlame],
}}
runtimeConfig={{
//
biz: 'gitlink',
// editor null
scenario: null,
// editor none
startupEditor: 'none',
// editor tab
hideEditorTab: true,
}}
editorConfig={{
stretchHeight: true,
disableEditorSearch: true,
}}
//
documentModel={{
// code code
type: 'code',
// tag
ref: branchName,
//
owner: owner,
//
name: projectsId,
//
filepath: filepath,
//
readFile: async (filepath) => {
if (!filepath) {
return;
}
//
// const res = await fetch('https://gist.githubusercontent.com/gaearon/0b180827c190fe4fd98b4c7f570ea4a8/raw/b9157ce933c79a4559d2aa9ff3372668cce48de7/LikeButton.js');
const res = await fetch(`${getUrl()}/attachments/entries/get_file?download_url=${download_url}`);
setTimeout(()=>{
onWordWrapChange(localStorage.getItem('code.file.viewer.worlWrap') === 'on');
},5000)
return res.arrayBuffer();
},
// utf8 gbk
// encoding: 'utf8',
encoding,
onFilepathChange: (changedFilepath) => {
if (changedFilepath) {
console.log(`路由跳转到 ${changedFilepath}`)
}
},
//
// lineNumber: [0, 1],
onLineNumberChange: line => {
if (typeof line === 'number') {
console.log(`#L${line}`)
} else {
console.log(`#L${line[0]}-${line[1]}`)
}
},
}}
/>
</Fragment>
)
}
export default CloudIDE;

View File

@ -0,0 +1,56 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'editor-plugin-blame',
version: '0.2.6',
},
packageJSON: {
name: 'editor-plugin-blame',
publisher: 'alex-ext-public',
version: '0.2.6',
repository: {
type: 'git',
url: 'http://code.alipay.com/yxy167584/editor-plugin-blame.git',
},
displayName: 'editor-plugin-blame',
description: ' ',
activationEvents: ['*'],
kaitianContributes: {
workerMain: './out/worker/index.js',
},
contributes: {
commands: [
{
command: 'code.blame.toggleBlame',
title: '查看blame',
},
{
command: 'code.blame.acrToggleBlame',
title: 'blame',
},
{
command: 'code.blame.linktocommit',
title: 'hover详情跳转',
},
],
views: {},
menus: {
'editor/title': [
{
command: 'code.blame.acrToggleBlame',
type: 'checkbox',
group: 'navigation',
toggledWhen: 'acr_blame_context',
when: 'resourceScheme =~ /^git$|^diff$/',
},
],
},
workerMain: './out/worker/index.js',
},
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
webAssets: ['package.json', 'out/worker/index.js'],
mode: 'public',
};

View File

@ -0,0 +1,72 @@
import { DependencyList, useEffect, useState, useMemo } from 'react';
// @ts-ignore
import { useEventEmitter, usePrevious } from 'ahooks';
// @ts-ignore
import sha1 from 'sha1';
// @ts-ignore
import differenceBy from 'lodash/differenceBy';
export function useRequest(
factory,
options
) {
const { deps = [], initial, ready = true } = options || {};
const [val, setVal] = useState(initial);
useEffect(() => {
if (!ready) return;
let cancel = false;
const promise = factory();
if (promise === undefined || promise === null) return;
promise
.then((val) => {
if (!cancel) {
setVal(val);
}
})
.catch((err) => {
console.error(err);
});
return () => {
cancel = true;
};
}, [...deps, ready]);
return val;
}
export function useFileReadMarkChange$(
diffs,
readMarks
) {
const fileReadMarkChange$ = useEventEmitter();
const filePathShaMap = useMemo(() => {
const map = new Map();
for (const diff of diffs) {
map.set(sha1(diff.newPath), diff.newPath);
}
return map;
}, [diffs]);
const prevReadMarks = usePrevious(readMarks);
useEffect(() => {
const changesA = differenceBy(
prevReadMarks,
readMarks,
(readMark) => readMark.filePathSha
);
const changesB = differenceBy(
readMarks,
prevReadMarks,
(readMark) => readMark.filePathSha
);
for (const change of [...changesA, ...changesB]) {
const filePath = filePathShaMap.get(change.filePathSha);
if (filePath) {
fileReadMarkChange$.emit(filePath);
}
}
}, [readMarks, filePathShaMap]);
return fileReadMarkChange$;
}

View File

@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" viewBox="0 0 200 200">
<defs>
<linearGradient id="linear-gradient" y1="0.5" x2="1" y2="0.5" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#6dffff"/>
<stop offset="1" stop-color="#0080ff"/>
</linearGradient>
</defs>
<g id="组_1758" data-name="组 1758" transform="translate(9483 -10311)">
<rect id="矩形_340" data-name="矩形 340" width="200" height="200" transform="translate(-9483 10311)" fill="#fff" opacity="0"/>
<g id="图层_2" data-name="图层 2" transform="translate(-9472 10328)">
<g id="图层_1" data-name="图层 1">
<path id="路径_1227" data-name="路径 1227" d="M127.508,32.458,123.06,26.4l-3.9,1.958V77.007l3.239,1.7v-5.4l5.11,8.248,4.145.648V23.03l-4.145,1.6ZM35.847,82.2l9.284-4.62V27.708L35.847,23.03Zm63.463,6.29,8.305,4.951V9.241L99.31,14.192ZM87.378,0H75.863V105.291H93.509V94.841H87.378ZM49.954,21.821l4.577,1.684V93.344l8.348-4.318V27.132l4.477,1.655V19.3L49.954,9.226ZM5.448,78.634,25.6,68.457V49.3l-9.543-1.022v6.736h3.829V61.52l-6.621,3.008V43.873L25.541,46.06V38.216L5.39,31.58ZM154.18,54.855,168.919,31.58l-11.04,1.195-7.355,9.788V36.071L142.406,38V73.006l8.118,1.684V67.982l7.845,10.076,12.7.633Z" transform="translate(2.368)" fill-rule="evenodd" fill="url(#linear-gradient)"/>
<path id="路径_1228" data-name="路径 1228" d="M0,110.683a27.2,27.2,0,0,0,2.634-4.577q1.051-2.375,1.727-4.4a33.61,33.61,0,0,0,.979-3.527c.2-.993.317-1.612.345-1.9V92.964h-3.5V88.876H17.445v4.088H9.37V96.75q0,.158-.432,2.245a34.545,34.545,0,0,1-1.439,5.11h5.556v-.72H16.74V122.86a4.822,4.822,0,0,1-1.209,3.325,3.915,3.915,0,0,1-3.008,1.339h-8.2V110.942l-.72,1.137c-.23.389-.489.763-.763,1.152Zm8.017,12.753h4.491c.36,0,.547-.187.547-.576V108.193H8.017Zm9.471-27.521q1.209-1.6,2.073-2.879c.576-.82,1.195-1.77,1.842-2.879a28.457,28.457,0,0,0,1.727-3.267L26.4,88.7a17.271,17.271,0,0,1-.849,1.713H39.727V93.67l-3.354,4.995h2v-.777h3.685v24.758a4.808,4.808,0,0,1-1.209,3.325,3.872,3.872,0,0,1-2.98,1.353h-2.1v-4.045h2.058c.374,0,.547-.2.547-.619v-3.368h-6.29v7.2H28.4v-7.2H22.3q-.23,1.67-.518,3.109c-.187.964-.36,1.814-.533,2.562s-.317,1.353-.446,1.814a7.658,7.658,0,0,0-.187.748l-1.77-.619-1.727-.619c.4-1.6.734-3.008.979-4.318s.475-2.648.677-4.189a35.307,35.307,0,0,0,.3-4.592V98.664H31.811l2.735-4.088H23.131q-1.123,1.756-1.986,2.879l-.993,1.108ZM28.4,115.188v-4.707H22.757v4.707Zm-5.642-12.436v3.627H28.4v-3.627Zm15.617,0h-6.29v3.627h6.29Zm0,12.436v-4.707h-6.29v4.707Z" transform="translate(0 38.179)" fill="#333"/>
<path id="路径_1229" data-name="路径 1229" d="M32.82,110.943H51.014c.518-.806.993-1.612,1.439-2.447a15.2,15.2,0,0,0,1.036-2.634V97.168h3.685V106.7a21.591,21.591,0,0,1-1.7,4.2H73.382v4.088H58.427l14.509,8.751L71.151,127.4,55.49,117.91l1.439-2.879H52.756a31.018,31.018,0,0,1-3.742,3.786,41.975,41.975,0,0,1-4.045,3.051,43.833,43.833,0,0,1-3.944,2.361c-1.267.677-2.418,1.223-3.426,1.655s-1.842.777-2.49,1.008l-1.123.417-.518-1.972-.5-1.914s.5-.173,1.439-.547,2.116-.878,3.527-1.569,2.965-1.555,4.635-2.591a32.934,32.934,0,0,0,4.908-3.685H32.82Zm.1-19.849H51.287l-1.166-2.433,3.267-1.871,2,4.318H69.251v-.734h3.685V99.86H69.251V95.182H36.606v4.865H32.921Zm4.75,10.824,9.889,4.462-1.353,3.742-9.889-4.462Zm12.681,3.325-9.831-4.62,1.439-3.786,9.831,4.649Z" transform="translate(14.421 38.135)" fill="#333"/>
<path id="路径_1230" data-name="路径 1230" d="M105.835,108.223h-9.37v18.453h-3.7V108.223H77.307a38.49,38.49,0,0,1-2.03,7.125,62.419,62.419,0,0,1-2.634,5.758,44.3,44.3,0,0,1-2.332,3.944c-.691.979-1.065,1.526-1.123,1.626l-2.879-2.591.907-1.3a34.806,34.806,0,0,0,2.015-3.4c.763-1.439,1.54-3.109,2.346-5.023a34.877,34.877,0,0,0,1.958-6.146H64.05v-4.088H74.126V92.117H66.857V88h36.229v4.1H96.465v12.019h9.37Zm-13.07-4.088V92.117H77.955v12.019Z" transform="translate(28.143 38.666)" fill="#333"/>
<path id="路径_1231" data-name="路径 1231" d="M98.847,127.257l-3.267-1.77,6.348-14.394,3.311,1.814Zm3.958-29.8L96,90.667l2.433-3.008,6.852,6.794Zm0,10.882L96,101.549l2.433-3.008,6.852,6.794Zm.187,16.985c.317-.59.72-1.439,1.238-2.533a29.74,29.74,0,0,0,1.439-3.6,34.757,34.757,0,0,0,1.281-3.987,14.767,14.767,0,0,0,.547-3.714V87.4h28.788v4.1H111.255V111.5a17.965,17.965,0,0,1-.36,3.4,30.226,30.226,0,0,1-.921,3.527c-.374,1.152-.777,2.26-1.209,3.311s-.82,2-1.195,2.879-.691,1.439-.964,2l-.446.849Zm7.931-.1a34.386,34.386,0,0,0,2.116-4.059c.576-1.339,1.051-2.519,1.439-3.555a30.893,30.893,0,0,0,1.022-3.527l3.6.993s-.1.461-.288,1.18-.475,1.67-.878,2.879-.921,2.461-1.569,3.93a35.5,35.5,0,0,1-2.346,4.491Zm13.617-31.451-1.022,1.871h7.787v-.936h3.742v13.7a4.836,4.836,0,0,1-1.209,3.3,3.858,3.858,0,0,1-2.994,1.439H126.1v9.586a4.736,4.736,0,0,1-1.223,3.282,3.8,3.8,0,0,1-2.98,1.439h-2.749v-4.1h2.706a.576.576,0,0,0,.4-.144.6.6,0,0,0,.158-.417v-9.586h-8.953V95.648h5.758L121.46,91.6Zm6.765,6H117.128v2.792h14.178Zm-.518,9.27q.518,0,.518-.561v-1.814H117.128v2.375ZM132,113.827l4.433,11.8-3.4,1.5-4.318-11.8Z" transform="translate(41.997 38.403)" fill="#333"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,292 @@
import React, { useEffect, useState, useMemo } from 'react';
// import { default as AntcodeCR } from 'acr';
import { ACR } from 'alex';
import { Button, Switch, Spin, Icon } from 'antd';
// blame worker
import CodeBlame from './extensions/alex-ext-public.editor-plugin-blame.js';
// plugin
import CodeBlamePlugin, { ExtensionCommand } from './plugins/code-blame.plugin';
import { usePersistFn } from "ahooks";
import {windowsOrMac} from 'educoder';
import {
Provider,
useGlobal,
usePr,
useNote,
useReadMark,
useAcr,
useSetting,
} from './model';
import {
DiscussionItem,
Commenting,
Menubar,
AnnotationEntry,
PRMoreActionLinks,
} from './mock/component/index';
import { projectService } from './mock/project.service';
import { lsifService } from './mock/lsif.service';
import { useFileReadMarkChange$ } from './hooks';
import { repoService } from './mock/repo.service';
import ideLogo from './ideLogo.svg';
const CodeReview = (props) => {
const { match: { params: { owner, projectsId, mergeId } }, projectDetail, current_user } = props;
const [visible, setVisible] = React.useState(true);
const [count, setCount] = React.useState(0);
const [isFullscreen, setFullscreen] = React.useState(false);
const { locale, setLocale, gbk, setGBK } = useSetting();
const { commentPack } = useNote();
const { project, user, setCurrent_user, setProjectDetail } = useGlobal();
const { pr } = usePr();
const {
getFileReadStatus: _getFileReadStatus,
markFileAsRead,
markFileAsUnread,
readMarks,
} = useReadMark();
// todo
const getFileReadStatus = usePersistFn(_getFileReadStatus);
// const getFileReadStatus = _getFileReadStatus;
const {
diffsPack,
getDiffById,
getFileContent,
IDEMode,
toggleViewerType,
annotationPacks,
setAcrFlag,
} = useAcr();
const fileReadMarkChange$ = useFileReadMarkChange$(
diffsPack && diffsPack.diffs || [],
readMarks
);
//
const [pluginActivated, setPluginActivated] = useState(false);
const blamePlugin = useMemo(() => {
return new CodeBlamePlugin(
() => setPluginActivated(true),
(commitId) => {
window.open(
`/${owner}/${projectsId}/commit/${commitId}`
)
},
// blame
(projectId, commitId, filepath) =>
repoService.getCodeBlame(projectsId, owner, { sha: commitId, filepath })
);
}, []);
useEffect(() => {
if (!pluginActivated) {
return;
}
const projectData = {
projectId: projectsId,
prevSha: diffsPack.fromVersion && diffsPack.fromVersion.headCommitSha || diffsPack.toVersion.baseCommitSha,
nextSha: diffsPack.toVersion.headCommitSha,
};
blamePlugin.commands && blamePlugin.commands.executeCommand(
ExtensionCommand.setProjectData,
projectData
);
//
blamePlugin.commands && blamePlugin.commands.executeCommand(
'alex.setDefaultPreference',
'acr.lsifEnabled',
false
);
}, [pluginActivated, diffsPack]);
if (!diffsPack) {
return <Spin style={{ height: "100vh" }} spinning={true}></Spin>
}
function EditorEmpty() {
const system=windowsOrMac();
console.log(system);
return <div className='ide-logo'>
<img className='ide-logo-img' src={ideLogo} />
<div>
<div className='ide-logo-text'>IDE代码体验高效的代码编辑</div>
{/* <span className='ide-btn'>⇧</span> */}
<div className='ide-logo-text'>标记文件为已查看 <span className='ide-btn'>{system==='mac'?'⌥':'Alt'}</span> <span className='ide-btn'>C</span> </div>
<div className='ide-logo-text'>快速打开变更文件 <span className='ide-btn'>^</span> <span className='ide-btn'>{system==='mac'?'⌥':'Alt'}</span> <span className='ide-btn'>P</span></div>
<div className='ide-logo-text'>切换变更文件 <span className='ide-btn'>{system==='mac'?'⌥':'Alt'}</span> <span className='ide-btn'></span> / <span className='ide-btn'></span></div>
</div>
</div>
}
// <Icon className='ide-btn-demo' type="arrow-up" /> <Icon className='ide-btn-demo' type="arrow-down" />
const propsIDE = {
//
noteIdToReplyIdSet: commentPack.noteIdToReplyIdSet,
//
addLineNum: diffsPack.addLineNum,
//
deleteLineNum: diffsPack.delLineNum,
// sha
prevSha:
diffsPack.fromVersion && diffsPack.fromVersion.headCommitSha || diffsPack.toVersion.baseCommitSha,
// sha
nextSha: diffsPack.toVersion.headCommitSha,
toggleViewerType,
DiscussionItem,
Commenting,
getFileContent,
lineToNoteIdSet: commentPack.lineToNoteIdSet,
noteIdToNote: commentPack.noteIdToNote,
noteUpdateFlag: commentPack.updateFlag,
getDiffById,
diffs: diffsPack.diffs,
latestCommitSha: pr.diff.headCommitSha,
projectId: projectsId,
projectPath: `${owner}/${projectsId}`,
pullRequestId: mergeId,
pr,
//
getLanguages: () =>
projectService
.getLanguages(projectsId, {
aggBy: 'file_extension',
//
orderBy: 'count',
size: 20,
})
.then((res) => res && Object.keys(res)),
getFileReadStatus,
fileReadMarkChange$,
markFileAsRead,
markFileAsUnread,
bulkChangeFiles: (actions, header) => {
let files = [];
for (const item of actions) {
files.push({
action_type: item.actionType,
content: item.content,
encoding: item.encoding,
file_path: item.filePath,
});
}
let data = {
files: files,
author_email: current_user.email,
author_name: current_user.login,
committer_email: current_user.email,
committer_name: current_user.login,
branch: header.branch,
message: header.commitMessage,
};
if (pr && pr.forkProject) {
projectService.bulkChangeFiles(pr.forkProject.identifier, pr.forkProject.login, data).then(res => {
if (res) {
// giteacommit
setTimeout(() => {
setAcrFlag({});
}, 1000)
}
})
} else {
projectService.bulkChangeFiles(projectsId, owner, data).then(res => {
if (res) {
// giteacommit
setTimeout(() => {
setAcrFlag({});
}, 1000)
}
})
}
},
//
Menubar: () => (
<Menubar
initialFullscreen={isFullscreen}
handleFullscreenChange={setFullscreen}
toggleViewerType={toggleViewerType}
logFullScreen={(bool) => console.log('>>>logFullScreen', bool)}
/>
),
user,
lsifService,
defaultEncoding: project.encoding,
//
encoding: gbk ? 'gbk' : 'utf-8',
//
setEncoding: (val) => {
setGBK(val === 'gbk');
},
locale,
annotations: annotationPacks,
AnnotationEntry,
PRMoreActionLinks,
EditorEmpty: EditorEmpty,
onigWasmUri: 'https://gw.alipayobjects.com/os/lib/vscode-oniguruma/1.6.2/release/onig.wasm',
//
isFullscreen,
appConfig: {
//
plugins: [blamePlugin],
//
extensionMetadata: [CodeBlame],
},
};
const IDEContainerStyle = {
position: isFullscreen ? 'fixed' : 'static',
left: 0,
top: 0,
width: '100%',
height: isFullscreen ? '100vh' : 'calc(100vh - 72px)',
zIndex: 1002,
};
console.log('propsIDE:');
console.log(propsIDE);
return (
<div style={{ height: '100%', lineHeight: '1.4' }}>
{/* <EditorEmpty></EditorEmpty> */}
{/* <div className="controller">
{!IDEMode && (
<>
IDE 模式: <Switch checked={IDEMode} onChange={toggleViewerType} />
</>
)}
</div> */}
{/* <div className="pr-head">
<div>{pr.description}</div>
<div>
评审人
{pr.review && pr.review.reviewers && pr.review.reviewers.map((r) => (
<span style={{ marginRight: 4 }} key={r.id}>
{r.name}
</span>
))}
<span style={{ marginRight: 24 }}></span>
合并人{pr.assignee && pr.assignee.name}
</div>
</div> */}
{IDEMode && (
<div style={IDEContainerStyle}>
{visible && <ACR {...propsIDE} key={count} />}
</div>
)}
</div>
);
};
const CodeReviewP = (props) => {
return (
<Provider {...props}><CodeReview {...props} /></Provider>
)
}
export default CodeReviewP;

View File

@ -0,0 +1,5 @@
export const group = 'Gitlink';
export const repo = 'forgeplus';
export const prIid = '2';
export const project = `${group}/${repo}`;

View File

@ -0,0 +1,67 @@
import { request } from './request/index';
export const apiService = {
get(
url,
query,
options = {}
) {
return request(
url,
{
method: 'GET',
params: query,
},
options
);
},
post(
url,
query,
body,
options= {}
) {
return request(
url,
{
method: 'POST',
params: query,
data: body,
},
options
);
},
put(
url,
query,
body,
options= {}
) {
return request(
url,
{
method: 'PUT',
params: query,
data: body,
},
options
);
},
delete(
url,
query,
options = {}
) {
return request(
url,
{
method: 'DELETE',
params: query,
},
options
);
},
};

View File

@ -0,0 +1,601 @@
import React, {
FC,
MutableRefObject,
memo,
useEffect,
useRef,
useState,
useCallback,
Fragment,
} from "react";
import {
message,
Dropdown,
Icon,
Menu,
Button,
Tooltip,
Avatar,
Modal,
Switch,
Input,
Popconfirm,
Checkbox,
} from "antd";
import { Link } from "react-router-dom";
import { usePersistFn } from "ahooks";
import { getImageUrl, timeAgo } from "educoder";
import "./style.module.less";
import { useAcr, useNote, useGlobal, usePr } from "../../model";
const AnnotationStatus = {
Init: 'Init',
Ignore: 'Ignore',
Confirm: 'Confirm',
FalsePositive: 'FalsePositive',
}
const noteType = {
comment: 'comment',
problem: 'problem',
}
export const Commenting = (props) => {
const { user } = useGlobal();
const { addComment } = useNote();
const { toVersion, stDiff } = useAcr();
const submit = usePersistFn(async (note, type) => {
try {
console.log(note, type);
await addComment({
note,
diffId: toVersion.id,
line_code: props.lineCode,
parent_id: props.id,
path: props.path,
type: type || 'comment',
diff: props.id ? null : stDiff,
});
props.onClose && props.onClose();
} catch (e) {
console.error(e);
}
});
const { replyNote } = props;
const [note, setNote] = useState("");
const [isProblem, setIsProblem] = useState(false);
const onSubmit = useCallback(() => {
const submitNote = replyNote ? insertReplyNote(note, replyNote) : note;
submit(submitNote, isProblem ? noteType.problem : noteType.comment);
}, [note, replyNote, isProblem]);
function insertReplyNote(baseNote, replyNote) {
if (replyNote && replyNote.note) {
const replyLine = replyNote.note.split("\n");
const blockquoteNote = [
`<!-- reply id="${replyNote.id}" -->`,
...replyLine,
]
.map((note) => `> ${note}`)
.join("\n");
return blockquoteNote + "\n\n" + baseNote;
} else {
return baseNote;
}
}
return (
// <div className="arc-container">
<div
className={`arc-commenting-container ${props.noPadding ? "no-padding" : ""}`}>
<div className="avatar">
<Tooltip title={user.name}>
<Avatar src={user && user.image_url
? getImageUrl(`/${user.image_url}`)
: "images/avatars/User/b"} size={24} />
</Tooltip>
</div>
<Input.TextArea
autoFocus
placeholder="请输入评论信息"
value={note}
onChange={(e) => setNote(e.target.value)}
style={{ height: 150 }}
/>
<div className="action">
<div>
<Button
type="primary"
onClick={onSubmit}
disabled={!note.trim()}
style={{ marginRight: 8 }}
>
评论
</Button>
{note.trim() ? (
<Popconfirm
title="你确定要取消?"
okText="确定"
cancelText="取消"
onConfirm={props.onClose}
>
<Button>取消</Button>
</Popconfirm>
) : (
<Button onClick={props.onClose}>取消</Button>
)}
</div>
<div>
{replyNote && (
<div className="reply-note">Reply to {replyNote.author.name}</div>
)}
</div>
</div>
</div>
// </div>
);
};
const ReplyItem = memo(({ note, isChild }) => {
const { user } = useGlobal();
const { deleteComment } = useNote();
const [visible, setVisible] = useState(false);
return <div className={`reply-item ${isChild ? 'reply-item-child' : ''}`}>
<div className="reply-item-head">
<Link
to={`/${note.user && note.user.login}`}
className="show-user-link"
>
<Avatar src={note.user && note.user.imageUrl
? getImageUrl(`/${note.user.imageUrl}`)
: "images/avatars/User/b"} size={24} />
</Link>
<Link
to={`/${note.user && note.user.login}`}
className="show-user-link color-black ml10 fwb"
>
{note.user && note.user.username}
</Link> <span className="show-user-link color-black">{timeAgo(note.createdAt)}</span> 发表评论
<span className="btn-right">
{user &&
(user.admin ||
user.login === note.user.login) ? (
<Popconfirm
placement="bottom"
title={"确定要删除当前评论吗?"}
okText="是"
cancelText="否"
onConfirm={() => deleteComment(note.id)}
>
<Button type="link">
<i className="iconfont icon-shanchu3 font-15 color-grey-6 mr5 ver-middle"></i>
<span className="font-12 color-grey-6">删除</span>
</Button>
</Popconfirm>
) : (
""
)}
{!isChild ? <Button
type="link"
className="ml-10"
onClick={() => setVisible(true)}
>
<i className="iconfont icon-huifu1 font-15 color-grey-6 mr5 ver-middle"></i>
<span className="font-12 color-grey-6">回复</span>
</Button> : ''}
</span>
</div>
<div className="reply-item-content">{note.note}</div>
{visible && <Commenting style={{ paddingLeft: '20px' }} onClose={() => { setVisible(false) }} {...note} />}
</div>
})
export const DiscussionItem = memo((props) => {
const { noteId } = props;
const { commentPack, deleteComment } = useNote();
// const [commenting, setCommenting] = useState(false);
// const [commentReply, setCommentReply] = useState();
const replyIdSet = commentPack.noteIdToReplyIdSet.get(noteId);
const replyIds = replyIdSet
? Array.from(replyIdSet).sort((a, b) => a - b)
: null;
// const pending = (function () {
// if (!commentPack.hasPendingReview) {
// return false;
// }
// const reviewId = commentPack.noteIdToReviewId.get(noteId);
// if (!reviewId) return false;
// const review = reviewId ? commentPack.reviewIdToReview.get(reviewId) : null;
// if (!review) return false;
// return review.pending;
// })();
// const hasPendingNote = (function () {
// if (pending) return true;
// if (replyIds) {
// for (const replyId of replyIds) {
// const reviewId = commentPack.noteIdToReviewId.get(replyId);
// if (reviewId && commentPack.reviewIdToReview.get(reviewId) && commentPack.reviewIdToReview.get(reviewId).pending) {
// return true;
// }
// }
// }
// return false;
// })();
// const handleReply = (replyNote) => {
// if (replyNote) {
// setCommentReply(replyNote);
// }
// setCommenting(true);
// };
// useEffect(() => {
// if (!commenting) {
// setCommentReply(undefined);
// }
// }, [commenting]);
const note = commentPack.noteIdToNote.get(noteId);
if (!note) return null;
// console.log('note');
// console.log(note);
return (
<div className="arc-container">
{/* <div>
{note.type === noteType.problem && (
<Fragment>
{note.state === "resolved" && !pending && "已解决"}
{note.state === "opened" && !pending && "待解决"}
{pending && "需要回应"}
</Fragment>
)}
</div> */}
<ReplyItem note={note} />
{replyIds && (
<div style={{ paddingLeft: 50 }}>
{replyIds.reverse().map((replyId) => {
const note = commentPack.noteIdToNote.get(replyId);
if (!note) return null;
return <ReplyItem note={note} key={replyId} isChild={true} />
})}
</div>
)}
</div>
);
});
export const Menubar = memo((props) => {
const [isFullscreen, setFullscreen] = React.useState(
props.initialFullscreen || false
);
const handleChangeFullscreen = (isFullscreen) => {
setFullscreen(isFullscreen);
if (isFullscreen) {
message.info("全屏模式支持直接使用 ↑/↓ 切换变更文件");
}
props.handleFullscreenChange(isFullscreen);
props.logFullScreen(isFullscreen);
};
useEffect(() => {
if (isFullscreen) {
document.body.style.overflow = "hidden";
}
return () => {
document.body.style.overflow = "";
};
}, [isFullscreen]);
const { versions, fromVersion, toVersion, updateQuery } = useAcr();
const idToVerbose = new Map();
idToVerbose.set(0, "Base Version");
versions.forEach((version, index) => {
if (index === versions.length - 1) {
idToVerbose.set(version.id, `Latest Version`);
} else {
idToVerbose.set(version.id, `Version ${versions.length - index - 1}`);
}
});
const fromId = fromVersion && fromVersion.id || 0;
const toId = toVersion && toVersion.id || 0;
const filterCleared = fromId === 0 && toId === versions[0] && versions[0].id;
function renderMenuItem(version, selectedId) {
const verbose = idToVerbose.get(version.id);
return (
<Menu.Item
className={`arc-menuItem ${version.id === selectedId ? "selected" : ""
}`}
key={version.id}
>
<div className='headRow'>
<span>{verbose}</span>
<span>{version.headCommitSha.slice(0, 8)}</span>
</div>
<div className='desc'>
包含 {version.commitsCount}次提交{version.filesCount} 份文件变更
</div>
</Menu.Item>
);
}
const menuFrom = (
<Menu
onClick={(e) => {
const val = parseInt(e.key);
updateQuery({
from: val === 0 ? undefined : val,
});
}}
>
{versions.slice(1).map((version) => renderMenuItem(version, fromId))}
<Menu.Item
key={0}
className={`${'menuItem'} ${fromId === 0 ? 'selected' : ""}`}
>
Base Version
</Menu.Item>
</Menu>
);
const menuTo = (
<Menu
onClick={(e) => {
updateQuery({
to: parseInt(e.key),
});
}}
>
{versions
.slice(0, versions.length)
.map((version) => renderMenuItem(version, toId))}
</Menu>
);
const MenuStyle = {
zIndex: 1200,
};
return (
<div className="arc-menubar">
<div className={'group'}>
{versions.length !== 0 && (
<div className={'versionContainer'}>
<Dropdown overlay={menuFrom} trigger={["click"]} overlayStyle={MenuStyle}>
<div className={'versionItem'}>
<span>{idToVerbose.get(fromId)}</span>
<Icon type="down" />
</div>
</Dropdown>
<div>
<Icon type="arrow-right" />
</div>
<Dropdown overlay={menuTo} trigger={["click"]} overlayStyle={MenuStyle}>
<div className={'versionItem'}>
<span>{idToVerbose.get(toId)}</span>
<Icon type="down" />
</div>
</Dropdown>
{!filterCleared && (
<div>
<a
onClick={() => {
updateQuery({
from: undefined,
to: undefined,
});
}}
>
重置
</a>
</div>
)}
</div>
)}
</div>
<div className={'group'}>
{/* {isFullscreen ? null : (
<div className={'switchContainer'}>
<span className={'ideLabel'}>IDE 模式</span>
<Tooltip
placement="top"
title="提供 IDE 代码浏览体验、完善的语言服务和高效的代码编辑"
>
<Switch checked onChange={props.toggleViewerType} />
</Tooltip>
</div>
)} */}
{isFullscreen ? (
<Icon
type="fullscreen-exit"
onClick={() => handleChangeFullscreen(false)}
/>
) : (
<Tooltip title="全屏">
<Icon
type="fullscreen"
onClick={() => handleChangeFullscreen(true)}
/>
</Tooltip>
)}
</div>
</div>
);
});
export const AnnotationEntry = memo((props) => {
const { annotation, checkSuite } = props;
//
const anchorOffsetRef = useRef(null);
const hash = `#annotation_${annotation.id}`;
const hashActive = hash === window.location.hash;
useEffect(() => {
setTimeout(() => {
if (hashActive) {
anchorOffsetRef.current && anchorOffsetRef.current.scrollIntoView(true);
}
}, 500);
});
const [isIgnore, setIsIgnore] = useState(
annotation.feedBackStatus === AnnotationStatus.Ignore
);
return (
<div className="arc-container">
<div
ref={anchorOffsetRef}
id={`annotation_${annotation.id}`}
className="arc-annotationContainer"
>
<div className={'codeLine'}>
<div>{annotation.level}</div>
<div className={'line'}>
{annotation.startLine === annotation.endLine ? (
<span>
{annotation.endLine}
行代码分析
</span>
) : (
<span>
{annotation.startLine}{annotation.endLine}
行代码分析
</span>
)}
</div>
</div>
<div className={'botRow'}>
<Avatar
alt={checkSuite.service.nameShow}
size={20}
src="https://gw-office.alipayobjects.com/bmw-prod/a9596840-928c-4603-a865-b24373ab4b4d.png"
/>
<div>
<div className={'strong'}>{checkSuite.service.nameShow}</div>
</div>
</div>
<div className={'content'}>
<p>{annotation.title}</p>
<div>{annotation.message}</div>
</div>
<div className={'detail'}>
<div>
{annotation.bugId ? (
isIgnore ? (
<Button type="primary" style={{ marginRight: 8 }}>
取消忽略
</Button>
) : (
<Fragment>
<Button style={{ marginRight: 8 }}>忽略</Button>
<Button style={{ marginRight: 8 }}>误报</Button>
<Button>确认</Button>
</Fragment>
)
) : null}
</div>
<div>
<a
className={'halfPlainA'}
href={`https://codeinsightapi.alipay.com/api/v1/describe?bug_type=${annotation.bugType}&bug_id=${annotation.bugId}`}
target="_blank"
>
查看问题详情 <Icon type="double-right" />
</a>
</div>
</div>
</div>
</div>
);
});
export const PRMoreActionLinks = ({
setVisible,
}) => {
const { pr } = usePr();
const { project } = useGlobal();
const [checkoutBranchVisible, setCheckoutBranchVisible] = useState(false);
return (
<Fragment>
<div>正在丰富功能中...</div>
<div style={{display:'none'}}>
<Switch checked />
</div>
{/* <div
className="arc-checkoutBranch"
onClick={() => {
setCheckoutBranchVisible(true);
setVisible(false);
}}
>
检出分支
</div>
<a
className="arc-flexA"
download
href={`http://code.test.alipay.net/${project.pathWithNamespace}/pull_requests/${pr.iid}.patch`}
>
<span onClick={() => setVisible(false)}>下载 patch 文件</span>
</a>
<a
className="arc-flexA"
download
href={`http://code.test.alipay.net/${project.pathWithNamespace}/pull_requests/${pr.iid}.diff`}
>
<span onClick={() => setVisible(false)}>下载 diff 文件</span>
</a>
<Modal
title="检出,评审,并在本地执行合并"
visible={checkoutBranchVisible}
footer={null}
onCancel={() => setCheckoutBranchVisible(false)}
>
<div className="arc-checkout">
<p>
<span className="fw">Step 1.</span> Fetch and check out the branch
for this pull request
</p>
<pre>
git fetch origin
<br />
{`git checkout -b ${pr.sourceBranch} origin/${pr.sourceBranch}`}
</pre>
<p>
<span className="fw">Step 2.</span> Review the changes locally
</p>
<p>
<span className="fw">Step 3.</span> Merge the branch and fix any
conflicts that come up
</p>
<pre>
git fetch origin
<br />
{`git checkout origin/${pr.targetBranch}`}
<br />
git merge --no-ff master
</pre>
<p>
<span className="fw">Step 4.</span> Push the result of the merge
</p>
<pre>{`git push origin ${pr.targetBranch}`}</pre>
</div>
</Modal> */}
</Fragment>
);
};

View File

@ -0,0 +1,219 @@
.arc-container {
padding:5px 0 10px;
border-bottom: solid 1px #eee;
.reply-item{
padding:6px 16px 10px;
&.reply-item-child{
margin-top:-10px;
padding:5px 0;
}
}
.reply-item-head{
line-height: 30px;
color: #666;
}
.btn-right{
float: right;
}
.reply-item-content{
padding-left:35px;
line-height: 1.4;
}
.show-user-link{
font-weight: 600;
}
}
.arc-menubar {
display: flex;
align-items: center;
justify-content: space-between;
.group {
display: flex;
align-items: center;
> * {
margin-right: 12px;
transition: 300ms all ease-in-out;
&:last-child {
margin-right: 0;
}
}
}
.versionContainer {
display: flex;
align-items: center;
> *:not(:last-child) {
margin-right: 2px;
}
}
.versionItem {
cursor: pointer;
padding: 4px 6px;
.anticon {
margin-left: 4px;
}
}
.switchContainer {
display: flex;
align-items: center;
}
.ideLabel {
line-height: 1;
margin-right: 8px;
}
}
.arc-menuItem {
background: #ffffff;
width: 350px;
padding-top: 8px;
padding-bottom: 8px;
&.selected {
background: #f0f5ff;
}
.headRow {
display: flex;
justify-content: space-between;
}
.desc {
margin-top: 2px;
color: rgba(0, 0, 0, 0.45);
}
}
.arc-annotationContainer {
border-bottom: solid 1px #eee;
&:last-child {
border-bottom: none;
}
.codeLine {
display: flex;
align-items: center;
}
.line {
padding-top: 8px;
padding-left: 8px;
font-size: 12px;
&.active {
color: rgba(0, 0, 0, 0.45);
}
}
.botRow {
padding: 8px 12px 0 16px;
display: flex;
align-items: center;
> *:not(:first-child) {
margin-left: 8px;
}
}
.content {
padding: 8px 12px 12px 48px;
p {
margin-bottom: 8px;
}
}
.strong {
color: rgba(0, 0, 0, 0.85);
}
.detail {
height: 50px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
padding: 0 21px;
border-top: 1px solid #ebedf0;
}
.halfPlainA {
color: inherit;
&:hover {
color: #2f54eb;
}
}
}
.arc-checkoutBranch {
font-size: 14px;
color: #2f54eb;
cursor: pointer;
line-height: 32px;
}
.arc-flexA {
display: flex !important;
justify-content: space-between;
align-items: center;
line-height: 32px;
color: #2f54eb;
i {
color: #2f54eb;
}
&:hover {
color: #2f54eb;
}
}
.arc-checkout {
pre {
margin-bottom: 8px;
padding: 8px 12px;
background-color: #fbfbfb;
border: 1px solid #ebedf0;
border-radius: 4px;
}
}
.arc-commenting-container {
position: relative;
padding: 8px 12px 0 44px;
&.no-padding {
padding: 0 0 0 32px;
.avatar {
top: 2px;
left: 0;
}
}
.avatar {
position: absolute;
top: 8px;
left: 12px;
}
.action {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 0;
.reply-note {
padding: 1px 5px;
color: #8c8c8c;
line-height: 20px;
background-color: #fafafa;
}
}
}

View File

@ -0,0 +1,68 @@
import { apiService } from './api.service';
// 语言服务接口
export const lsifService = {
async lsifExists(projectPath, sha) {
return !!(await apiService.post(
`/webapi/projects/${encodeURIComponent(
projectPath
)}/repository/lsif/exists`,
{
sha,
}
)) ;
},
async lsifHover(
projectId,
data
) {
return (await apiService.post(
`/webapi/projects/42422/repository/lsif/hover`,
undefined,
{
...data,
method: 'hover',
}
))
},
async lsifDefinitions(
projectId,
data
) {
return (await apiService.post(
`/webapi/projects/42422/repository/lsif/definitions`,
undefined,
data
))
},
async lsifReferences(
projectId,
data
) {
return (await apiService.post(
`/webapi/projects/42422/repository/lsif/references`,
undefined,
data,
{
disableBodyConvert: true,
}
))
},
async lsifReferencesV2(
projectId,
data
) {
return (await apiService.post(
`/webapi/projects/42422/repository/lsif/reference/v2`,
undefined,
data,
{
disableBodyConvert: true,
}
))
},
};

View File

@ -0,0 +1,197 @@
import { apiService } from './api.service';
import { message } from 'antd';
import { getImageUrlAbsolute } from "educoder";
import { calcChangeLineNum } from './utils/calc-change-line-num';
import { underscoreToCamelcase } from './utils/camelcase-convert';
import axios from 'axios';
import sha1 from 'sha1';
export const prService = {
async getPRByIid(projectsId, owner, mergeId) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}.json`);
let data = underscoreToCamelcase(res.data);
let newData = {
...data,
checkSuites: [],
iid: data.index,
sourceBranch: data.head,
sourceProjectId: projectsId,
// state: "opened",
// targetBranch: "master",
// targetProjectId: 42422,
diff: {
baseCommitSha: data.baseCommitSha,
commitsCount: data.commitNum,
headCommitSha: data.headCommitSha,
startCommitSha: data.mergeBase,
// delLineNum: 379,
// addLineNum: 753,
// createdAt: "2021-05-20T14:27:46+0800",
// filesCount: 22,
// overflow: false,
// startCommitSha: "2566c6dec7756e51f7f16267d9a2a63116ac015b",
// updatedAt: "2021-05-20T14:27:46+0800",
},
};
return newData;
},
async getDiffVersions(projectsId, owner, mergeId) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/versions.json`);
return Array.isArray(res.data.versions) ? underscoreToCamelcase(res.data.versions) : [];
},
async getDiffs(
projectId,
owner,
from,
to,
) {
const res = await axios.get(`/v1/${owner}/42422/compare.json`, { params: { from, to } })
const changeLineNum = calcChangeLineNum(res.diffs);
return {
...res,
...changeLineNum,
};
},
async getDiffOverviews(projectsId, owner, mergeId, versionId) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/versions/${versionId}/diff.json`);
let files = res.data.files;
for (const [i, item] of files.entries()) {
// item.aMode= "100644";
// item.bMode= "0";
// item.markAsRead= true;
// item.tooLarge= false;
// item.updatedAfterRead= false;
item.compareDiffId = versionId;
item.id = item.name;
item.addLineNum = item.addition;
item.binaryFile = item.is_bin;
item.delLineNum = item.deletion;
item.deletedFile = item.is_deleted;
item.newFile = item.is_created;
item.newPath = item.name;
item.oldPath = item.oldname;
item.renamedFile = item.is_renamed;
}
return Array.isArray(res.data.files) ? underscoreToCamelcase(res.data.files) : []
},
async getCommentPack(projectsId, owner, mergeId,params) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/journals.json`,{params});
let journals=underscoreToCamelcase(res.data.journals);
for(const item of journals){
item.author=item.user;
item.author.avatarUrl=item.user.imageUrl?getImageUrlAbsolute(item.user.imageUrl):'';
item.author.username=item.user.name;
item.type='Common';
item.stDiff=item.diff;
item.discussionId=item.parentId
// item.lineType='old';
}
return journals;
},
async editPRComment(projectsId, owner, mergeId, noteId, data) {
const res = await axios.patch(`/v1/${owner}/${projectsId}/pulls/${mergeId}/journals/${noteId}.json`, data);
return res.data;
},
async getDiffById(
projectsId, owner, mergeId, versionId,
params = {}
) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/versions/${versionId}/diff.json`, { params });
let data = res.data;
data.compareDiffId = versionId;
data.id = data.name;
data.addLineNum = data.addition;
data.binaryFile = data.is_bin;
data.delLineNum = data.deletion;
data.deletedFile = data.is_deleted;
data.newFile = data.is_created;
data.newPath = data.name;
data.oldPath = data.oldname;
data.renamedFile = data.is_renamed;
return data;
},
async getFileReadMarks(projectsId, owner, mergeId,) {
const res = await axios.get(`/${owner}/${projectsId}/pulls/${mergeId}/diffs/mark_files.json`);
let data=[];
for (const item of res.data.files) {
if(item.mark_as_read){
item.newPath = item.name;
item.file_path_sha2 = item.file_path_sha;
item.file_path_sha = sha1(item.newPath);
data.push(item);
}
}
return underscoreToCamelcase(data);
},
async markFileAsRead(projectsId, owner, mergeId, data) {
const res = await axios.put(`/${owner}/${projectsId}/pulls/${mergeId}/diffs/mark_file_as_read.json`, data);
return res.data;
},
async markFileAsUnread(projectsId, owner, mergeId, data) {
const res = await axios.put(`/${owner}/${projectsId}/pulls/${mergeId}/diffs/mark_file_as_unread.json`, data);
return res.data;
},
async addComment(projectsId, owner, mergeId, data) {
const res = await axios.post(`/v1/${owner}/${projectsId}/pulls/${mergeId}/journals.json`, data);
let commentData=underscoreToCamelcase(res.data);
commentData.author=commentData.user;
commentData.author.avatarUrl=commentData.user.imageUrl?getImageUrlAbsolute(commentData.user.imageUrl):'';
commentData.author.username=commentData.user.name;
commentData.type='Common';
commentData.stDiff=commentData.diff;
commentData.discussionId=commentData.parentId
return commentData;
},
async deleteComment(projectsId, owner, mergeId,id){
const res = await axios.delete(`/v1/${owner}/${projectsId}/pulls/${mergeId}/journals/${id}.json`);
return res.data;
},
async createReview(projectsId, owner, mergeId, data) {
const res = await axios.post(`/v1/${owner}/${projectsId}/pulls/${mergeId}/reviews.json`, data);
console.log('createReview--data');
if (res.data.id) {
return res.data;
} else {
message.error(res.data && res.data.message);
}
},
async getReviews(projectsId, owner, mergeId,) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/reviews.json`,);
let reviews=underscoreToCamelcase(res.data.reviews);
for(const item of reviews){
item.author=item.reviewer;
item.author.avatarUrl=getImageUrlAbsolute(item.reviewer.imageUrl);
item.author.username=item.reviewer.name;
item.author.webUrl='';
item.body=item.content;
item.pending=false;
item.pullRequestId=mergeId;
}
return reviews;
},
async commitReview(projectsId, owner, mergeId, body) {
return await apiService.put(
`/api/v3/projects/42422/pull_requests/13055/reviews`,
undefined,
{
body,
}
);
},
};

View File

@ -0,0 +1,41 @@
import { apiService } from './api.service';
import { underscoreToCamelcase } from './utils/camelcase-convert';
import axios from 'axios';
export const projectService = {
async getUser() {
return apiService.get(`/api/v3/user`);
},
async getProject(namespace, projectName) {
return (await apiService.get(
`/webapi/projects/${namespace}/${projectName}/`
))
},
async getFileBlob(projectsId, owner,options={}) {
const res = await axios.get(`/${owner}/${projectsId}/sub_entries.json`,{params:options});
return res.data.entries.content;
},
async getLanguages(
projectId,
params
) {
return (await apiService.get(
`/api/v4/projects/42422/languages`,
params,
{
disableResponseConvert: true,
}
))
},
async bulkChangeFiles(
projectsId, owner,data
) {
const res = await axios.post(`/v1/${owner}/${projectsId}/contents/batch`,data);
return res;
},
};

View File

@ -0,0 +1,78 @@
import { apiService } from './api.service';
import { underscoreToCamelcase } from './utils/camelcase-convert';
import axios from 'axios';
import moment from 'moment';
const format = "YYYY-MM-DD HH:mm:ss";
export const repoService = {
async getFileList(
projectId,
refName,
path,
withCommit
) {
return (await apiService.get(
`/webapi/projects/42422/repository/tree`,
{
refName,
path,
withCommit,
}
))
},
async getTreeEntry(projectId, refName, path) {
return (await apiService.get(
`/api/v3/projects/42422/repository/tree_entry`,
{
refName,
path,
}
))
},
async getCodeSymbols(projectId, ref, filepath) {
return await apiService.get(
`/api/v3/projects/42422/repository/file_symbols/${encodeURIComponent(
ref
)}/`,
{
filepath,
}
);
},
async getCodeBlame(projectsId, owner, params) {
const res = await axios.get(`/v1/${owner}/${projectsId}/blame.json`, { params });
let blame_parts = underscoreToCamelcase(res.data.blame_parts);
let blameParts = [];
for (const item of blame_parts) {
let blamePart = {
commit: {
author: item.commit.author,
// authorEmail: "taian.lta@test.com",
authorName: item.commit.author.name,
authoredDate: moment(new Date(item.commit.authoredTime * 1000)).format(format),
committedDate: moment(new Date(item.commit.committedTime * 1000)).format(format),
committer: item.commit.committer,
// committerEmail: "taian.lta@test.com",
committerName: item.commit.committer.name,
createdAt: moment(new Date(item.commit.createdTime * 1000)).format(format),
id: item.commit.sha,
message: item.commit.commitMessage,
// parentIds: null,
// shortId: "7c501a19",
title: item.commit.commitMessage,
// treeHash: null,
},
lines: [{
currentNumber: item.currentNumber,
effectLine: item.effectLine,
}]
};
blameParts.push(blamePart);
}
return blameParts;
},
};

View File

@ -0,0 +1,20 @@
import { mockService } from './mock';
import { underscoreToCamelcase } from '../utils/camelcase-convert';
export async function request(
url,
options,
extraOptions
) {
const data = underscoreToCamelcase(mockService[url]);
console.log('request ==== ', url);
console.log(data)
if (data) {
return data;
} else {
return '文件展示内容只mock了 aaa/package.json';
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,32 @@
import { message as messageTip } from 'antd';
export function createApiError(message = '请求出错', response) {
const err = new Error(message);
err.isApiError = true;
err.response = response;
return err;
}
export function isApiError(e) {
return e&&e.isApiError;
}
/**
* 目前存在一些请求成功也报错的情况
* 返回值可能情况
* 1. success === true 此时只提示有 errorMessage 的情况
* 2. success === false 此时提示包括 message 的信息
* todo: 目前由于intl使用的是react component注入的方式这里暂时无法做到国际化后续等移除 konjac 之后采用全局api方式即可支持国际化
* @param responseData 返回值各种可能
*/
export function successErrorMessage(responseData) {
if (responseData) {
const errorMessage = responseData.error || responseData.errorMessage;
const message = errorMessage || responseData.message;
if (responseData.success === false && message) {
messageTip.error(message);
} else if (errorMessage) {
messageTip.error(errorMessage);
}
}
}

View File

@ -0,0 +1,20 @@
export function calcChangeLineNum(diffs) {
let addLineNum = 0;
let delLineNum = 0;
for (const diff of diffs) {
if (diff.tooLarge) {
continue;
}
const addition = diff.diff.match(/^\+/gm)&&diff.diff.match(/^\+/gm).length || 0;
const deletion = diff.diff.match(/^-/gm)&&diff.diff.match(/^-/gm).length || 0;
diff.addLineNum = addition;
diff.delLineNum = deletion;
addLineNum += addition;
delLineNum += deletion;
}
return {
addLineNum,
delLineNum,
};
}

View File

@ -0,0 +1,35 @@
export function underscoreToCamelcase(obj){
if (Array.isArray(obj)) {
return obj.map((item) => underscoreToCamelcase(item))
}
if (typeof obj !== 'object' || obj === null || obj === undefined) {
return obj;
}
const ret = {};
for (const key in obj) {
if (obj.hasOwnProperty && !obj.hasOwnProperty(key)) {
continue;
}
const newKey = key.replace(/_([a-z])/g, (m) => m[1].toUpperCase());
ret[newKey] = underscoreToCamelcase(obj[key]);
}
return ret
}
export function camelcaseToUnderscore(obj) {
if (Array.isArray(obj)) {
return obj.map((item) => camelcaseToUnderscore(item))
}
if (typeof obj !== 'object' || obj === null || obj === undefined) {
return obj;
}
const ret= {};
for (const key in obj) {
if (obj.hasOwnProperty && !obj.hasOwnProperty(key)) {
continue;
}
const newKey = key.replace(/([a-z][A-Z])/g, (m) => m[0] + '_' + m[1].toLowerCase());
ret[newKey] = camelcaseToUnderscore(obj[key]);
}
return ret
}

View File

@ -0,0 +1,2 @@
export const getLocale = () =>
document.cookie.indexOf('LOCALE=en_US') > -1 ? 'en-US' : 'zh-CN';

View File

@ -0,0 +1,8 @@
export function mapGetSet(map, key, fallback) {
let ret = map.get(key);
if (!ret) {
ret = fallback();
map.set(key, ret);
}
return ret;
}

View File

@ -0,0 +1,682 @@
import React, {
useState,
useMemo,
useRef,
useContext,
useEffect,
useCallback,
createContext,
createElement,
FC,
} from 'react';
import { getLocale } from './mock/utils/locale';
// @ts-ignore
import sha1 from 'sha1';
// @ts-ignore
import memoize from 'lodash/memoize';
import jCookie from 'js-cookie';
import { useRequest } from './hooks';
// import { group, repo, prIid, project } from './meta';
import { prService } from './mock/pr.service.js';
import { projectService } from './mock/project.service.js';
import { mapGetSet } from './mock/utils/map-get-set.js';
import { isApiError } from './mock/utils/api-error.js';
import { mockService } from './mock/request/mock';
import { Base64 } from 'js-base64';
import './style.less';
const NoteType = {
comment: 'Comment',
problem: 'Problem',
}
const NoteState = {
opened: 'opened',
resolved: 'resolved',
invalid: 'invalid',
}
const CharsetName = {
gbk: 'GBK',
utf8: 'UTF-8',
};
function createContainer(useHook, checkNull = false) {
let Context = createContext(null);
const Provider = ({ children }) => {
const value = useHook();
if (checkNull && !value) return null;
return <Context.Provider value={value}>{children}</Context.Provider>;
};
const useContainer = () => React.useContext(Context);
return [Provider, useContainer];
}
let ContextProps = createContext(null);
const useGlobal = () => React.useContext(ContextProps);
const [PrProvider, usePr] = createContainer(() => {
const { params } = useGlobal();
const { projectsId, owner, mergeId } = params;
const data = useRequest(async () => {
const pr = await prService.getPRByIid(projectsId, owner, mergeId);
return { pr }
})
return data;
}, true);
const [SettingProvider, useSetting] = createContainer(() => {
const { project } = useGlobal();
const [gbk, setGBK] = useState(() => project.encoding === 'GBK');
const charsetName = gbk ? CharsetName.gbk : CharsetName.utf8;
const [locale, setLocal] = useState(() => getLocale());
const setLocale = React.useCallback(() => {
jCookie.set('LOCALE', locale === 'en-US' ? 'zh_CN' : 'en_US');
window.location.reload();
}, [locale]);
return {
gbk,
setGBK,
charsetName,
locale,
setLocale,
};
});
//
/* note review /webapi/projects/42422/pull_requests/13055/comments mock
* note格式 参照 types/note.ts
* review 参照 types/review.ts
* review 包含 reviewNotes 内有多个note
*
* 需传递的数据
* noteIdToReplyIdSet noteIdToNote lineToNoteIdSet updateFlag
*/
const useCommentPack = (projectId, prId) => {
const { params } = useGlobal();
const { projectsId, owner, mergeId } = params;
// ACR
const lineToNoteIdSet = useMemo(() => new Map(), []);
// note noteid => note
const noteIdToNote = useMemo(() => new Map(), []);
const noteIdToReviewId = useMemo(() => new Map(), []);
// review reviewid => review
const reviewIdToReview = useMemo(() => new Map(), []);
// 使
const noteIdToReplyIdSet = useMemo(() => new Map(), []);
// review
const pendingNoteIdSet = useMemo(() => new Set(), []);
const recordNoteIdSet = useMemo(() => new Set(), []);
const topLevelCommentNoteIdSet = useMemo(() => new Set(), []);
//
const commentNoteIdSet = useMemo(() => new Set(), []);
const openedProblemNoteIdSet = useMemo(() => new Set(), []);
const [updateFlag, setUpdateFlag] = useState({});
const fetchingRef = useRef(false);
const lastFetchAtRef = useRef();
const pendingReviewRef = useRef();
const [hasPendingReview, setHasPendingReview] = useState(false); // TODO: state
function processNote(note, review = null) {
noteIdToNote.set(note.id, note);
noteIdToReviewId.set(note.id, review && review.id);
if (note.type === NoteType.problem && note.state === NoteState.opened) {
openedProblemNoteIdSet.add(note.id);
} else {
openedProblemNoteIdSet.delete(note.id);
}
if (!note.discussionId) {
if (!note.lineCode && note.system) {
recordNoteIdSet.add(note.id);
} else {
topLevelCommentNoteIdSet.add(note.id);
}
}
if (note.discussionId || note.lineCode) {
commentNoteIdSet.add(note.id);
}
if (note.lineCode) {
const noteIdSet = mapGetSet(
lineToNoteIdSet,
note.lineCode,
() => new Set()
);
noteIdSet.add(note.id);
}
if (note.discussionId) {
const replyIdSet = mapGetSet(
noteIdToReplyIdSet,
note.discussionId,
() => new Set()
);
replyIdSet.add(note.id);
}
if (review && review.pending) {
pendingNoteIdSet.add(note.id);
} else {
pendingNoteIdSet.delete(note.id);
}
}
//
async function doRefresh(force = false, filePath) {
if (fetchingRef.current) {
return;
}
fetchingRef.current = true;
//
const commentPack = await prService.getCommentPack(projectsId, owner, mergeId, { path: filePath, is_full: true });
const reviews = await prService.getReviews(projectsId, owner, mergeId);
// debugger
for (const review of reviews) {
review.reviewNotes = commentPack;
}
const pack = {
committedReviews: reviews,
notes: commentPack,
pendingReview: reviews[0],
};
console.log('pack');
console.log(pack);
// debugger
lastFetchAtRef.current = pack.currentFetchedAt;
let updated = false;
if (force) {
lineToNoteIdSet.clear();
noteIdToNote.clear();
noteIdToReviewId.clear();
reviewIdToReview.clear();
noteIdToReplyIdSet.clear();
pendingNoteIdSet.clear();
recordNoteIdSet.clear();
topLevelCommentNoteIdSet.clear();
commentNoteIdSet.clear();
openedProblemNoteIdSet.clear();
updated = true;
}
for (const note of pack.notes) {
processNote(note);
updated = true;
}
for (const review of pack.committedReviews) {
reviewIdToReview.set(review.id, review);
for (const note of review.reviewNotes) {
processNote(note, review);
}
updated = true;
}
if (pack.pendingReview) {
reviewIdToReview.set(pack.pendingReview.id, pack.pendingReview);
pendingReviewRef.current = pack.pendingReview;
setHasPendingReview(true);
for (const note of pack.pendingReview.reviewNotes) {
processNote(note, pack.pendingReview);
updated = true;
}
} else {
setHasPendingReview(false);
}
if (updated) {
setUpdateFlag({});
}
fetchingRef.current = false;
}
function manualUpdateReview(review) {
reviewIdToReview.set(review.id, review);
if (review.pending) {
pendingReviewRef.current = review;
setHasPendingReview(!!review);
}
setUpdateFlag({});
}
function manualAddNote(note, review = null) {
processNote(note, review);
setUpdateFlag({});
}
function manualRemoveNote(id) {
noteIdToNote.delete(id);
setUpdateFlag({});
}
function manualUpdateNote(note) {
const reviewId = noteIdToReviewId.get(note.id);
const review = reviewId ? reviewIdToReview.get(reviewId) : null;
processNote(note, review);
setUpdateFlag({});
}
function getRelatedFilePathByNoteId(noteId) {
const note = noteIdToNote.get(noteId);
if (!note) return;
let path = note.path;
if (note.discussionId) {
const parent = noteIdToNote.get(note.discussionId);
path = parent && parent.path;
}
return path;
}
//
async function editNote(
noteId, data
) {
const newNote = await prService.editPRComment(projectsId, owner, mergeId, noteId, data);
manualUpdateNote(newNote);
newNote.discussions && newNote.discussions.forEach((note) => manualUpdateNote(note));
}
const pathShaToNoteCount = (function () {
const ret = new Map();
function handleNote(note) {
if (!note || !note.lineCode) return;
const key = note.lineCode.split('_')[0];
ret.set(key, (ret.get(key) || 0) + 1);
}
topLevelCommentNoteIdSet.forEach((noteId) => {
const note = noteIdToNote.get(noteId);
handleNote(note);
});
return ret;
})();
return {
lineToNoteIdSet,
noteIdToNote,
noteIdToReviewId,
reviewIdToReview,
noteIdToReplyIdSet,
pendingNoteIdSet,
recordNoteIdSet,
topLevelCommentNoteIdSet,
openedProblemNoteIdSet,
hasPendingReview,
hasOpenedProblem: openedProblemNoteIdSet.size > 0,
doRefresh,
manualUpdateReview,
manualAddNote,
manualRemoveNote,
manualUpdateNote,
pendingReviewRef,
getRelatedFilePathByNoteId,
editNote,
updateFlag,
pathShaToNoteCount,
setUpdateFlag,
};
};
// note
const [NoteProvider, useNote] = createContainer(() => {
const { params } = useGlobal();
const { projectsId, owner, mergeId } = params;
const { pr: { id: prId } } = usePr();
const commentPack = useCommentPack(projectsId, mergeId);
// useEffect(() => {
// commentPack.doRefresh();
// }, []);
async function addComment(data) {
if (!commentPack.hasPendingReview) {
let reviewData = {
content: data.note,
status: 'common',
}
const review = await prService.createReview(projectsId, owner, mergeId, reviewData);
commentPack.manualUpdateReview(review);
}
const note = await prService.addComment(projectsId, owner, mergeId, data);
commentPack.manualAddNote(note, commentPack.pendingReviewRef.current);
}
async function deleteComment(id) {
const res = await prService.deleteComment(projectsId, owner, mergeId, id);
if (res.status == 0) {
commentPack.manualRemoveNote(id);
}
}
async function commitReview(body) {
if (!commentPack.hasPendingReview) {
let reviewData = {
content: body.note,
status: 'common',
}
await prService.createReview(projectsId, owner, mergeId, reviewData);
}
await prService.commitReview(projectsId, owner, mergeId, body);
await commentPack.doRefresh(true);
}
const activateRef = useRef();
return {
commentPack,
...commentPack,
addComment,
deleteComment,
commitReview,
prId,
activateRef,
};
});
const [ReadMarkProvider, useReadMark] = createContainer(() => {
const { params } = useGlobal();
const { projectsId, owner, mergeId } = params;
const { pr: { id: prId } } = usePr();
const [flag, updateFlag] = useState({});
const readMarks = useRequest(
() => prService.getFileReadMarks(projectsId, owner, mergeId,),
{
deps: [projectsId, owner, mergeId, flag],
}
);
const readMarkMap = useMemo(() => {
const map = new Map();
if (!readMarks) return map;
for (const mark of readMarks) {
mark.markAsRead && map.set(mark.filePathSha, mark);
}
return map;
}, [readMarks]);
const memoizedSha1 = useMemo(() => memoize(sha1), []);
async function markFileAsRead(filePath) {
const data = await prService.markFileAsRead(
projectsId, owner, mergeId,
{ file_path_sha: Base64.encode(filePath) }
);
updateFlag({});
return data;
}
async function markFileAsUnread(filePath) {
const data = await prService.markFileAsUnread(
projectsId, owner, mergeId,
{ file_path_sha: Base64.encode(filePath) }
);
updateFlag({});
return data;
}
const getFileReadMark = useCallback(
(filePath) => {
const filePathSha = memoizedSha1(filePath);
return readMarkMap.get(filePathSha);
},
[readMarkMap]
);
const getFileReadStatus = useCallback(
(filePath) => {
const readMark = getFileReadMark(filePath);
return readMark && readMark.markAsRead && !readMark.updatedAfterRead;
},
[getFileReadMark]
);
return {
readMarks,
readMarkMap,
getFileReadMark,
getFileReadStatus,
markFileAsRead,
markFileAsUnread,
};
});
const [AcrProvider, useAcr] = createContainer(() => {
const { params } = useGlobal();
const { pr } = usePr();
const { charsetName } = useSetting();
const { commentPack } = useNote();
const { projectsId, owner, mergeId } = params;
const [IDEMode, setIDEMode] = useState(true);
const toggleViewerType = useCallback(() => {
setIDEMode((v) => !v);
}, [setIDEMode]);
const [stDiff, setStDiff] = useState({});
const [search, setSearch] = useState(window.location.search);
const [acrflag, setAcrFlag] = useState();
const [versions, setVersions] = useState([]);
const query = useMemo(() => {
const searchParams = new URLSearchParams(search);
const ret = {};
if (searchParams.has('from')) {
ret.from = parseInt(searchParams.get('from'));
}
if (searchParams.has('to')) {
ret.to = parseInt(searchParams.get('to'));
}
return ret;
}, [search]);
function updateQuery(data) {
const searchParam = new URLSearchParams();
if (data.from) {
searchParam.set('from', String(data.from));
}
if (data.to) {
searchParam.set('to', String(data.to));
}
const search = searchParam.toString();
window.history.replaceState(
null,
'',
`${window.location.pathname}${search ? '?' : ''}${search}`
);
setSearch(search);
}
async function getDiffVersions() {
const data = await prService.getDiffVersions(projectsId, owner, mergeId);
if (acrflag && data.length == versions.length) {
setTimeout(() => {
setAcrFlag({});
}, 3000);
}
setVersions(data);
}
useEffect(() => {
getDiffVersions()
}, [acrflag])
const versionMap = useMemo(() => {
const map = new Map();
for (const version of versions) {
map.set(version.id, version);
}
return map;
}, [versions]);
const latestVersionId = versions[0] && versions[versions.length - 1].id || pr.diff.id;
const from = query.from;
const to = query.to || latestVersionId;
const fromVersion = from ? versionMap.get(from) : null;
const toVersion = versionMap.get(to);
console.log('diffpack', from, to, fromVersion, toVersion);
const ignoreWhiteSpace = false;
// @ts-ignore
const diffsPack = useRequest(
async () => {
if (!toVersion) return null;
const isPartial = !!fromVersion || toVersion.id !== latestVersionId;
const options = {
ignoreWhiteSpaceChange: ignoreWhiteSpace,
charsetName,
};
let res;
if (fromVersion) {
res = await prService.getDiffs(
projectsId, owner,
fromVersion.headCommitSha,
toVersion.headCommitSha,
options
);
} else {
if (ignoreWhiteSpace) {
res = await prService.getDiffs(
projectsId, owner,
toVersion.baseCommitSha,
toVersion.headCommitSha,
options
);
} else {
const diffs = await prService.getDiffOverviews(
projectsId, owner,
mergeId,
toVersion.id
);
res = {
diffs,
overflow: false,
addLineNum: toVersion.addLineNum,
delLineNum: toVersion.delLineNum,
};
}
}
return {
...res,
fromVersion,
toVersion,
isPartial,
};
},
{
deps: [fromVersion, toVersion, charsetName],
}
);
useEffect(() => {
commentPack.doRefresh();
}, []);
async function getDiffById(diffId) {
let thisStDiff = await prService.getDiffById(projectsId, owner, mergeId, toVersion.id,
{
filepath: diffId,
});
delete thisStDiff.sections
setStDiff(thisStDiff);
commentPack.doRefresh(false, diffId);
return thisStDiff;
}
async function getFileContent(path, sha, maxSize) {
try {
return await projectService.getFileBlob(projectsId, owner, {
filepath: path,
ref: sha,
});
} catch (e) {
if (isApiError(e) && e.response.status === 413) {
return null;
}
throw e;
}
}
const annotationPacks = [];
let countMap = new Map();
for (const checkSuite of pr.checkSuites) {
if (checkSuite.checkRuns) {
for (const checkRun of checkSuite.checkRuns) {
if (checkRun.annotations) {
for (const annotation of checkRun.annotations) {
const pack = {
annotation,
checkSuite,
};
annotationPacks.push(pack);
countMap.set(
annotation.path,
(countMap.get(annotation.path) || 0) + 1
);
}
}
}
}
}
return {
diffsPack,
versions,
fromVersion,
toVersion,
getDiffById,
stDiff,
getFileContent,
IDEMode,
toggleViewerType,
updateQuery,
setAcrFlag,
annotationPacks,
};
});
export const Provider = (props) => {
const match = props.match;
let project = {};
// let user = mockService['/api/v3/user'];
if (props.projectDetail) {
let details = props.projectDetail;
project = {
...project,
...props.projectDetail,
pathWithNamespace: details.full_name,
}
}
let user = {};
if (props.current_user) {
let current_user = props.current_user;
user = { ...current_user, avatar_url: current_user.image_url, name: current_user.username, };
}
return (
<ContextProps.Provider value={{
project, user, params: match.params
}}>
{/* <GlobalProvider> */}
<PrProvider>
<SettingProvider>
<NoteProvider>
<ReadMarkProvider>
<AcrProvider>{props.children}</AcrProvider>
</ReadMarkProvider>
</NoteProvider>
</SettingProvider>
</PrProvider>
{/* </GlobalProvider> */}
</ContextProps.Provider>
);
};
export { useGlobal, usePr, useNote, useReadMark, useAcr, useSetting };

View File

@ -0,0 +1,62 @@
export const ExtensionCommand ={
acrToggleBlame : 'code.blame.acrToggleBlame',
linkToCommit : 'code.blame.linktocommit',
onActive : 'code.blame.extension.active',
setProjectData : 'code.blame.setProjectData',
getBlameData : 'code.blame.getBlameData',
}
export default class IDEPlugin {
/**
* 插件 ID用于唯一标识插件
*/
PLUGIN_ID = 'ACR_BLAME_PLUGIN';
constructor(
onActive,
linkToCommit,
getBlame
) {
this.onActivate = onActive;
// 跳转
this.linkToCommit = linkToCommit;
// 传递blame数据
this.getBlame = getBlame;
}
/**
* 激活插件
*/
activate = (ctx) => {
const { commands, context } = ctx;
this.commands = commands;
context.subscriptions.push(
commands.registerCommand(ExtensionCommand.onActive, () => {
this.onActivate();
}),
commands.registerCommand(ExtensionCommand.linkToCommit, (params) => {
const { commitId } = params;
this.linkToCommit(commitId);
}),
commands.registerCommand(
ExtensionCommand.getBlameData,
async (sendData) => {
const { projectId, prevSha, nextSha, filePath } = sendData;
// kaitian内uri 和 antcode内不同 多了一个 /
const path = filePath.startsWith('/') ? filePath.slice(1) : filePath;
const response = await this.getBlame(projectId, nextSha, path).then(
(res) => {
return res;
}
);
return response;
}
)
);
};
/**
* 注销插件可在此时机清理副作用
*/
deactivate() {}
}

View File

@ -0,0 +1,109 @@
@c45: rgba(0, 0, 0, 0.45);
@c85: rgba(0, 0, 0, 0.85);
html,
body,
#main {
overflow: visible;
}
#main {
padding: 8px 24px 24px;
width: auto;
height: auto;
background-color: #f7f8fa;
}
.pr-head {
padding: 24px;
background-color: #fff;
margin-bottom: 24px;
}
.controller {
margin-bottom: 24px;
> * {
margin-right: 8px;
}
}
.monaco-editor-hover {
border: none !important;
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
transform: translate(-20px, 0px);
}
.monaco-editor-hover-content .markdown-hover .hover-contents {
padding: 16px;
background: #fff;
h1 {
color: @c85;
font-size: 14px;
line-height: 22px;
}
p {
color: @c45;
}
}
.ide-logo {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.ide-btn {
margin: 4px;
display: inline-block;
}
.ide-btn {
display: inline-flex;
box-shadow: inset 0 0 35px 5px rgba(0, 0, 0, 0.05),
inset 0 2px 1px 1px rgba(255, 255, 255, 0.9),
inset 0 -2px 1px 0 rgba(0, 0, 0, 0.05);
border-radius: 8px;
background: #fefefe;
position: relative;
height: 40px;
width: 40px;
justify-content: center;
align-items: center;
font-size: 20px;
}
// .ide-btn:before {
// box-shadow: 0 0 17.5px 8.75px white;
// border-radius: 118.3px;
// background: white;
// position: absolute;
// margin-left: -50.4px;
// margin-top: -50.4px;
// opacity: 0.2;
// content: "";
// height: 100.8px;
// width: 100.8px;
// left: 50%;
// top: 50%;
// }
}
.ide-logo-img {
width: 150px;
}
.ide-logo-text {
color: #aaa;
margin-top: 0.5em;
font-size: 14px;
&:nth-child(2) {
margin-left: 2em;
}
&:nth-child(3) {
margin-left: 2em;
}
&:nth-child(4) {
margin-left: 4em;
}
}

View File

@ -1,8 +1,30 @@
.monaco-editor .view-overlays .current-line{
.monaco-editor .view-overlays .current-line {
border: none !important;
background-color: rgba(48,232,132,0.15);
background-color: rgba(48, 232, 132, 0.15);
}
.monaco-editor .margin-view-overlays .current-line{
background-color: rgba(48,232,132,0.15);
.monaco-editor .margin-view-overlays .current-line {
background-color: rgba(48, 232, 132, 0.15);
}
.branchTable .margin-view-overlays {
border-right: none !important;
background-color: #fcfcfc !important;
}
.editorBorderBox {
position: relative;
}
.ide-tool-bar {
position: absolute;
z-index: 10;
right: 114px;
top: -37px;
display: flex;
align-items: center;
}
.ant-switch-checked {
background-color: #466aff;
}
.read-more{
font-size: 18px;
margin:0 10px 0 15px;
color: #666;
}
.branchTable .margin-view-overlays{border-right: none !important; background-color: #fcfcfc !important;}

View File

@ -0,0 +1,724 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'css-language-features-worker',
version: '1.53.0-patch.1',
},
packageJSON: {
name: 'css-language-features-worker',
publisher: 'alex',
version: '1.53.0-patch.1',
displayName: '%displayName%',
description: '%description%',
icon: 'icons/css.png',
activationEvents: [
'onLanguage:css',
'onLanguage:less',
'onLanguage:scss',
'onCommand:_css.applyCodeAction',
],
kaitianContributes: {
workerMain: 'client/dist/browser/cssClientMain.js',
},
contributes: {
configuration: [
{
order: 22,
id: 'css',
title: '%css.title%',
properties: {
'css.customData': {
type: 'array',
markdownDescription: '%css.customData.desc%',
default: [],
items: {
type: 'string',
},
scope: 'resource',
},
'css.completion.triggerPropertyValueCompletion': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%css.completion.triggerPropertyValueCompletion.desc%',
},
'css.completion.completePropertyWithSemicolon': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%css.completion.completePropertyWithSemicolon.desc%',
},
'css.validate': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%css.validate.desc%',
},
'css.lint.compatibleVendorPrefixes': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.compatibleVendorPrefixes.desc%',
},
'css.lint.vendorPrefix': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%css.lint.vendorPrefix.desc%',
},
'css.lint.duplicateProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.duplicateProperties.desc%',
},
'css.lint.emptyRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%css.lint.emptyRules.desc%',
},
'css.lint.importStatement': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.importStatement.desc%',
},
'css.lint.boxModel': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%css.lint.boxModel.desc%',
},
'css.lint.universalSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%css.lint.universalSelector.desc%',
},
'css.lint.zeroUnits': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.zeroUnits.desc%',
},
'css.lint.fontFaceProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription: '%css.lint.fontFaceProperties.desc%',
},
'css.lint.hexColorLength': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%css.lint.hexColorLength.desc%',
},
'css.lint.argumentsInColorFunction': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%css.lint.argumentsInColorFunction.desc%',
},
'css.lint.unknownProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%css.lint.unknownProperties.desc%',
},
'css.lint.validProperties': {
type: 'array',
uniqueItems: true,
items: {
type: 'string',
},
scope: 'resource',
default: [],
description: '%css.lint.validProperties.desc%',
},
'css.lint.ieHack': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.ieHack.desc%',
},
'css.lint.unknownVendorSpecificProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.unknownVendorSpecificProperties.desc%',
},
'css.lint.propertyIgnoredDueToDisplay': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription:
'%css.lint.propertyIgnoredDueToDisplay.desc%',
},
'css.lint.important': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%css.lint.important.desc%',
},
'css.lint.float': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%css.lint.float.desc%',
},
'css.lint.idSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.idSelector.desc%',
},
'css.lint.unknownAtRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%css.lint.unknownAtRules.desc%',
},
'css.trace.server': {
type: 'string',
scope: 'window',
enum: ['off', 'messages', 'verbose'],
default: 'off',
description: '%css.trace.server.desc%',
},
},
},
{
id: 'scss',
order: 24,
title: '%scss.title%',
properties: {
'scss.completion.triggerPropertyValueCompletion': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%scss.completion.triggerPropertyValueCompletion.desc%',
},
'scss.completion.completePropertyWithSemicolon': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%scss.completion.completePropertyWithSemicolon.desc%',
},
'scss.validate': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%scss.validate.desc%',
},
'scss.lint.compatibleVendorPrefixes': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.compatibleVendorPrefixes.desc%',
},
'scss.lint.vendorPrefix': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%scss.lint.vendorPrefix.desc%',
},
'scss.lint.duplicateProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.duplicateProperties.desc%',
},
'scss.lint.emptyRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%scss.lint.emptyRules.desc%',
},
'scss.lint.importStatement': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.importStatement.desc%',
},
'scss.lint.boxModel': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%scss.lint.boxModel.desc%',
},
'scss.lint.universalSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%scss.lint.universalSelector.desc%',
},
'scss.lint.zeroUnits': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.zeroUnits.desc%',
},
'scss.lint.fontFaceProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription: '%scss.lint.fontFaceProperties.desc%',
},
'scss.lint.hexColorLength': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%scss.lint.hexColorLength.desc%',
},
'scss.lint.argumentsInColorFunction': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%scss.lint.argumentsInColorFunction.desc%',
},
'scss.lint.unknownProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%scss.lint.unknownProperties.desc%',
},
'scss.lint.validProperties': {
type: 'array',
uniqueItems: true,
items: {
type: 'string',
},
scope: 'resource',
default: [],
description: '%scss.lint.validProperties.desc%',
},
'scss.lint.ieHack': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.ieHack.desc%',
},
'scss.lint.unknownVendorSpecificProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.unknownVendorSpecificProperties.desc%',
},
'scss.lint.propertyIgnoredDueToDisplay': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription:
'%scss.lint.propertyIgnoredDueToDisplay.desc%',
},
'scss.lint.important': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%scss.lint.important.desc%',
},
'scss.lint.float': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%scss.lint.float.desc%',
},
'scss.lint.idSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.idSelector.desc%',
},
'scss.lint.unknownAtRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%scss.lint.unknownAtRules.desc%',
},
},
},
{
id: 'less',
order: 23,
type: 'object',
title: '%less.title%',
properties: {
'less.completion.triggerPropertyValueCompletion': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%less.completion.triggerPropertyValueCompletion.desc%',
},
'less.completion.completePropertyWithSemicolon': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%less.completion.completePropertyWithSemicolon.desc%',
},
'less.validate': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%less.validate.desc%',
},
'less.lint.compatibleVendorPrefixes': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.compatibleVendorPrefixes.desc%',
},
'less.lint.vendorPrefix': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%less.lint.vendorPrefix.desc%',
},
'less.lint.duplicateProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.duplicateProperties.desc%',
},
'less.lint.emptyRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%less.lint.emptyRules.desc%',
},
'less.lint.importStatement': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.importStatement.desc%',
},
'less.lint.boxModel': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%less.lint.boxModel.desc%',
},
'less.lint.universalSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%less.lint.universalSelector.desc%',
},
'less.lint.zeroUnits': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.zeroUnits.desc%',
},
'less.lint.fontFaceProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription: '%less.lint.fontFaceProperties.desc%',
},
'less.lint.hexColorLength': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%less.lint.hexColorLength.desc%',
},
'less.lint.argumentsInColorFunction': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%less.lint.argumentsInColorFunction.desc%',
},
'less.lint.unknownProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%less.lint.unknownProperties.desc%',
},
'less.lint.validProperties': {
type: 'array',
uniqueItems: true,
items: {
type: 'string',
},
scope: 'resource',
default: [],
description: '%less.lint.validProperties.desc%',
},
'less.lint.ieHack': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.ieHack.desc%',
},
'less.lint.unknownVendorSpecificProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.unknownVendorSpecificProperties.desc%',
},
'less.lint.propertyIgnoredDueToDisplay': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription:
'%less.lint.propertyIgnoredDueToDisplay.desc%',
},
'less.lint.important': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%less.lint.important.desc%',
},
'less.lint.float': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%less.lint.float.desc%',
},
'less.lint.idSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.idSelector.desc%',
},
'less.lint.unknownAtRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%less.lint.unknownAtRules.desc%',
},
},
},
],
configurationDefaults: {
'[css]': {
'editor.suggest.insertMode': 'replace',
},
'[scss]': {
'editor.suggest.insertMode': 'replace',
},
'[less]': {
'editor.suggest.insertMode': 'replace',
},
},
jsonValidation: [
{
fileMatch: '*.css-data.json',
url: 'https://raw.githubusercontent.com/microsoft/vscode-css-languageservice/master/docs/customData.schema.json',
},
{
fileMatch: 'package.json',
url: './schemas/package.schema.json',
},
],
workerMain: 'client/dist/browser/cssClientMain.js',
},
},
defaultPkgNlsJSON: {
displayName: 'CSS Language Features',
description: 'Provides rich language support for CSS, LESS and SCSS files.',
'css.title': 'CSS',
'css.customData.desc':
'A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.',
'css.completion.triggerPropertyValueCompletion.desc':
'By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.',
'css.completion.completePropertyWithSemicolon.desc':
'Insert semicolon at end of line when completing CSS properties',
'css.lint.argumentsInColorFunction.desc': 'Invalid number of parameters.',
'css.lint.boxModel.desc':
'Do not use `width` or `height` when using `padding` or `border`.',
'css.lint.compatibleVendorPrefixes.desc':
'When using a vendor-specific prefix make sure to also include all other vendor-specific properties.',
'css.lint.duplicateProperties.desc':
'Do not use duplicate style definitions.',
'css.lint.emptyRules.desc': 'Do not use empty rulesets.',
'css.lint.float.desc':
'Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.',
'css.lint.fontFaceProperties.desc':
'`@font-face` rule must define `src` and `font-family` properties.',
'css.lint.hexColorLength.desc':
'Hex colors must consist of three or six hex numbers.',
'css.lint.idSelector.desc':
'Selectors should not contain IDs because these rules are too tightly coupled with the HTML.',
'css.lint.ieHack.desc':
'IE hacks are only necessary when supporting IE7 and older.',
'css.lint.important.desc':
'Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.',
'css.lint.importStatement.desc':
'Import statements do not load in parallel.',
'css.lint.propertyIgnoredDueToDisplay.desc':
'Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.',
'css.lint.universalSelector.desc':
'The universal selector (`*`) is known to be slow.',
'css.lint.unknownAtRules.desc': 'Unknown at-rule.',
'css.lint.unknownProperties.desc': 'Unknown property.',
'css.lint.validProperties.desc':
'A list of properties that are not validated against the `unknownProperties` rule.',
'css.lint.unknownVendorSpecificProperties.desc':
'Unknown vendor specific property.',
'css.lint.vendorPrefix.desc':
'When using a vendor-specific prefix, also include the standard property.',
'css.lint.zeroUnits.desc': 'No unit for zero needed.',
'css.trace.server.desc':
'Traces the communication between VS Code and the CSS language server.',
'css.validate.title': 'Controls CSS validation and problem severities.',
'css.validate.desc': 'Enables or disables all validations.',
'less.title': 'LESS',
'less.completion.triggerPropertyValueCompletion.desc':
'By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.',
'less.completion.completePropertyWithSemicolon.desc':
'Insert semicolon at end of line when completing CSS properties',
'less.lint.argumentsInColorFunction.desc': 'Invalid number of parameters.',
'less.lint.boxModel.desc':
'Do not use `width` or `height` when using `padding` or `border`.',
'less.lint.compatibleVendorPrefixes.desc':
'When using a vendor-specific prefix make sure to also include all other vendor-specific properties.',
'less.lint.duplicateProperties.desc':
'Do not use duplicate style definitions.',
'less.lint.emptyRules.desc': 'Do not use empty rulesets.',
'less.lint.float.desc':
'Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.',
'less.lint.fontFaceProperties.desc':
'`@font-face` rule must define `src` and `font-family` properties.',
'less.lint.hexColorLength.desc':
'Hex colors must consist of three or six hex numbers.',
'less.lint.idSelector.desc':
'Selectors should not contain IDs because these rules are too tightly coupled with the HTML.',
'less.lint.ieHack.desc':
'IE hacks are only necessary when supporting IE7 and older.',
'less.lint.important.desc':
'Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.',
'less.lint.importStatement.desc':
'Import statements do not load in parallel.',
'less.lint.propertyIgnoredDueToDisplay.desc':
'Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.',
'less.lint.universalSelector.desc':
'The universal selector (`*`) is known to be slow.',
'less.lint.unknownAtRules.desc': 'Unknown at-rule.',
'less.lint.unknownProperties.desc': 'Unknown property.',
'less.lint.validProperties.desc':
'A list of properties that are not validated against the `unknownProperties` rule.',
'less.lint.unknownVendorSpecificProperties.desc':
'Unknown vendor specific property.',
'less.lint.vendorPrefix.desc':
'When using a vendor-specific prefix, also include the standard property.',
'less.lint.zeroUnits.desc': 'No unit for zero needed.',
'less.validate.title': 'Controls LESS validation and problem severities.',
'less.validate.desc': 'Enables or disables all validations.',
'scss.title': 'SCSS (Sass)',
'scss.completion.triggerPropertyValueCompletion.desc':
'By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.',
'scss.completion.completePropertyWithSemicolon.desc':
'Insert semicolon at end of line when completing CSS properties',
'scss.lint.argumentsInColorFunction.desc': 'Invalid number of parameters.',
'scss.lint.boxModel.desc':
'Do not use `width` or `height` when using `padding` or `border`.',
'scss.lint.compatibleVendorPrefixes.desc':
'When using a vendor-specific prefix make sure to also include all other vendor-specific properties.',
'scss.lint.duplicateProperties.desc':
'Do not use duplicate style definitions.',
'scss.lint.emptyRules.desc': 'Do not use empty rulesets.',
'scss.lint.float.desc':
'Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.',
'scss.lint.fontFaceProperties.desc':
'`@font-face` rule must define `src` and `font-family` properties.',
'scss.lint.hexColorLength.desc':
'Hex colors must consist of three or six hex numbers.',
'scss.lint.idSelector.desc':
'Selectors should not contain IDs because these rules are too tightly coupled with the HTML.',
'scss.lint.ieHack.desc':
'IE hacks are only necessary when supporting IE7 and older.',
'scss.lint.important.desc':
'Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.',
'scss.lint.importStatement.desc':
'Import statements do not load in parallel.',
'scss.lint.propertyIgnoredDueToDisplay.desc':
'Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.',
'scss.lint.universalSelector.desc':
'The universal selector (`*`) is known to be slow.',
'scss.lint.unknownAtRules.desc': 'Unknown at-rule.',
'scss.lint.unknownProperties.desc': 'Unknown property.',
'scss.lint.validProperties.desc':
'A list of properties that are not validated against the `unknownProperties` rule.',
'scss.lint.unknownVendorSpecificProperties.desc':
'Unknown vendor specific property.',
'scss.lint.vendorPrefix.desc':
'When using a vendor-specific prefix, also include the standard property.',
'scss.lint.zeroUnits.desc': 'No unit for zero needed.',
'scss.validate.title': 'Controls SCSS validation and problem severities.',
'scss.validate.desc': 'Enables or disables all validations.',
'css.colorDecorators.enable.deprecationMessage':
'The setting `css.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.',
'scss.colorDecorators.enable.deprecationMessage':
'The setting `scss.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.',
'less.colorDecorators.enable.deprecationMessage':
'The setting `less.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.',
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
mode: 'public',
};

View File

@ -0,0 +1,56 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'editor-plugin-blame',
version: '0.2.6',
},
packageJSON: {
name: 'editor-plugin-blame',
publisher: 'alex-ext-public',
version: '0.2.6',
repository: {
type: 'git',
url: 'http://code.alipay.com/yxy167584/editor-plugin-blame.git',
},
displayName: 'editor-plugin-blame',
description: ' ',
activationEvents: ['*'],
kaitianContributes: {
workerMain: './out/worker/index.js',
},
contributes: {
commands: [
{
command: 'code.blame.toggleBlame',
title: '查看blame',
},
{
command: 'code.blame.acrToggleBlame',
title: 'blame',
},
{
command: 'code.blame.linktocommit',
title: 'hover详情跳转',
},
],
views: {},
menus: {
'editor/title': [
{
command: 'code.blame.acrToggleBlame',
type: 'checkbox',
group: 'navigation',
toggledWhen: 'acr_blame_context',
when: 'resourceScheme =~ /^git$|^diff$/',
},
],
},
workerMain: './out/worker/index.js',
},
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
webAssets: ['package.json', 'out/worker/index.js'],
mode: 'public',
};

View File

@ -0,0 +1,273 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'html-language-features-worker',
version: '1.53.0-patch.1',
},
packageJSON: {
name: 'html-language-features-worker',
publisher: 'alex',
version: '1.53.0-patch.1',
displayName: '%displayName%',
description: '%description%',
icon: 'icons/html.png',
activationEvents: ['onLanguage:html', 'onLanguage:handlebars'],
kaitianContributes: {
workerMain: 'client/dist/browser/htmlClientMain.js',
},
contributes: {
configuration: {
id: 'html',
order: 20,
type: 'object',
title: 'HTML',
properties: {
'html.customData': {
type: 'array',
markdownDescription: '%html.customData.desc%',
default: [],
items: {
type: 'string',
},
scope: 'resource',
},
'html.format.enable': {
type: 'boolean',
scope: 'window',
default: true,
description: '%html.format.enable.desc%',
},
'html.format.wrapLineLength': {
type: 'integer',
scope: 'resource',
default: 120,
description: '%html.format.wrapLineLength.desc%',
},
'html.format.unformatted': {
type: ['string', 'null'],
scope: 'resource',
default: 'wbr',
markdownDescription: '%html.format.unformatted.desc%',
},
'html.format.contentUnformatted': {
type: ['string', 'null'],
scope: 'resource',
default: 'pre,code,textarea',
markdownDescription: '%html.format.contentUnformatted.desc%',
},
'html.format.indentInnerHtml': {
type: 'boolean',
scope: 'resource',
default: false,
markdownDescription: '%html.format.indentInnerHtml.desc%',
},
'html.format.preserveNewLines': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.format.preserveNewLines.desc%',
},
'html.format.maxPreserveNewLines': {
type: ['number', 'null'],
scope: 'resource',
default: null,
markdownDescription: '%html.format.maxPreserveNewLines.desc%',
},
'html.format.indentHandlebars': {
type: 'boolean',
scope: 'resource',
default: false,
markdownDescription: '%html.format.indentHandlebars.desc%',
},
'html.format.endWithNewline': {
type: 'boolean',
scope: 'resource',
default: false,
description: '%html.format.endWithNewline.desc%',
},
'html.format.extraLiners': {
type: ['string', 'null'],
scope: 'resource',
default: 'head, body, /html',
markdownDescription: '%html.format.extraLiners.desc%',
},
'html.format.wrapAttributes': {
type: 'string',
scope: 'resource',
default: 'auto',
enum: [
'auto',
'force',
'force-aligned',
'force-expand-multiline',
'aligned-multiple',
'preserve',
'preserve-aligned',
],
enumDescriptions: [
'%html.format.wrapAttributes.auto%',
'%html.format.wrapAttributes.force%',
'%html.format.wrapAttributes.forcealign%',
'%html.format.wrapAttributes.forcemultiline%',
'%html.format.wrapAttributes.alignedmultiple%',
'%html.format.wrapAttributes.preserve%',
'%html.format.wrapAttributes.preservealigned%',
],
description: '%html.format.wrapAttributes.desc%',
},
'html.format.wrapAttributesIndentSize': {
type: ['number', 'null'],
scope: 'resource',
default: null,
description: '%html.format.wrapAttributesIndentSize.desc%',
},
'html.format.templating': {
type: ['boolean'],
scope: 'resource',
default: false,
description: '%html.format.templating.desc%',
},
'html.format.unformattedContentDelimiter': {
type: ['string'],
scope: 'resource',
default: '',
markdownDescription:
'%html.format.unformattedContentDelimiter.desc%',
},
'html.suggest.html5': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.suggest.html5.desc%',
},
'html.validate.scripts': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.validate.scripts%',
},
'html.validate.styles': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.validate.styles%',
},
'html.autoClosingTags': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.autoClosingTags%',
},
'html.hover.documentation': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.hover.documentation%',
},
'html.hover.references': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.hover.references%',
},
'html.mirrorCursorOnMatchingTag': {
type: 'boolean',
scope: 'resource',
default: false,
description: '%html.mirrorCursorOnMatchingTag%',
deprecationMessage:
'%html.mirrorCursorOnMatchingTagDeprecationMessage%',
},
'html.trace.server': {
type: 'string',
scope: 'window',
enum: ['off', 'messages', 'verbose'],
default: 'off',
description: '%html.trace.server.desc%',
},
},
},
configurationDefaults: {
'[html]': {
'editor.suggest.insertMode': 'replace',
},
'[handlebars]': {
'editor.suggest.insertMode': 'replace',
},
},
jsonValidation: [
{
fileMatch: '*.html-data.json',
url: 'https://raw.githubusercontent.com/microsoft/vscode-html-languageservice/master/docs/customData.schema.json',
},
{
fileMatch: 'package.json',
url: './schemas/package.schema.json',
},
],
workerMain: 'client/dist/browser/htmlClientMain.js',
},
},
defaultPkgNlsJSON: {
displayName: 'HTML Language Features',
description: 'Provides rich language support for HTML and Handlebar files',
'html.customData.desc':
'A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-html-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its HTML support for the custom HTML tags, attributes and attribute values you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.',
'html.format.enable.desc': 'Enable/disable default HTML formatter.',
'html.format.wrapLineLength.desc':
'Maximum amount of characters per line (0 = disable).',
'html.format.unformatted.desc':
"List of tags, comma separated, that shouldn't be reformatted. `null` defaults to all tags listed at https://www.w3.org/TR/html5/dom.html#phrasing-content.",
'html.format.contentUnformatted.desc':
"List of tags, comma separated, where the content shouldn't be reformatted. `null` defaults to the `pre` tag.",
'html.format.indentInnerHtml.desc':
'Indent `<head>` and `<body>` sections.',
'html.format.preserveNewLines.desc':
'Controls whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text.',
'html.format.maxPreserveNewLines.desc':
'Maximum number of line breaks to be preserved in one chunk. Use `null` for unlimited.',
'html.format.indentHandlebars.desc':
'Format and indent `{{#foo}}` and `{{/foo}}`.',
'html.format.endWithNewline.desc': 'End with a newline.',
'html.format.extraLiners.desc':
'List of tags, comma separated, that should have an extra newline before them. `null` defaults to `"head, body, /html"`.',
'html.format.wrapAttributes.desc': 'Wrap attributes.',
'html.format.wrapAttributes.auto':
'Wrap attributes only when line length is exceeded.',
'html.format.wrapAttributes.force': 'Wrap each attribute except first.',
'html.format.wrapAttributes.forcealign':
'Wrap each attribute except first and keep aligned.',
'html.format.wrapAttributes.forcemultiline': 'Wrap each attribute.',
'html.format.wrapAttributes.alignedmultiple':
'Wrap when line length is exceeded, align attributes vertically.',
'html.format.wrapAttributes.preserve': 'Preserve wrapping of attributes',
'html.format.wrapAttributes.preservealigned':
'Preserve wrapping of attributes but align.',
'html.format.templating.desc':
'Honor django, erb, handlebars and php templating language tags.',
'html.format.unformattedContentDelimiter.desc':
'Keep text content together between this string.',
'html.format.wrapAttributesIndentSize.desc':
"Alignment size when using 'force aligned' and 'aligned multiple' in `#html.format.wrapAttributes#` or `null` to use the default indent size.",
'html.suggest.html5.desc':
'Controls whether the built-in HTML language support suggests HTML5 tags, properties and values.',
'html.trace.server.desc':
'Traces the communication between VS Code and the HTML language server.',
'html.validate.scripts':
'Controls whether the built-in HTML language support validates embedded scripts.',
'html.validate.styles':
'Controls whether the built-in HTML language support validates embedded styles.',
'html.autoClosingTags': 'Enable/disable autoclosing of HTML tags.',
'html.mirrorCursorOnMatchingTag':
'Enable/disable mirroring cursor on matching HTML tag.',
'html.mirrorCursorOnMatchingTagDeprecationMessage':
'Deprecated in favor of `editor.linkedEditing`',
'html.hover.documentation':
'Show tag and attribute documentation in hover.',
'html.hover.references': 'Show references to MDN in hover.',
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
mode: 'public',
};

View File

@ -0,0 +1,35 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'ide-dark-theme',
version: '2.4.0',
},
packageJSON: {
publisher: 'kaitian',
name: 'ide-dark-theme',
version: '2.4.0',
displayName: 'IDE UI Theme',
description: 'IDE UI Theme',
contributes: {
themes: [
{
id: 'opensumi-dark',
label: 'OpenSumi Dark',
uiTheme: 'vs-dark',
path: './themes/dark/plus.json',
},
{
id: 'opensumi-light',
label: 'OpenSumi Light',
uiTheme: 'vs',
path: './themes/light/plus.json',
},
],
},
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
webAssets: [],
mode: 'public',
};

View File

@ -0,0 +1,147 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'json-language-features-worker',
version: '1.53.0-patch.1',
},
packageJSON: {
name: 'json-language-features-worker',
publisher: 'alex',
version: '1.53.0-patch.1',
displayName: '%displayName%',
description: '%description%',
icon: 'icons/json.png',
activationEvents: ['onLanguage:json', 'onLanguage:jsonc'],
kaitianContributes: {
workerMain: 'client/dist/browser/jsonClientMain.js',
},
contributes: {
configuration: {
id: 'json',
order: 20,
type: 'object',
title: 'JSON',
properties: {
'json.schemas': {
type: 'array',
scope: 'resource',
description: '%json.schemas.desc%',
items: {
type: 'object',
default: {
fileMatch: ['/myfile'],
url: 'schemaURL',
},
properties: {
url: {
type: 'string',
default: '/user.schema.json',
description: '%json.schemas.url.desc%',
},
fileMatch: {
type: 'array',
items: {
type: 'string',
default: 'MyFile.json',
description: '%json.schemas.fileMatch.item.desc%',
},
minItems: 1,
description: '%json.schemas.fileMatch.desc%',
},
schema: {
$ref: 'http://json-schema.org/draft-07/schema#',
description: '%json.schemas.schema.desc%',
},
},
},
},
'json.format.enable': {
type: 'boolean',
scope: 'window',
default: true,
description: '%json.format.enable.desc%',
},
'json.trace.server': {
type: 'string',
scope: 'window',
enum: ['off', 'messages', 'verbose'],
default: 'off',
description: '%json.tracing.desc%',
},
'json.colorDecorators.enable': {
type: 'boolean',
scope: 'window',
default: true,
description: '%json.colorDecorators.enable.desc%',
deprecationMessage:
'%json.colorDecorators.enable.deprecationMessage%',
},
'json.maxItemsComputed': {
type: 'number',
default: 5000,
description: '%json.maxItemsComputed.desc%',
},
'json.schemaDownload.enable': {
type: 'boolean',
default: true,
description: '%json.enableSchemaDownload.desc%',
tags: ['usesOnlineServices'],
},
},
},
configurationDefaults: {
'[json]': {
'editor.quickSuggestions': {
strings: true,
},
'editor.suggest.insertMode': 'replace',
},
'[jsonc]': {
'editor.quickSuggestions': {
strings: true,
},
'editor.suggest.insertMode': 'replace',
},
},
jsonValidation: [
{
fileMatch: '*.schema.json',
url: 'http://json-schema.org/draft-07/schema#',
},
],
workerMain: 'client/dist/browser/jsonClientMain.js',
},
},
defaultPkgNlsJSON: {
displayName: 'JSON Language Features',
description: 'Provides rich language support for JSON files.',
'json.schemas.desc':
'Associate schemas to JSON files in the current project',
'json.schemas.url.desc':
'A URL to a schema or a relative path to a schema in the current directory',
'json.schemas.fileMatch.desc':
"An array of file patterns to match against when resolving JSON files to schemas. `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.",
'json.schemas.fileMatch.item.desc':
"A file pattern that can contain '*' to match against when resolving JSON files to schemas.",
'json.schemas.schema.desc':
'The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.',
'json.format.enable.desc': 'Enable/disable default JSON formatter',
'json.tracing.desc':
'Traces the communication between VS Code and the JSON language server.',
'json.colorDecorators.enable.desc': 'Enables or disables color decorators',
'json.colorDecorators.enable.deprecationMessage':
'The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.',
'json.schemaResolutionErrorMessage': 'Unable to resolve schema.',
'json.clickToRetry': 'Click to retry.',
'json.maxItemsComputed.desc':
'The maximum number of outline symbols and folding regions computed (limited for performance reasons).',
'json.maxItemsExceededInformation.desc':
'Show notification when exceeding the maximum number of outline symbols and folding regions.',
'json.enableSchemaDownload.desc':
'When enabled, JSON schemas can be fetched from http and https locations.',
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
mode: 'public',
};

View File

@ -0,0 +1,346 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'markdown-language-features-worker',
version: '1.53.0-patch.1',
},
packageJSON: {
name: 'markdown-language-features-worker',
publisher: 'alex',
version: '1.53.0-patch.1',
displayName: '%displayName%',
description: '%description%',
icon: 'icon.png',
activationEvents: [
'onLanguage:markdown',
'onCommand:markdown.preview.toggleLock',
'onCommand:markdown.preview.refresh',
'onCommand:markdown.showPreview',
'onCommand:markdown.showPreviewToSide',
'onCommand:markdown.showLockedPreviewToSide',
'onCommand:markdown.showSource',
'onCommand:markdown.showPreviewSecuritySelector',
'onCommand:markdown.api.render',
'onWebviewPanel:markdown.preview',
'onCustomEditor:vscode.markdown.preview.editor',
],
kaitianContributes: {
workerMain: './dist/browser/extension.js',
},
contributes: {
commands: [
{
command: 'markdown.showPreview',
title: '%markdown.preview.title%',
category: 'Markdown',
icon: {
light: './media/preview-light.svg',
dark: './media/preview-dark.svg',
},
},
{
command: 'markdown.showPreviewToSide',
title: '%markdown.previewSide.title%',
category: 'Markdown',
icon: '$(open-preview)',
},
{
command: 'markdown.showLockedPreviewToSide',
title: '%markdown.showLockedPreviewToSide.title%',
category: 'Markdown',
icon: '$(open-preview)',
},
{
command: 'markdown.showSource',
title: '%markdown.showSource.title%',
category: 'Markdown',
icon: '$(go-to-file)',
},
{
command: 'markdown.showPreviewSecuritySelector',
title: '%markdown.showPreviewSecuritySelector.title%',
category: 'Markdown',
},
{
command: 'markdown.preview.refresh',
title: '%markdown.preview.refresh.title%',
category: 'Markdown',
},
{
command: 'markdown.preview.toggleLock',
title: '%markdown.preview.toggleLock.title%',
category: 'Markdown',
},
],
menus: {
'editor/title': [
{
command: 'markdown.showPreviewToSide',
when: 'editorLangId == markdown && !notebookEditorFocused',
alt: 'markdown.showPreview',
group: 'navigation',
},
{
command: 'markdown.showSource',
when: 'markdownPreviewFocus',
group: 'navigation',
},
{
command: 'markdown.preview.refresh',
when: 'markdownPreviewFocus',
group: '1_markdown',
},
{
command: 'markdown.preview.toggleLock',
when: 'markdownPreviewFocus',
group: '1_markdown',
},
{
command: 'markdown.showPreviewSecuritySelector',
when: 'markdownPreviewFocus',
group: '1_markdown',
},
],
'explorer/context': [
{
command: 'markdown.showPreview',
when: 'resourceLangId == markdown',
group: 'navigation',
},
],
'editor/title/context': [
{
command: 'markdown.showPreview',
when: 'resourceLangId == markdown',
group: '1_open',
},
],
commandPalette: [
{
command: 'markdown.showPreview',
when: 'editorLangId == markdown && !notebookEditorFocused',
group: 'navigation',
},
{
command: 'markdown.showPreviewToSide',
when: 'editorLangId == markdown && !notebookEditorFocused',
group: 'navigation',
},
{
command: 'markdown.showLockedPreviewToSide',
when: 'editorLangId == markdown && !notebookEditorFocused',
group: 'navigation',
},
{
command: 'markdown.showSource',
when: 'markdownPreviewFocus',
group: 'navigation',
},
{
command: 'markdown.showPreviewSecuritySelector',
when: 'editorLangId == markdown && !notebookEditorFocused',
},
{
command: 'markdown.showPreviewSecuritySelector',
when: 'markdownPreviewFocus',
},
{
command: 'markdown.preview.toggleLock',
when: 'markdownPreviewFocus',
},
{
command: 'markdown.preview.refresh',
when: 'editorLangId == markdown && !notebookEditorFocused',
},
{
command: 'markdown.preview.refresh',
when: 'markdownPreviewFocus',
},
],
},
keybindings: [
{
command: 'markdown.showPreview',
key: 'shift+ctrl+v',
mac: 'shift+cmd+v',
when: 'editorLangId == markdown && !notebookEditorFocused',
},
{
command: 'markdown.showPreviewToSide',
key: 'ctrl+k v',
mac: 'cmd+k v',
when: 'editorLangId == markdown && !notebookEditorFocused',
},
],
configuration: {
type: 'object',
title: 'Markdown',
order: 20,
properties: {
'markdown.styles': {
type: 'array',
items: {
type: 'string',
},
default: [],
description: '%markdown.styles.dec%',
scope: 'resource',
},
'markdown.preview.breaks': {
type: 'boolean',
default: false,
description: '%markdown.preview.breaks.desc%',
scope: 'resource',
},
'markdown.preview.linkify': {
type: 'boolean',
default: true,
description: '%markdown.preview.linkify%',
scope: 'resource',
},
'markdown.preview.fontFamily': {
type: 'string',
default:
"-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif",
description: '%markdown.preview.fontFamily.desc%',
scope: 'resource',
},
'markdown.preview.fontSize': {
type: 'number',
default: 14,
description: '%markdown.preview.fontSize.desc%',
scope: 'resource',
},
'markdown.preview.lineHeight': {
type: 'number',
default: 1.6,
description: '%markdown.preview.lineHeight.desc%',
scope: 'resource',
},
'markdown.preview.scrollPreviewWithEditor': {
type: 'boolean',
default: true,
description: '%markdown.preview.scrollPreviewWithEditor.desc%',
scope: 'resource',
},
'markdown.preview.markEditorSelection': {
type: 'boolean',
default: true,
description: '%markdown.preview.markEditorSelection.desc%',
scope: 'resource',
},
'markdown.preview.scrollEditorWithPreview': {
type: 'boolean',
default: true,
description: '%markdown.preview.scrollEditorWithPreview.desc%',
scope: 'resource',
},
'markdown.preview.doubleClickToSwitchToEditor': {
type: 'boolean',
default: true,
description: '%markdown.preview.doubleClickToSwitchToEditor.desc%',
scope: 'resource',
},
'markdown.preview.openMarkdownLinks': {
type: 'string',
default: 'inPreview',
description:
'%configuration.markdown.preview.openMarkdownLinks.description%',
scope: 'resource',
enum: ['inPreview', 'inEditor'],
enumDescriptions: [
'%configuration.markdown.preview.openMarkdownLinks.inPreview%',
'%configuration.markdown.preview.openMarkdownLinks.inEditor%',
],
},
'markdown.links.openLocation': {
type: 'string',
default: 'currentGroup',
description:
'%configuration.markdown.links.openLocation.description%',
scope: 'resource',
enum: ['currentGroup', 'beside'],
enumDescriptions: [
'%configuration.markdown.links.openLocation.currentGroup%',
'%configuration.markdown.links.openLocation.beside%',
],
},
'markdown.trace': {
type: 'string',
enum: ['off', 'verbose'],
default: 'off',
description: '%markdown.trace.desc%',
scope: 'window',
},
},
},
configurationDefaults: {
'[markdown]': {
'editor.wordWrap': 'on',
'editor.quickSuggestions': false,
},
},
jsonValidation: [
{
fileMatch: 'package.json',
url: './schemas/package.schema.json',
},
],
'markdown.previewStyles': [
'./media/markdown.css',
'./media/highlight.css',
],
'markdown.previewScripts': ['./media/index.js'],
workerMain: './dist/browser/extension.js',
},
},
defaultPkgNlsJSON: {
displayName: 'Markdown Language Features',
description: 'Provides rich language support for Markdown.',
'markdown.preview.breaks.desc':
"Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a <br> for newlines inside paragraphs.",
'markdown.preview.linkify':
'Enable or disable conversion of URL-like text to links in the markdown preview.',
'markdown.preview.doubleClickToSwitchToEditor.desc':
'Double click in the markdown preview to switch to the editor.',
'markdown.preview.fontFamily.desc':
'Controls the font family used in the markdown preview.',
'markdown.preview.fontSize.desc':
'Controls the font size in pixels used in the markdown preview.',
'markdown.preview.lineHeight.desc':
'Controls the line height used in the markdown preview. This number is relative to the font size.',
'markdown.preview.markEditorSelection.desc':
'Mark the current editor selection in the markdown preview.',
'markdown.preview.scrollEditorWithPreview.desc':
'When a markdown preview is scrolled, update the view of the editor.',
'markdown.preview.scrollPreviewWithEditor.desc':
'When a markdown editor is scrolled, update the view of the preview.',
'markdown.preview.title': 'Open Preview',
'markdown.previewSide.title': 'Open Preview to the Side',
'markdown.showLockedPreviewToSide.title': 'Open Locked Preview to the Side',
'markdown.showSource.title': 'Show Source',
'markdown.styles.dec':
"A list of URLs or local paths to CSS style sheets to use from the markdown preview. Relative paths are interpreted relative to the folder open in the explorer. If there is no open folder, they are interpreted relative to the location of the markdown file. All '\\' need to be written as '\\\\'.",
'markdown.showPreviewSecuritySelector.title':
'Change Preview Security Settings',
'markdown.trace.desc': 'Enable debug logging for the markdown extension.',
'markdown.preview.refresh.title': 'Refresh Preview',
'markdown.preview.toggleLock.title': 'Toggle Preview Locking',
'configuration.markdown.preview.openMarkdownLinks.description':
'Controls how links to other markdown files in the markdown preview should be opened.',
'configuration.markdown.preview.openMarkdownLinks.inEditor':
'Try to open links in the editor',
'configuration.markdown.preview.openMarkdownLinks.inPreview':
'Try to open links in the markdown preview',
'configuration.markdown.links.openLocation.description':
'Controls where links in markdown files should be opened.',
'configuration.markdown.links.openLocation.currentGroup':
'Open links in the active editor group.',
'configuration.markdown.links.openLocation.beside':
'Open links beside the active editor.',
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
mode: 'public',
};

File diff suppressed because it is too large Load Diff

59
src/forge/Newfile/ide-plugin.js Executable file
View File

@ -0,0 +1,59 @@
export const ExtensionCommand={
toggleBlame : 'code.blame.toggleBlame',
linkToCommit : 'code.blame.linktocommit',
onActive : 'code.blame.extension.active',
setPerference : 'code.blame.setPerference',
}
export default class IDEPlugin {
// // @ts-ignore
// commands: IPluginAPI['commands'];
// onActivate: () => void;
// linkToCommit: (commitId: string) => void;
/**
* 插件 ID用于唯一标识插件
*/
PLUGIN_ID = 'antcode-communication';
constructor(onActive, linkToCommit) {
this.onActivate = onActive;
this.linkToCommit = linkToCommit;
}
/**
* 激活插件
*/
activate = (ctx) => {
const { commands, context } = ctx;
this.commands = commands;
context.subscriptions.push(
commands.registerCommand(ExtensionCommand.onActive, () => {
this.onActivate();
}),
commands.registerCommand(ExtensionCommand.linkToCommit, (params) => {
const { commitId } = params;
this.linkToCommit(commitId);
})
);
};
/**
* 注销插件可在此时机清理副作用
*/
deactivate() {}
/**
* 修改配置项
* @param name 配置项名称
* @param value
* @param global 是否全局生效
*/
setPerference(name, value, global) {
this.commands.executeCommand(
ExtensionCommand.setPerference,
name,
value,
!!global
);
}
}

View File

@ -1,11 +1,11 @@
import React, { Component } from "react";
import {UnControlled as CodeMirror} from 'react-codemirror2';
import React, { Component, Fragment } from "react";
import { UnControlled as CodeMirror } from 'react-codemirror2';
import 'codemirror/addon/selection/active-line.js';
import 'codemirror/mode/javascript/javascript.js';
import 'codemirror/mode/clike/clike';
import 'codemirror/mode/css/css';
import UserSubmitComponent from "./UserSubmitComponent";
import CloudIDE from "./cloudIDE";
import "./index.css";
import "./editor.css";
@ -15,26 +15,28 @@ class m_editor extends Component {
super(props);
this.state = {
editorValue: this.props.content,
changeValue:this.props.content,
prevHeight:0
changeValue: this.props.content,
prevHeight: 0,
};
}
componentDidUpdate=(prevProps)=>{
if(prevProps && this.props && this.props.content !== prevProps.content){
componentDidUpdate = (prevProps) => {
if (prevProps && this.props && this.props.content !== prevProps.content) {
this.setState({
editorValue:this.props.content
editorValue: this.props.content
})
}
}
changeEditor = (editorValue,data) => {
changeEditor = (editorValue, data) => {
this.setState({
changeValue:editorValue.getValue(),
changeValue: editorValue.getValue(),
});
};
render() {
const { editorValue , changeValue } = this.state;
const { readOnly, editorType, currentBranch , descName , checkName } = this.props;
const { editorValue, changeValue } = this.state;
const { readOnly, editorType, currentBranch, descName, checkName, detail, match: { params },filepath,ideTheme } = this.props;
console.log('params')
console.log(params)
const editor_options = {
lineNumbers: "on",
lineWrapping: true, //强制换行
@ -42,7 +44,7 @@ class m_editor extends Component {
lineHeight: 24,
renderLineHighlight: "line",
revealHorizontalRightPadding: 5,
placeholder:"请输入内容",
placeholder: "请输入内容",
readOnly: readOnly,
cursorStyle: readOnly ? "underline-thin" : "line",
folding: true,
@ -50,24 +52,29 @@ class m_editor extends Component {
automaticLayout: true, // 自适应布局
overviewRulerBorder: false, // 不要滚动条的边框
scrollBeyondLastLine: false, // 取消代码后面一大段空白
styleActiveLine:true,//光标代码高亮
styleActiveLine: true,//光标代码高亮
minimap: {
// 不要小地图
enabled: false,
},
};
let download_url = detail && detail.download_url;
return (
<React.Fragment>
<Fragment>
<div className="editorBorderBox">
<CodeMirror
placeholder="请输入内容"
value={editorValue}
options={editor_options}
onChange={this.changeEditor}
/>
{!readOnly ?
<CodeMirror
placeholder="请输入内容"
value={editorValue}
options={editor_options}
onChange={this.changeEditor}
/> :
<CloudIDE download_url={download_url} params={params} filepath={filepath&&filepath.startsWith('/')?filepath.slice(1):filepath}/>
}
</div>
{!readOnly && (
<div className="editorBorderSubmitBox" style={{padding:"20px"}}>
<div className="editorBorderSubmitBox" style={{ padding: "20px" }}>
<UserSubmitComponent
{...this.props}
{...this.state}
@ -78,9 +85,9 @@ class m_editor extends Component {
descName={descName}
checkName={checkName}
></UserSubmitComponent>
</div>
</div>
)}
</React.Fragment>
</Fragment>
);
}
}

View File

@ -168,7 +168,7 @@ class Milepost extends Component {
return (
<Spin spinning={spinings}>
<div className="main" style={{minHeight:"400px"}}>
<div className="main mt20" style={{minHeight:"400px"}}>
<div style={{ display: this.state.display }}>
<div className="tagdiv" >
<span>里程碑{data && data.issue_tags_count}已创建</span>

View File

@ -651,7 +651,7 @@ class order extends Component {
begin, end, checkedValue, all,search
} = this.state;
return (
<div className="main" style={{padding:"0px"}}>
<div className="main mt20" style={{padding:"0px"}}>
<div style={{padding:"10px 20px 0px 20px"}}>
<div className="topWrapper" style={{ paddingTop: "10px" }}>
<ul className="topWrapper_type">

View File

@ -13,10 +13,20 @@ const Data = Loadable({
loader: () => import('./data'),
loading: Loading,
})
//
const Reposyncer = Loadable({
loader: () => import('./reposyncer'),
loading: Loading,
})
function ServerIndex(props){
return(
<div className="panels">
<Switch {...props}>
<Route path="/:owner/:projectsId/server/reposyncer"
render={
() => (<Reposyncer {...props}/>)
}
></Route>
<Route path="/:owner/:projectsId/server/:id"
render={
() => (<Data {...props}/>)

View File

@ -1,4 +1,5 @@
import React,{useState , useEffect} from 'react';
import { Link } from 'react-router-dom';
import AgreementModal from './agreementModal';
function Main(props){
@ -9,7 +10,9 @@ function Main(props){
const { current_user , resetUserInfo, projectDetail, showNotification } = props;
useEffect(()=>{
if(current_user){
if(current_user && !current_user.login){
props.history.push(`/login?go_page=/${owner}/${projectsId}/server`);
}else{
setHas_trace_user(current_user.has_trace_user);
}
},[current_user])
@ -43,6 +46,16 @@ function Main(props){
<a onClick={openDetail} className="btnhover">查看详情</a>
</span>
</li>
<li>
<span className="servername">
<img src={require('./img/logo.png')} alt=""/>
<Link to={`/${owner}/${projectsId}/server/reposyncer`}>Reposyncer仓库同步系统</Link>
</span>
<p className="task-hide-2 serverdesc">支持不同开源托管平台自动同步推送/拉取相关代码实现多平台项目同步开发功能</p>
<span className="serverbtn">
<Link to={`/${owner}/${projectsId}/server/reposyncer`} className="btnhover">查看详情</Link>
</span>
</li>
</ul>
</div>
)

View File

@ -3,7 +3,7 @@ import DataEmpty from './dataEmpty';
import DetectionModal from './detectionModal';
import { Table, Spin, message } from 'antd';
import axios from 'axios';
import img from '../Images/img1.png';
function Data(props) {
const [detectionVisible, setDetectionVisible] = useState(false);
@ -16,6 +16,7 @@ function Data(props) {
const [lookResultUrl, setLookResultUrl] = useState(undefined);
const [openResultTaskId, setOpenResultTaskId] = useState(undefined);
const [viewBase, setViewBase] = useState('https://cjntest.trustie.net/');
const [operateTime, setOperateTime] = useState(undefined);
const { owner, projectsId } = props.match.params;
const { isManager, projectDetail, history } = props;
@ -37,7 +38,9 @@ function Data(props) {
}
}).then(result => {
if (result) {
if (Array.isArray(result.data.data)) {
if(result.data.code === 501){
setOperateTime(result.data.data.operate_time);
}else if (Array.isArray(result.data.data)) {
setDataSource(result.data.data);
setRelayCount(result.data.left_tasks_count);
setLookResultUrl(result.data.view_base);
@ -59,11 +62,13 @@ function Data(props) {
}
useEffect(() => {
if(projectDetail && projectDetail.forked_from_project_id){
history.go(-1);
}else{
setSpining(true);
Init();
if(projectDetail){
if(projectDetail.forked_from_project_id){
history.go(-1);
}else{
setSpining(true);
Init();
}
}
}, [projectDetail])
@ -76,8 +81,6 @@ function Data(props) {
function iframeHeight(e){
console.log('iframeHeight');
console.log(e.data);
if (e && e.data && viewBase && e.origin && viewBase.indexOf(e.origin)>-1) {
let myHeight = JSON.parse(e.data);
if (document.querySelector("#htmlIframe")) {
@ -138,9 +141,7 @@ function Data(props) {
function iframeLoad() {
try {
let myIframe = document.getElementById("htmlIframe");
console.log('myIframe', myIframe, myIframe.contentDocument);
if (myIframe.contentDocument) {
console.log(myIframe.contentDocument.querySelector('.el-main'));
myIframe.height = myIframe.contentDocument.querySelector('.el-main').clientHeight + 260;
// myIframe.contentDocument.querySelector('.admin-body-container').style.overflow = 'hidden';
}
@ -162,12 +163,17 @@ function Data(props) {
/>
<div className="servertitle">
<span className="systitle">重晴鸟代码溯源系统</span>
{isManager && <a className="btnhover" onClick={createCheck}>新建分析</a>}
{!operateTime && isManager && <a className="btnhover" onClick={createCheck}>新建分析</a>}
</div>
<Spin spinning={spining}>
<div style={{ minHeight: "400px" }}>
{operateTime && <div className='operateBox mt25'>
<img src={img} alt='' width={130}/>
<div className='font-20 mt20 mb5'>系统维护中</div>
<div className='font-17'>预计<span className='timeBox'>{operateTime}小时后</span>代码溯源系统将恢复正常访问给您带来不便敬请谅解!</div>
</div>}
{
dataSource && dataSource.length > 0 &&
!operateTime && dataSource && dataSource.length > 0 &&
<div>
<ul className="dataUl">
<li className="dataUlhead">
@ -237,7 +243,7 @@ function Data(props) {
</div>
}
{
(dataSource === null || (dataSource && dataSource.length === 0)) &&
!operateTime && (dataSource === null || (dataSource && dataSource.length === 0)) &&
<DataEmpty />
}
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="23.589" height="23.589" viewBox="0 0 23.589 23.589">
<g id="gitee" transform="translate(0)">
<g id="Group" transform="translate(0 0)">
<ellipse id="Combined-Shape" cx="11.795" cy="11.795" rx="11.795" ry="11.795" fill="#c71d23"/>
<path id="G" d="M32.458,25.178h-6.7a.583.583,0,0,0-.583.582v1.456a.582.582,0,0,0,.582.583h4.078a.582.582,0,0,1,.582.582h0v.146h0v.146a1.747,1.747,0,0,1-1.747,1.747H23.138a.582.582,0,0,1-.582-.582V24.3A1.747,1.747,0,0,1,24.3,22.557h8.153a.584.584,0,0,0,.583-.582V20.518a.582.582,0,0,0-.582-.583H24.3A4.368,4.368,0,0,0,19.935,24.3v8.154a.582.582,0,0,0,.582.582h8.591a3.931,3.931,0,0,0,3.931-3.931V25.76A.582.582,0,0,0,32.458,25.178Z" transform="translate(-14.693 -14.693)" fill="#fff" fill-rule="evenodd"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,3 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="23.585" height="23.587" viewBox="0 0 23.585 23.587">
<path id="路径_46" data-name="路径 46" d="M11.831,0A11.914,11.914,0,0,0,.136,10.156,12.116,12.116,0,0,0,8.089,23.568c.6.111.806-.267.806-.586V20.925c-3.306.735-4-1.626-4-1.626a3.23,3.23,0,0,0-1.315-1.774c-1.068-.742.087-.742.087-.742A2.492,2.492,0,0,1,5.473,18.03,2.539,2.539,0,0,0,6.99,19.266a2.481,2.481,0,0,0,1.927-.226,2.593,2.593,0,0,1,.727-1.618c-2.63-.3-5.391-1.344-5.391-5.938A4.734,4.734,0,0,1,5.466,8.239a4.5,4.5,0,0,1,.116-3.2s1-.327,3.255,1.24a10.968,10.968,0,0,1,5.929,0c2.26-1.566,3.248-1.24,3.248-1.24a4.475,4.475,0,0,1,.145,3.177,4.734,4.734,0,0,1,1.213,3.244c0,4.647-2.768,5.664-5.406,5.938a2.893,2.893,0,0,1,.806,2.227c0,1.618,0,2.925,0,3.318s.211.7.814.579a12.12,12.12,0,0,0,7.824-13.379A11.917,11.917,0,0,0,11.831,0Z" transform="translate(0.018 0)" fill="#1a1414" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 952 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -451,4 +451,14 @@
}
}
}
}
.operateBox{
color: #333;
text-align: center;
background-color:#fafcff;
border-radius:4px 4px 0px 0px;
padding: 50px 0 80px;
.timeBox{
color: rgba(70, 106, 255, 1)
}
}

View File

@ -0,0 +1,75 @@
import React, {useState} from 'react';
import { Button, Modal, Input, Select, message } from 'antd';
import axios from 'axios';
const {Option} = Select;
function DeleteBox({owner, projectsId, visible, setVisible, createJobBy, branchOptions, reload}) {
const [error, setError] = useState(undefined);
const [errorByOther, setErrorByOther] = useState(undefined);
const [branch, setBranch] = useState(undefined);
const [branchByOther, setBranchByOther] = useState(undefined);
//
function createNew(){
if(branch && branchByOther){
//
const param = {
gitlink_branch: branch,
job_type: 'TwoWay',
github_branch: undefined,
gitee_branch: undefined
}
createJobBy === 'Github' ? (param.github_branch = branchByOther) : (param.gitee_branch = branchByOther)
axios.post(`/${owner}/${projectsId}/synchronizes/create_jobs.json`,param).then(res=>{
if(res && res.data.message === "success"){
setBranch(undefined);
setBranchByOther(undefined);
reload && reload(Math.random());
message.success('新建成功');
setVisible(false);
}
})
}else{
!branch && setError('请选择仓库分支')
!branchByOther && setErrorByOther('请输入仓库分支')
}
}
return(
<Modal
title="新建同步分支"
visible={visible}
onCancel={()=>{setVisible(false)}}
footer={<div><Button style={{width: '104px', height:'36px'}} onClick={()=>{setVisible(false)}}>取消</Button><Button type="primary" style={{width: '104px', height:'36px', marginLeft: '40px'}} onClick={createNew}>确认</Button></div>}
width={550}
className="cancelBound createJobBox"
>
<div className="itemBox mt10">
<label className="labelBox font-16"><i className="iconfont icon-a-bitian2x font-12"></i> {createJobBy}分支:</label>
<Input placeholder="请输入分支名称" className="inputBox" value={branchByOther} onChange={(e)=>setBranchByOther(e.target.value)} maxLength={50}/>
<div className="errorBox">{errorByOther}</div>
</div>
<div className="itemBox mt30 mb20">
<label className="labelBox font-16"><i className="iconfont icon-a-bitian2x font-12"></i> GitLink分支:</label>
<Select
value={branch}
onSelect={(e) => {setBranch(e)}}
showSearch
className="inputBox"
dropdownMatchSelectWidth={false}
dropdownClassName="overlihide"
placeholder="请选择仓库分支"
>
{branchOptions && branchOptions.map((item, key) => {
return (
<Option key={key + 1} value={item.name}>
{item.name}
</Option>
)})}
</Select>
<div className="errorBox">{error}</div>
</div>
</Modal>
)
}
export default DeleteBox;

View File

@ -0,0 +1,97 @@
import React, {forwardRef} from "react";
import { Button, Form, Input, message } from "antd";
import gitHub from '../../img/github.png';
import gitee from '../../img/gitee.png';
import '../index.scss';
import axios from "axios";
function EditStore(props){
const { form, history} = props;
const { owner , projectsId } = props.match.params;
const { getFieldDecorator, validateFields , setFieldsValue, setFields } = form;
function submit() {
validateFields((error,values)=>{
setFields({
'github_address':{value: values.github_address, errors: null},
'github_token':{value: values.github_token, errors: null},
'gitee_address':{value: values.gitee_address, errors: null},
'gitee_token':{value: values.gitee_token, errors: null}
})
if(!error){
//
for(let i in values){
if(!values[i]){
delete values[i];
}
}
const keysArr = Object.keys(values);
if(keysArr.indexOf('github_address') === -1 && keysArr.indexOf('gitee_address') === -1){
// githubgitee
form.setFields({github_address: {value:values.github_address,errors:[new Error('请至少输入一个地址')]}});
form.setFields({gitee_address: {value:values.gitee_address,errors:[new Error('请至少输入一个地址')]}});
}else if(keysArr.indexOf('github_address') !== -1 && keysArr.indexOf('github_token') === -1){
form.setFields({github_token: {value:values.github_token,errors:[new Error('请输入Github的授权token')]}});
}else if(keysArr.indexOf('gitee_address') !== -1 && keysArr.indexOf('gitee_token') === -1){
form.setFields({gitee_token: {value:values.gitee_token,errors:[new Error('请输入Gitee的授权token')]}});
}else{
axios.post(`/${owner}/${projectsId}/synchronizes.json`,values).then(res=>{
if(res && res.data.message === "success"){
message.success('绑定成功');
window.location.href = `/${owner}/${projectsId}/server/reposyncer`;
}else{
// message.error('');
}
})
}
}
})
}
return <div className="storeListBox mt20">
<div className="registerBox">
<Form>
<div className="storeTitle pb10 mb15"><img src={gitHub} alt="" className="storeLogo"/></div>
<Form.Item label="Github同步仓库地址" className="storeFormItem">
{getFieldDecorator("github_address",{
rules:[]
})(
<Input placeholder="请输入Github目标版本库地址" className="storeInput"/>
)}
</Form.Item>
<Form.Item label="Github同步仓库授权验证" className="storeFormItem">
{getFieldDecorator("github_token",{
rules:[]
})(
<Input addonBefore="token" placeholder="请输入Github的授权token" className="storeInput"/>
)}
</Form.Item>
<div className="storeTitle pb10 mb15 pt20"><img src={gitee} alt="" className="storeLogo"/></div>
<Form.Item label="Gitee同步仓库地址" className="storeFormItem">
{getFieldDecorator("gitee_address",{
rules:[]
})(
<Input placeholder="请输入Gitee目标版本库地址" className="storeInput"/>
)}
</Form.Item>
<Form.Item label="Gitee同步仓库授权验证" className="storeFormItem">
{getFieldDecorator("gitee_token",{
rules:[]
})(
<Input addonBefore="token" placeholder="请输入Gitee的授权token" className="storeInput"/>
)}
</Form.Item>
<div className="tipStoreBox">
1在开启仓库同步前您需要绑定并授权目标同步仓库<br/>
2完成仓库绑定后需对仓库内的分支进行二次绑定当已绑定的分支有代码提交代码推送变更将实时同步更新至其他仓库<br/>
3目前仅提供Github与Gitee平台的仓库同步功能每个平台支持同时同步一个仓库
</div>
<Form.Item>
<Button type="primary" style={{width: '112px', height: '36px'}} onClick={submit}>确认绑定</Button>
<Button style={{width: '112px', height: '36px'}} className="ml40" onClick={()=>{history.goBack(-1)}}>取消</Button>
</Form.Item>
</Form>
</div>
</div>
}
export default Form.create()(forwardRef(EditStore));

View File

@ -0,0 +1,165 @@
import React, {useState} from "react";
import { Table, Modal, Button, message } from 'antd';
import { Link } from "react-router-dom";
import gitHub1 from '../../img/github2.svg';
import gitee1 from '../../img/gitee1.svg';
import anniu from '../../img/anniu.png';
import anniu2 from '../../img/anniu2.png';
import '../index.scss';
import { useEffect } from "react";
import axios from "axios";
import CreateJobModal from './createJobModal';
import Nodata from "../../../Nodata";
function RecordList(props){
const { owner , projectsId, type } = props.match.params;
const { storeDetail} = props;
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
// table
const [current, setCurrent] = useState(1);
const [total, setTotal] = useState(0);
const [pageSize, setPageSize] = useState(10);
const [expandedRowKeys, setExpandedRowKeys] = useState([]);
const [deleteRecordId, setDeleteRecordId] = useState(undefined);
const [visible, setVisible] = useState(false);
const [reload, setReload] = useState(undefined);
const [visibleNew, setVisibleNew] = useState(false);
const [branchOptions, setBranchOptions] = useState([]);
const [logInfo, setLogInfo]= useState([]);
useEffect(()=>{
axios.get(`/${owner}/${projectsId}/pulls/get_branches.json`, {}).then(res=>{
res && setBranchOptions(res.data);
})
}, [])
useEffect(()=>{
//
axios.get(`/${owner}/${projectsId}/synchronizes/jobs.json`, {params: {type: type, limit: pageSize, page: current}}).then(res=>{
if(res && res.data && res.data.message === "success"){
//
if(current !== 1 && res.data.data.length === 0){
setCurrent(current-1);
}else{
res.data.data.map(item=>{
item.create_time = item.create_time.replace('T', ' ')
})
setData(res.data.data);
setTotal(res.data.count);
}
}
})
},[reload, pageSize, current])
// errorBox
let columns = [
{ title: '序号', dataIndex: 'index', className:"recordColumns", render: (text, item, index) => <span>{(current-1)*pageSize+index + 1}</span> },
{ title: type === 'github' ? 'Github分支' : 'Gitee分支', dataIndex: type === 'github' ? 'github_branch': 'gitee_branch', className:"recordColumns taskName"},
{ title: 'GitLink分支', dataIndex: 'gitlink_branch', className:"recordColumns"},
{ title: '创建时间', dataIndex: 'create_time', className:"primaryColor recordColumns"},
{ title: '同步状态', dataIndex: 'status', className:"recordColumns", render: ()=><span className="accomplish statusBox">开启中</span>},
{ title: '操作', dataIndex: 'action', align: 'center', className:"primaryColor recordColumns", render: (text, item)=><span className="deleteRecord" onClick={()=>{setVisible(true);setDeleteRecordId(item.id);}}>删除</span>},
]
const customExpandIcon = (props) => {
if (props.expanded) {
return <a className='primaryColor' onClick={e => {
props.onExpand(props.record, e);
}}>查看日志<img alt="" src={anniu2} style={{width: '18px'}} className="ml5"/></a>
} else {
return <a className='primaryColor' onClick={e => {
axios.get(`/${owner}/${projectsId}/synchronizes/job_logs.json`, {params:{job_id: props.record.id}}).then(res=>{
if(res && res.data){
setLogInfo(res.data.data);
props.onExpand(props.record, e);
}
})
}}>查看日志<img alt="" src={anniu} style={{width: '18px'}} className="ml5"/></a>
}
}
const expandRow = (record) => {
return logInfo && logInfo.length > 0 ? <div className="expandBox">
{logInfo.map(item=>{
return <div className="expandCont" key={item.id}>
{item.create_time + ' [' + item.log_type + '] ' + item.log}
</div>
})}
</div> : <Nodata _html="暂无数据"/>}
//
function onExpand(expanded, record){
const keys = new Set(expandedRowKeys);
if(expanded){
keys.add(record.id);
}else{
keys.delete(record.id);
}
setExpandedRowKeys(Array.from(keys));
}
// pagesize
function onShowSizeChange(current, pageSize){
window.scrollTo(0, 0);
setCurrent(1);
setPageSize(pageSize);
}
//
function changePage(page, pageSize){
window.scrollTo(0, 0);
setCurrent(page);
}
//
function deleteRecord(){
deleteRecordId && axios.delete(`/${owner}/${projectsId}/synchronizes/delete_job.json`, {data: {job_id: deleteRecordId}}).then(res=>{
if(res && res.data.message === "success"){
setReload(Math.random());
message.success('删除成功');
setVisible(false);
}
})
}
return <div className="storeListBox">
<div className="font-16">
<Link to={`/${owner}/${projectsId}/server/reposyncer`} className="blueSpan">仓库绑定</Link>
<span> &gt; 同步分支</span>
</div>
<div className="headBox font-16 pl15 mt20 mb10">
<img src={type === 'github' ? gitHub1 : gitee1} alt="" className="mr10 mb5"/>
<span>{type === 'github' ? 'Github' : 'Gitee'}仓库地址</span>
{storeDetail && <a className="ml15 blueSpan" href={type === 'github' ? storeDetail.github_address : storeDetail.gitee_address} target="_blank">{type === 'github' ? storeDetail.github_address : storeDetail.gitee_address}</a>}
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but createJobBut" onClick={()=>{setVisibleNew(true);}}>新建同步分支</Button>
</div>
<Table
className="storeListTable"
loading={loading}
columns={columns}
dataSource={data}
expandedRowRender={expandRow}
expandIconColumnIndex={5}
expandIconAsCell={false}
expandIcon={customExpandIcon}
rowKey={'id'}
expandedRowKeys={expandedRowKeys}
onExpand={onExpand}
pagination={{current: current, pageSize: pageSize, total: total, showSizeChanger: true, onShowSizeChange:onShowSizeChange, showQuickJumper: true, onChange: changePage}}
/>
<Modal
title="删除同步分支"
visible={visible}
onCancel={()=>{setVisible(false)}}
footer={<div><Button style={{width: '90px', height:'36px'}} onClick={()=>{setVisible(false)}}>取消</Button><Button className="okBut" style={{width: '90px', height:'36px'}} onClick={deleteRecord}>确认删除</Button></div>}
width={535}
className="cancelBound"
>
<div className="bTilModal font-16"><span className="errorRedSpan font-18 mr20 mt20 ml15">!</span>确认删除此同步分支</div>
<div className="sTilModal"> 删除同步分支后系统将清除此条同步数据及日志对应分支也将停止自动同步</div>
</Modal>
<CreateJobModal owner={owner} projectsId={projectsId} visible={visibleNew} setVisible={setVisibleNew} createJobBy={type === 'github' ? 'Github' : 'Gitee'} branchOptions={branchOptions} reload={setReload}/>
</div>
}
export default RecordList;

View File

@ -0,0 +1,89 @@
import React, { useEffect, useState} from "react";
import { Button, Modal, Icon, message, Select, Input, Tooltip } from "antd";
import { Link } from "react-router-dom";
import gitHub1 from '../../img/github2.svg';
import gitee1 from '../../img/gitee1.svg';
import logo from '../../img/logo2.png';
import '../index.scss';
import axios from "axios";
import CreateJobModal from './createJobModal';
function StoreList(props){
const { storeDetail} = props;
const { owner , projectsId } = props.match.params;
const [visible, setVisible] = useState(false);
const [visibleNew, setVisibleNew] = useState(false);
const [createJobBy, setCreateJobBy] = useState("Github");
const [branchOptions, setBranchOptions] = useState([]);
useEffect(()=>{
axios.get(`/${owner}/${projectsId}/pulls/get_branches.json`, {}).then(res=>{
res && setBranchOptions(res.data);
})
}, [])
//
function updateStore(){
axios.delete(`/${owner}/${projectsId}/synchronizes/delete.json`).then(res=>{
if(res && res.data.message === "success"){
message.success('取消绑定成功');
setVisible(false);
props.history.push(`/${owner}/${projectsId}/server`)
}
})
}
return <div className="storeListBox">
<div className="headBox font-16 pl15">Reposyncer仓库同步系统
{storeDetail !== null && <Tooltip title="Reposyncer仓库同步系统提供跨托管平台的项目协同开发同步功能。支持用户在任何一个托管平台上的代码提交、代码推送、合并请求等操作自动同步至其他托管平台。不仅增加每个开源项目与开发者的流量也使不同平台的开源项目维护与更新变得方便与快捷" overlayStyle={{width: 400}}><span className="helpBox1 font-12 ml10">?</span></Tooltip>}
</div>
{/* 空数据 */}
{!storeDetail && <div className="nullStoreBox mt25">
<img src={logo} alt="" className="loBox mt50"/>
<p className="font-22 mt10">欢迎使用跨平台代码同步系统</p>
<div className="introBox font-15">跨平台代码同步系统提供跨托管平台的项目协同开发同步功能支持用户在任何一个托管平台上的代码提交代码推送合并请求等操作自动同步至其他托管平台不仅增加每个开源项目与开发者的流量也使不同平台的开源项目维护与更新变得方便与快捷</div>
<div className="borBox"></div>
<Button type="primary" style={{width: '112px', height: '36px'}}><Link to={`/${owner}/${projectsId}/server/reposyncer/store/edit`}>开始体验</Link></Button>
</div>}
{/* 已绑定仓库信息 */}
{storeDetail && <div className="listStore mt20">
<div className="storeTitle pb5 mb5 font-18">已绑定仓库地址<Icon type="exclamation-circle" style={{color: '#466aff'}} className="ml10 font-14"/><span className="ml5 font-14" style={{fontWeight: 'normal'}}>对已绑定的仓库请添加同步分支实现分支的跨平台双向同步</span></div>
{storeDetail && storeDetail.github_address && <div className="showStoreInfo dashedBor">
<div className="storeInfoBox">
<div className="font-15 sTil"><img src={gitHub1} alt="" className="mr10"/><span>Github仓库地址</span></div>
<span>{storeDetail && storeDetail.github_address}</span>
</div>
<div>
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but mr20"><Link to={`/${owner}/${projectsId}/server/reposyncer/record/github`}>查看同步分支</Link></Button>
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but" onClick={()=>{setVisibleNew(true);setCreateJobBy('Github')}}>新建同步分支</Button>
</div>
</div>}
{storeDetail && storeDetail.gitee_address && <div className="showStoreInfo">
<div className="storeInfoBox">
<div className="font-15 sTil"><img src={gitee1} alt="" className="mr10"/><span>Gitee仓库地址</span></div>
<span>{storeDetail.gitee_address}</span>
</div>
<div>
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but mr20"><Link to={`/${owner}/${projectsId}/server/reposyncer/record/gitee`}>查看同步分支</Link></Button>
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but" onClick={()=>{setVisibleNew(true);setCreateJobBy('Gitee')}}>新建同步分支</Button>
</div>
</div>}
{/* <Button type="primary" style={{width: '134px', height:'36px', padding: 0}} className="mt30"><Link to={`/${owner}/${projectsId}/server/reposyncer/store/edit`}>更改绑定仓库信息</Link></Button> */}
<Button style={{width: '134px', height:'36px'}} className="red_border_but mt40" onClick={()=>{setVisible(true)}}>清空仓库绑定</Button>
</div>}
<Modal
title="取消绑定"
visible={visible}
onCancel={()=>{setVisible(false)}}
footer={<div><Button style={{width: '90px', height:'36px'}} onClick={()=>{setVisible(false)}}>取消</Button><Button className="okBut" style={{width: '90px', height:'36px'}} onClick={updateStore}>确认清空</Button></div>}
width={535}
className="cancelBound"
>
<div className="bTilModal font-16"><span className="errorRedSpan font-18 mr20 mt20 ml15">!</span>您确定要清空已绑定仓库</div>
<div className="sTilModal">此操作将清空所有绑定仓库/绑定分支及同步日志请谨慎操作</div>
</Modal>
<CreateJobModal owner={owner} projectsId={projectsId} visible={visibleNew} setVisible={setVisibleNew} createJobBy={createJobBy} branchOptions={branchOptions}/>
</div>
}
export default StoreList;

View File

@ -0,0 +1,64 @@
import React, {useState, useEffect} from "react";
import { Button, Tooltip } from "antd";
import './index.scss';
import logo from '../img/logo2.png';
import Loadable from "react-loadable";
import { Route, Switch } from "react-router";
import Loading from "../../../Loading";
import { Link } from "react-router-dom";
import axios from 'axios';
//
const EditStore = Loadable({
loader: () => import("./component/editStore"),
loading: Loading,
});
//
const StoreList = Loadable({
loader: () => import("./component/storeList"),
loading: Loading,
});
// -
const RecordList = Loadable({
loader: () => import("./component/recordList"),
loading: Loading,
});
function Reposyncer(propsF){
const { owner , projectsId } = propsF.match.params;
const [storeDetail, setStoreDetail] = useState(null);
useEffect(()=>{
//
axios.get(`/${owner}/${projectsId}/synchronizes.json`).then(res=>{
if(res && res.data.message === "success"){
setStoreDetail(res.data.data);
}
})
},[])
return <div className="reposyncerBox">
<Switch {...propsF}>
<Route
path="/:owner/:projectsId/server/reposyncer/record/:type"
render={(props) => (
<RecordList {...propsF} {...props} storeDetail={storeDetail}/>
)}
></Route>
<Route
path="/:owner/:projectsId/server/reposyncer/store/edit"
render={(props) => (
<EditStore {...propsF} {...props}/>
)}
></Route>
<Route
path="/:owner/:projectsId/server/reposyncer"
render={(props) => (
<StoreList {...propsF} {...props} storeDetail={storeDetail}/>
)}
></Route>
</Switch>
</div>
}
export default Reposyncer;

View File

@ -0,0 +1,295 @@
.red_border_but, .red_border_but:focus{
color:#f60011;
background-color:rgba(196, 0, 14, 0.09);
border:1px solid #f60011;
border-radius:5px;
&:hover{
color:#f60011;
background-color:rgba(196, 0, 14, 0.18);
border-color:#ff727c;
}
&:active{
color:#f60011;
background-color:rgba(196, 0, 14, 0.22);
border-color:#f60011;
}
}
.blue_border_but, .blue_border_but:focus{
color:$primary-color;
background-color:rgba(70, 106, 255, 0.09);
border:1px solid #1a47ff;
border-radius:5px;
&:hover{
color:#6684fe;
background-color:rgba(70, 106, 255, 0.09);;
border-color:#6684fe;
}
&:active{
color:#1a47ff;
background-color:rgba(70, 106, 255, 0.09);
border-color:#1a47ff;
}
}
.reposyncerBox .headBox, .storeListBox .headBox{
height:60px;
line-height: 60px;
background-color:#fafcff;
border:1px solid rgba(42, 97, 255, 0.23);
border-radius:3px 3px 0px 0px;
color:#333333;
position: relative;
}
.reposyncerBox{
padding-bottom: 60px;
.helpBox1{
width: 14px;
height: 14px;
border-radius: 50%;
color: white;
background-color: #466aff;
line-height: normal;
display: inline-flex;
justify-content: center;
align-items: center;
cursor: default;
}
.nullStoreBox{
background-color:#fafcff;
border-radius:4px 4px 0px 0px;
text-align: center;
color:#333333;
padding-bottom: 65px;
.loBox{width: 68px;}
.introBox{
color:#666666;
width: 57%;
margin: 15px auto;
}
.borBox{
width: 45%;
margin: 0 auto 20px;
border-bottom: 1px solid rgba(90, 117, 193, 0.23);
}
}
.rightContentBox{
display: flex;
.leftNav{
width: 185px;
margin-right: 36px;
background-size: 100% 100%;
background-image: url('../img/bg1.png');
.oneBox{
padding: 15px;
color:#4c5b76;
display: block;
&.active{
position: relative;
background-color:rgba(70, 106, 255, 0.06);
color: $primary-color;
&::before{
content: '';
position: absolute;
width: 4px;
height: 86%;
left: 0;
top: 4px;
background-color: $primary-color;
}
}
}
}
}
}
.storeListBox{
.storeListTable{
width: 100%;
}
.blueSpan{
color: $primary-color;
}
.createJobBut{
position: absolute;
top: 11px;
right: 40px;
}
.storeTitle{
font-weight:700;
color:#151d40;
border-bottom: 1px solid #e0e6f5;;
}
.registerBox{
width: 85%;
.storeLogo{
width: 75px;
}
.storeFormItem{
width: 70%;
}
.has-error .ant-input, .has-error .ant-input:hover, .has-error .storeInput .ant-input{
border-color:#f60011;
}
#gitHubToken, #giteeToken, .storeInput{
border-color: #9eaacb;
height: 36px;
&:hover{border-color: $primary-color;}
}
.storeInput .ant-input-group-addon, .storeInput .ant-input{
border-color: #9eaacb;
}
.tipStoreBox{
color: #4c5b76;
line-height: 30px;
padding: 10px 0 30px;
}
}
.listStore{
.showStoreInfo{
padding: 20px 0 10px;
display: flex;
justify-content: space-between;
align-items: center;
&.dashedBor{
border-bottom: 1px dashed #e0e6f5;
}
}
.linkSpan{
color:$primary-color;
&:hover{color: $primary-color-hover;}
}
.storeInfoBox{color: #666;}
.sTil{
color:#202d40;
display: flex;
align-items: flex-start;
}
}
.expandBox{
background-color: #171b23;
color: white;
margin: -16px;
padding-bottom: 16px;
.expandCont{
word-break: break-all;
padding: 16px 16px 0;
}
}
th.recordColumns{
background-color: rgba(90, 117, 193, 0);
.ant-table-column-title{
font-family:PingFang SC;
font-size: 16px;
font-weight: 700;
color:#202d40;
}
}
.ant-table-tbody > tr > td.recordColumns{
color: #333;
font-size: 15px;
border-bottom: 1px dashed #e0e6f5;
&.primaryColor{color: $primary-color;}
}
tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) td.recordColumns{
background-color:#f8f9ff !important;
}
.statusBox{
width:58px;
height:32px;
border-radius:4px;
display: inline-block;
text-align: center;
&.errorBox{
color:#ff0c0c;
background-color:rgba(230, 0, 6, 0.1);
border:1px solid #fcb6c2;;
}
&.accomplish{
color:#009c44;
background-color:rgba(83, 255, 163, 0.1);
border:1px solid #00b843;;
}
}
.deleteRecord{
color:#f60011;
cursor: pointer;
position: relative;
padding-left: 20px;
&::before{
content: '';
position: absolute;
top: 6px;
left: 5px;
width: 1px;
height: 9px;
background-color: #dfdfdf;;
}
}
.primaryColor{color: $primary-color;}
}
.cancelBound{
.ant-modal-header{
padding: 10px 25px;
background-color: #f8f8f8;
.ant-modal-title{
font-size: 16px;
text-align: left;
font-weight: normal !important;
}
}
.ant-modal-close{
top: 0 !important;
.ant-modal-close-x{font-size: 22px;}
}
.bTilModal{
color:#333333;
.errorRedSpan{
display: inline-block;
width: 36px;
height: 36px;
border-radius: 50%;
color: white;
background-color:#ca0002;
line-height: 36px;
text-align: center;
}
}
.sTilModal{
color:#666666;
margin: 30px 0 40px 70px;
}
.ant-modal-footer{
border-top: none;
text-align: center;
padding-bottom: 50px;
.okBut, .okBut:focus{
color:#df0002;
border-color: #d9d9d9;
margin-left: 43px;
&:hover{
border-color:#ff727c;
}
&:active{
border-color:#f60011;
}
}
}
}
.createJobBox{
.itemBox{
display: flex;
align-items: center;
position: relative;
.errorBox{
position: absolute;
top: 35px;
left: 130px;
color:#f60011;
}
}
.labelBox{
width: 125px;
.icon-a-bitian2x{color: #fe1010;}
}
.inputBox{
width: 80%;
}
}

View File

@ -1,9 +1,8 @@
import React, { useEffect, useState } from 'react';
import { Banner , FlexAJ } from '../../Component/layout';
import { Banner , AlignCenter } from '../../Component/layout';
import DeleteBox from '../../Component/DeleteModal/Index';
import './Index.scss';
import { Button , List, Pagination } from 'antd';
import { Link } from 'react-router-dom';
import { Button , List, Pagination, Modal, Tooltip, Popover } from 'antd';
import axios from 'axios';
const limit = 15;
@ -13,7 +12,10 @@ function Index(props) {
const [ total , setTotal ] = useState(1);
const [ deleteId , setDeleteId ] = useState(undefined);
const [ visible , setVisible ] = useState(false);
const [ url , setUrl ] = useState(undefined);
const [ editVisible , setEditVisible ] = useState(false);
const [ editId , setEditId ] = useState(undefined);
const [ deleteWebHookModTitle, setDeleteWebHookModTitle] = useState(undefined);
const [ deleteWebHookModSubTitle, setDeleteWebHookModSubTitle] = useState(undefined);
const { owner , projectsId } = props.match.params;
@ -29,15 +31,21 @@ function Index(props) {
params:{page,limit}
}).then(result=>{
if(result && result.data){
const reg1 = new RegExp("https://(.*?)jianmuhub.com(.*?)");
const reg2 = new RegExp("https://jianmu.gitlink.org.cn(.*?)");
result.data.webhooks.map(item=>{
item.isJianMu = reg1.test(item.url) || reg2.test(item.url);
})
setData(result.data.webhooks);
setTotal(result.data.total_count);
}
}).catch(error=>{})
}
function deleteFunc(id,url) {
setDeleteId(id);
setUrl(url);
function deleteFunc(webhook) {
setDeleteWebHookModTitle(webhook.isJianMu ? '请谨慎删除此Webhook' : '删除Webhook');
setDeleteWebHookModSubTitle(webhook.isJianMu ? '该Webhook由流水线创建, 删除此Webhook后, 将导致本仓库相关流水线失效。确定要删除此Webhook?' : `删除后未来事件将不会推送至此Webhook地址: ${webhook.url}`);
setDeleteId(webhook.id);
setVisible(true);
}
@ -65,16 +73,37 @@ function Index(props) {
props.history.push(`/${owner}/${projectsId}/settings/webhooks/new`)
}
function gotoEditWebhook(webhook){
if(webhook.isJianMu){
setEditId(webhook.id);
setEditVisible(true);
}else{
props.history.push(`/${owner}/${projectsId}/settings/webhooks/${webhook.id}`);
}
}
return(
<div>
<DeleteBox
visible={visible}
<DeleteBox
visible={visible}
onCancel={()=>setVisible(false)}
onSuccess={onSuccess}
title="删除Webhook"
title={deleteWebHookModTitle}
content="您确定要删除此Webhook吗"
subTitle={`删除后未来事件将不会推送至此Webhook地址${url}`}
subTitle={deleteWebHookModSubTitle}
/>
<Modal
visible={editVisible}
onCancel={()=>{setEditVisible(false)}}
title="请谨慎编辑此Webhook"
width={"520px"}
wrapClassName={"deleteBox"}
centered={true}
onOk={()=>{props.history.push(`/${owner}/${projectsId}/settings/webhooks/${editId}`)}}
>
<AlignCenter className="editWebhookModalTitle font-20 mb10"><i className="iconfont icon-shanchu_tc_icon mr10 font-36" style={{color: '#ca0002'}}></i>您确定要编辑此Webhook吗</AlignCenter>
<p className="task-hide-2" style={{WebkitLineClamp:5}}>该Webhook由流水线创建, 编辑此Webhook后, 将大概率导致本仓库相关流水线失效确定要修改此Webhook?</p>
</Modal>
<Banner>
<span>Webhooks(网络钩子)</span>
<Button type="primary" size="large" onClick={addFunc}>添加Webhook</Button>
@ -87,12 +116,16 @@ function Index(props) {
{data.map((i,k)=>{
return(
<List.Item key={k}>
<i className="iconfont icon-a-xuanzhongwebhookicon color-grey-d mr12 font-17"></i>
<Link to={`/${owner}/${projectsId}/settings/webhooks/${i.id}`} className="webName">{i.url}</Link>
<span>
<Button ghost type={"primary"} onClick={()=>{props.history.push(`/${owner}/${projectsId}/settings/webhooks/${i.id}`)}}>编辑</Button>
<Button ghost className="ml20" type="danger" onClick={()=>{deleteFunc(i.id,i.url)}}>删除</Button>
</span>
<i className={`iconfont mr12 font-17 ${i.isJianMu ? 'icon-gongzuoliuicon color-grey-6' : 'icon-a-xuanzhongwebhookicon color-grey-d'}`}></i>
<span className="webName">{i.isJianMu ? <Tooltip title="该Webhook由流水线创建请勿编辑或删除以防流水线失效"><span className="webName spanBox">{i.url}</span></Tooltip> : i.url}</span>
{!i.isJianMu && <span>
<Button ghost type={"primary"} onClick={()=>{gotoEditWebhook(i)}}>编辑</Button>
<Button ghost className="ml20" type="danger" onClick={()=>{deleteFunc(i)}}>删除</Button>
</span>}
{i.isJianMu && <span>
<Popover content={"该Webhook由流水线创建, 编辑此Webhook后, 将大概率导致本仓库相关流水线失效。请在流水线中对该Webhook配置进行更改"} title="无法编辑此Webhook" overlayClassName="disabledButPopover"><Button type={"primary"} onClick={()=>{gotoEditWebhook(i)}} disabled>编辑</Button></Popover>
<Popover content={"该Webhook由流水线创建, 删除此Webhook后, 将导致本仓库相关流水线失效。请在流水线中删除该Webhook"} title="无法删除此Webhook" overlayClassName="disabledButPopover"><Button className="ml20" type="danger" onClick={()=>{deleteFunc(i)}} disabled>删除</Button></Popover>
</span>}
</List.Item>
)
})}

View File

@ -15,6 +15,12 @@
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: default;
&.spanBox{
display: inline-block;
max-width: 100%;
overflow: hidden;
}
}
}
}
@ -168,4 +174,10 @@
}
}
}
}
.editWebhookModalTitle{
justify-content: center;
}
.disabledButPopover{
width: 300px;
}

View File

@ -71,6 +71,7 @@ function Index({onCancel,avatarImg,login}){
width="638px"
footer={null}
centered
maskClosable={false}
title="修改头像"
onCancel={()=>onCancel(false)}
className="avatarBox"

View File

@ -196,15 +196,15 @@ class InfosUser extends Component {
>
<a className="ant-dropdown-link">
<span className="color-blue font-16">
<img src={img_new} alt="" width="13px" />
<i className="iconfont icon-xinjian1 font-14"></i>
</span>
</a>
</Popover>
)}
<Popover content={this.menu()} trigger={["click"]} placement="bottom">
<a className="ant-dropdown-link">
<span className="color-blue font-16">
排序 <img src={img_array} alt="" width="10px" />
<span className="color-blue font-16" style={{display: 'inline-flex'}}>
<span>排序</span> <i className="iconfont icon-sanjiaoxing-down"></i>
</span>
</a>
</Popover>