支持ui插件下载&运行

This commit is contained in:
muwoo 2021-12-02 17:55:45 +08:00
parent c2f43bea39
commit 0132a11d7e
32 changed files with 951 additions and 244 deletions

View File

@ -4,6 +4,32 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@ant-design/icons-svg": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz",
"integrity": "sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw=="
},
"@ant-design/icons-vue": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-6.0.1.tgz",
"integrity": "sha512-HigIgEVV6bbcrz2A92/qDzi/aKWB5EC6b6E1mxMB6aQA7ksiKY+gi4U94TpqyEIIhR23uaDrjufJ+xCZQ+vx6Q==",
"requires": {
"@ant-design/colors": "^5.0.0",
"@ant-design/icons-svg": "^4.0.0",
"@types/lodash": "^4.14.165",
"lodash": "^4.17.15"
},
"dependencies": {
"@ant-design/colors": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-5.1.1.tgz",
"integrity": "sha512-Txy4KpHrp3q4XZdfgOBqLl+lkQIc3tEvHXOimRN1giX1AEC7mGtyrO9p8iRGJ3FLuVMGa2gNEzQyghVymLttKQ==",
"requires": {
"@ctrl/tinycolor": "^3.3.1"
}
}
}
},
"@babel/code-frame": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz",
@ -1070,7 +1096,6 @@
"version": "7.16.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz",
"integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
@ -1113,6 +1138,11 @@
"to-fast-properties": "^2.0.0"
}
},
"@ctrl/tinycolor": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
"integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ=="
},
"@hapi/address": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
@ -1207,6 +1237,15 @@
"fastq": "^1.6.0"
}
},
"@simonwep/pickr": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.8.2.tgz",
"integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==",
"requires": {
"core-js": "^3.15.1",
"nanopop": "^2.1.0"
}
},
"@soda/friendly-errors-webpack-plugin": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz",
@ -1353,6 +1392,11 @@
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
"dev": true
},
"@types/lodash": {
"version": "4.14.177",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz",
"integrity": "sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw=="
},
"@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@ -2657,6 +2701,29 @@
"color-convert": "^1.9.0"
}
},
"ant-design-vue": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.2.8.tgz",
"integrity": "sha512-3graq9/gCfJQs6hznrHV6sa9oDmk/D1H3Oo0vLdVpPS/I61fZPk8NEyNKCHpNA6fT2cx6xx9U3QS63uuyikg/Q==",
"requires": {
"@ant-design/icons-vue": "^6.0.0",
"@babel/runtime": "^7.10.5",
"@simonwep/pickr": "~1.8.0",
"array-tree-filter": "^2.1.0",
"async-validator": "^3.3.0",
"dom-align": "^1.12.1",
"dom-scroll-into-view": "^2.0.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.15",
"moment": "^2.27.0",
"omit.js": "^2.0.0",
"resize-observer-polyfill": "^1.5.1",
"scroll-into-view-if-needed": "^2.2.25",
"shallow-equal": "^1.0.0",
"vue-types": "^3.0.0",
"warning": "^4.0.0"
}
},
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@ -2718,6 +2785,11 @@
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"dev": true
},
"array-tree-filter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz",
"integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw=="
},
"array-union": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
@ -2834,6 +2906,11 @@
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
"dev": true
},
"async-validator": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-3.5.2.tgz",
"integrity": "sha512-8eLCg00W9pIRZSB781UUX/H6Oskmm8xloZfr09lz5bikRpBVDlJ3hRVuxxP1SxcwsEYfJ4IU8Q19Y8/893r3rQ=="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -2888,6 +2965,14 @@
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true
},
"axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"requires": {
"follow-redirects": "^1.14.4"
}
},
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@ -3980,6 +4065,11 @@
}
}
},
"compute-scroll-into-view": {
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz",
"integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -5001,6 +5091,11 @@
"esutils": "^2.0.2"
}
},
"dom-align": {
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.2.tgz",
"integrity": "sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg=="
},
"dom-converter": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
@ -5010,6 +5105,11 @@
"utila": "~0.4"
}
},
"dom-scroll-into-view": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz",
"integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w=="
},
"dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@ -6093,8 +6193,7 @@
"follow-redirects": {
"version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
"dev": true
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA=="
},
"for-in": {
"version": "1.0.2",
@ -7674,8 +7773,7 @@
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "3.14.1",
@ -7911,8 +8009,12 @@
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"lodash.debounce": {
"version": "4.0.8",
@ -7971,6 +8073,14 @@
"integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==",
"dev": true
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
@ -8303,6 +8413,11 @@
"minimist": "^1.2.5"
}
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@ -8387,6 +8502,11 @@
"to-regex": "^3.0.1"
}
},
"nanopop": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.1.0.tgz",
"integrity": "sha512-jGTwpFRexSH+fxappnGQtN9dspgE2ipa1aOjtR24igG0pv6JCxImIAmrLRHX+zUF5+1wtsFVbKyfP51kIGAVNw=="
},
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@ -8673,6 +8793,11 @@
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
"dev": true
},
"omit.js": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/omit.js/-/omit.js-2.0.2.tgz",
"integrity": "sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg=="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@ -9997,8 +10122,7 @@
"regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
"dev": true
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
},
"regenerator-transform": {
"version": "0.14.5",
@ -10228,6 +10352,11 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@ -10397,6 +10526,14 @@
"ajv-keywords": "^3.5.2"
}
},
"scroll-into-view-if-needed": {
"version": "2.2.28",
"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.28.tgz",
"integrity": "sha512-8LuxJSuFVc92+0AdNv4QOxRL4Abeo1DgLnGNkn1XlaujPH/3cCFz3QI60r2VNu4obJJROzgnIUw5TKQkZvZI1w==",
"requires": {
"compute-scroll-into-view": "^1.0.17"
}
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -10598,6 +10735,11 @@
"safe-buffer": "^5.0.1"
}
},
"shallow-equal": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
"integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@ -12276,6 +12418,21 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"vue-types": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/vue-types/-/vue-types-3.0.2.tgz",
"integrity": "sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==",
"requires": {
"is-plain-object": "3.0.1"
},
"dependencies": {
"is-plain-object": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz",
"integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g=="
}
}
},
"vuex": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
@ -12284,6 +12441,14 @@
"@vue/devtools-api": "^6.0.0-beta.11"
}
},
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"watchpack": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",

View File

@ -8,6 +8,9 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@ant-design/icons-vue": "^6.0.1",
"ant-design-vue": "^2.2.8",
"axios": "^0.24.0",
"core-js": "^3.6.5",
"vue": "^3.0.0",
"vue-router": "^4.0.0-0",

View File

@ -1,30 +1,87 @@
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<div class="main-container">
<div class="slider-bar">
<div class="top">
<div class="menu-item avatar">
<a-avatar shape="square" :size="30">
<template #icon><UserOutlined /></template>
</a-avatar>
</div>
<div class="menu-item" @click="changeMenu('market')">
<AppstoreOutlined :class="active === 'market' && 'active'" style="font-size: 24px;" />
</div>
<div class="menu-item" @click="changeMenu('installed')">
<HeartOutlined :class="active === 'installed' && 'active'" style="font-size: 24px;" />
</div>
<div class="menu-item" @click="changeMenu('settings')">
<SettingOutlined :class="active === 'installed' && 'active'" style="font-size: 24px;" />
</div>
<div class="menu-item" @click="changeMenu('dev')">
<BugOutlined :class="active === 'installed' && 'active'" style="font-size: 24px;" />
</div>
</div>
<div class="menu-item bottom" @click="changeMenu('more')">
<MenuOutlined style="font-size: 24px;" />
</div>
</div>
<router-view />
</div>
<router-view />
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
<script setup lang="ts">
import { ref } from "vue";
import { HeartOutlined, UserOutlined, SearchOutlined, MenuOutlined, AppstoreOutlined, SettingOutlined, BugOutlined } from "@ant-design/icons-vue";
const active = ref("market");
</script>
<style lang="less">
* {
margin: 0;
padding: 0;
}
#nav {
padding: 30px;
}
.main-container {
display: flex;
align-items: flex-start;
background: #F2EFEF;
#nav a {
font-weight: bold;
color: #2c3e50;
}
.slider-bar {
-webkit-app-region: drag;
width: 60px;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
padding-top: 15px;
height: 100vh;
background-image: linear-gradient(to top right, rgba(255, 159, 180, 0.3), rgba(255, 159, 180, 0.2));
color: #7D7170;
box-sizing: border-box;
#nav a.router-link-exact-active {
color: #42b983;
.top {
display: flex;
align-items: center;
flex-direction: column;
}
.menu-item {
cursor: pointer;
margin-bottom: 40px;
-webkit-app-region: no-drag;
.active {
color: #ff4ea4;
}
&.avatar {
margin-bottom: 30px;
}
&.bottom {
margin-bottom: 20px;
}
}
}
}
</style>

View File

@ -0,0 +1,5 @@
@import '~ant-design-vue/dist/antd.less'; // 引入官方提供的 less 样式入口文件
@primary-color: #ff4ea4; // 全局主色
@link-color: #ff4ea4; // 链接色
@error-color: #ff4ea4; // 错误色

View File

@ -0,0 +1,17 @@
import axios from "axios";
export default {
async getTotalPlugins() {
const res = await axios.get(
"https://gitee.com/monkeyWang/rubick-database/raw/master/plugins/total-plugins.json"
);
return res.data;
},
async getFinderDetail() {
const res = await axios.get(
"https://gitee.com/monkeyWang/rubick-database/raw/master/plugins/finder.json"
);
return res.data;
},
};

View File

@ -1,140 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br />
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener"
>vue-cli documentation</a
>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
target="_blank"
rel="noopener"
>babel</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router"
target="_blank"
rel="noopener"
>router</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex"
target="_blank"
rel="noopener"
>vuex</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
target="_blank"
rel="noopener"
>eslint</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript"
target="_blank"
rel="noopener"
>typescript</a
>
</li>
</ul>
<h3>Essential Links</h3>
<ul>
<li>
<a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
</li>
<li>
<a href="https://forum.vuejs.org" target="_blank" rel="noopener"
>Forum</a
>
</li>
<li>
<a href="https://chat.vuejs.org" target="_blank" rel="noopener"
>Community Chat</a
>
</li>
<li>
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
>Twitter</a
>
</li>
<li>
<a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
</li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li>
<a href="https://router.vuejs.org" target="_blank" rel="noopener"
>vue-router</a
>
</li>
<li>
<a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
</li>
<li>
<a
href="https://github.com/vuejs/vue-devtools#vue-devtools"
target="_blank"
rel="noopener"
>vue-devtools</a
>
</li>
<li>
<a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
>vue-loader</a
>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
rel="noopener"
>awesome-vue</a
>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "HelloWorld",
props: {
msg: String,
},
});
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@ -1,6 +1,8 @@
import { createApp } from "vue";
import Antd from "ant-design-vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "./assets/ant-reset.less";
createApp(App).use(store).use(router).mount("#app");
createApp(App).use(store).use(Antd).use(router).mount("#app");

View File

@ -1,21 +1,12 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import Home from "../views/Home.vue";
import Market from "../views/market/index.vue";
const routes: Array<RouteRecordRaw> = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
},
name: "Market",
component: Market,
}
];
const router = createRouter({

View File

@ -4,3 +4,5 @@ declare module '*.vue' {
const component: DefineComponent<{}, {}, any>
export default component
}
declare module 'axios'

View File

@ -1,8 +1,73 @@
import { createStore } from "vuex";
import request from "@/assets/request";
const isDownload = (item: any, targets: any[]) => {
let isDownload = false;
targets.some((plugin) => {
if (plugin.name === item.name) {
isDownload = true;
}
return isDownload;
});
return isDownload;
};
export default createStore({
state: {},
mutations: {},
actions: {},
state: {
totalPlugins: [],
localPlugins: [],
},
mutations: {
commonUpdate(state: any, payload) {
Object.keys(payload).forEach((key) => {
state[key] = payload[key];
});
},
},
actions: {
async init({ commit }) {
const totalPlugins = await request.getTotalPlugins();
const localPlugins = (window as any).rubick.getLocalPlugins();
totalPlugins.forEach(
(origin: { isdwonload?: any; name?: any; isloading: boolean }) => {
origin.isdwonload = isDownload(origin, localPlugins);
origin.isloading = false;
}
);
commit("commonUpdate", {
localPlugins,
totalPlugins,
});
},
startDownload({ commit, state }, name) {
const totalPlugins = JSON.parse(JSON.stringify(state.totalPlugins));
totalPlugins.forEach(
(origin: { isdwonload?: any; name?: any; isloading: boolean }) => {
if (origin.name === name) {
origin.isloading = true;
}
}
);
commit("commonUpdate", {
totalPlugins,
});
},
successDownload({ commit, state }, name) {
const totalPlugins = JSON.parse(JSON.stringify(state.totalPlugins));
totalPlugins.forEach(
(origin: { isdwonload?: any; name?: any; isloading: boolean }) => {
if (origin.name === name) {
origin.isloading = false;
origin.isdwonload = true;
}
}
);
commit("commonUpdate", {
totalPlugins,
});
},
},
modules: {},
});

View File

@ -1,5 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

View File

@ -1,18 +0,0 @@
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
export default defineComponent({
name: "Home",
components: {
HelloWorld,
},
});
</script>

View File

@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
name: "devlopment"
};
</script>
<style scoped>
</style>

View File

@ -0,0 +1,115 @@
<template>
<div class="finder">
<a-carousel arrows>
<template #prevArrow>
<div class="custom-slick-arrow" style="left: 10px; z-index: 1">
<left-circle-outlined />
</div>
</template>
<template #nextArrow>
<div class="custom-slick-arrow" style="right: 10px">
<right-circle-outlined />
</div>
</template>
<div :key="index" v-for="(banner, index) in (data.banners || [])">
<img @click="jumpTo(banner.link)" width="100%" :src="banner.src" />
</div>
</a-carousel>
<PluginList @downloadSuccess="downloadSuccess" title="推荐" :list="recommend || []" />
<PluginList title="最近更新" :list="newList || []" />
</div>
</template>
<script setup>
import {
LeftCircleOutlined,
RightCircleOutlined,
} from "@ant-design/icons-vue";
import { ref, computed } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { useStore } from "vuex";
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);
const data = ref([]);
request.getFinderDetail().then(res => {
data.value = res;
});
const recommend = computed(() => {
const defaultData = data.value.recommend || [];
if (!defaultData.length) return [];
return defaultData.map((plugin) => {
let searchInfo = null;
totalPlugins.value.forEach((t) => {
if (t.name === plugin) {
searchInfo = t;
}
});
return searchInfo;
});
});
const newList = computed(() => {
const defaultData = data.value.new || [];
if (!defaultData.length) return [];
return defaultData.map((plugin) => {
let searchInfo = null;
totalPlugins.value.forEach((t) => {
if (t.name === plugin) {
searchInfo = t;
}
});
return searchInfo;
});
});
</script>
<style lang="less">
.finder {
width: 100%;
height: 100vh;
overflow-x: hidden;
box-sizing: border-box;
.ant-carousel .slick-slide {
text-align: center;
height: 235px;
line-height: 160px;
overflow: hidden;
border-radius: 4px;
img {
width: 100%;
height: 235px;
}
}
.ant-carousel .custom-slick-arrow {
width: 25px;
height: 25px;
font-size: 25px;
color: #fff;
background-color: rgba(31, 45, 61, 0.11);
opacity: 0.3;
}
.ant-carousel .custom-slick-arrow.slick-next:focus {
color: #fff;
}
.ant-carousel .custom-slick-arrow:before {
display: none;
}
.ant-carousel .custom-slick-arrow:hover {
opacity: 0.5;
}
.ant-carousel .slick-slide h3 {
color: #fff;
}
}
</style>

View File

@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
name: "image"
};
</script>
<style scoped>
</style>

View File

@ -0,0 +1,165 @@
<template>
<div class="panel-item">
<h3 class="title">{{ title }}</h3>
<div class="list-item">
<a-list :grid="{ gutter: 16, column: 2 }" :data-source="list">
<template #renderItem="{ item, index }">
<a-list-item @click="visible=true">
<template #actions>
<a-button style="color: #ff4ea4;" type="text" :loading="item.isloading">
<CloudDownloadOutlined
v-show="!item.isloading && !item.isdwonload"
@click.stop="downloadPlugin(item, index)"
style="font-size: 20px; cursor: pointer"
/>
</a-button>
</template>
<a-list-item-meta
:description="item.description"
>
<template #title>
<span>{{ item.pluginName }}</span>
</template>
<template #avatar>
<a-avatar :src="item.logo" />
</template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</div>
</div>
<a-drawer
width="100%"
placement="right"
:closable="false"
:visible="visible"
:get-container="false"
class="plugin-info"
:style="{ position: 'absolute' }"
@close="visible=false"
>
<template #title>
<div class="plugin-title-info">
<div class="back-icon" @click="visible=false">
<ArrowLeftOutlined />
</div>
<div class="info">
<img src="https://static.91jkys.com/activity/img/2adb63c2e5d54dc1b26001958fcdb044.jpg"
class="plugin-icon" />
<div class="plugin-desc">
<div class="title">
备忘快贴
</div>
<div class="desc">
适合每个人的专业图像编辑软件
</div>
<a-button shape="round" type="primary">
<template #icon>
<CloudDownloadOutlined />
</template>
获取
</a-button>
</div>
</div>
</div>
</template>
</a-drawer>
</template>
<script setup>
import {
CloudDownloadOutlined,
ArrowLeftOutlined,
} from "@ant-design/icons-vue";
import { computed, defineProps, ref } from "vue";
import { useStore } from "vuex";
const store = useStore();
const startDownload = (name) => store.dispatch("startDownload", name);
const successDownload = (name) => store.dispatch("successDownload", name);
defineProps({
list: {
type: [Array],
default: () => [],
},
title: String,
});
const downloadPlugin = async (plugin) => {
startDownload(plugin.name);
await window.rubick.downloadPlugin(plugin);
successDownload(plugin.name);
};
const visible = ref(false);
</script>
<style lang="less">
.panel-item {
margin: 20px 0;
.title {
margin-bottom: 30px;
}
&:after{
content: " ";
display: block;
width: 100%;
height: 1px;
border-bottom: 1px solid #eee;
transform: scaleY(0.5);
}
.ant-list-item {
display: flex !important;
}
&:last-child {
border-bottom: none;
}
}
.plugin-info {
width: 100%;
.ant-drawer-content-wrapper {
box-shadow: none !important;
}
}
.plugin-title-info {
display: flex;
align-items: flex-start;
.back-icon {
font-size: 16px;
margin-right: 40px;
}
.info {
display: flex;
align-items: center;
.plugin-icon {
width: 100px;
height: 100px;
margin-right: 20px;
}
.plugin-desc {
.title {
font-size: 18px;
font-weight: bold;
}
.desc {
font-size: 12px;
font-weight: normal;
margin-top: 5px;
margin-bottom: 20px;
}
}
}
}
</style>

View File

@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
name: "tools"
};
</script>
<style scoped>
</style>

View File

@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
name: "worker"
};
</script>
<style scoped>
</style>

View File

@ -0,0 +1,125 @@
<template>
<div class="market">
<div class="left-menu">
<div class="search-container">
<a-input-search
v-model:value="searchValue"
placeholder="搜索插件"
style="width: 100%"
@search="onSearch"
/>
</div>
<a-menu v-model:selectedKeys="current" mode="inline">
<a-menu-item key="finder">
<template #icon>
<StarOutlined />
</template>
探索
</a-menu-item>
<a-menu-item key="worker">
<template #icon>
<SendOutlined style="transform: rotate(-45deg)" />
</template>
效率
</a-menu-item>
<a-menu-item key="tools">
<template #icon>
<SearchOutlined />
</template>
搜索工具
</a-menu-item>
<a-menu-item key="image">
<template #icon>
<FileImageOutlined />
</template>
图像
</a-menu-item>
<a-menu-item key="dev">
<template #icon>
<CodeOutlined />
</template>
开发
</a-menu-item>
<a-menu-item key="system">
<template #icon>
<DatabaseOutlined />
</template>
系统
</a-menu-item>
</a-menu>
</div>
<div class="container">
<component :totalPlugins="totalPlugins" :is="Components[current[0]]" />
</div>
</div>
</template>
<script lang="ts" setup>
import {
StarOutlined,
SendOutlined,
SearchOutlined,
FileImageOutlined,
DatabaseOutlined,
CodeOutlined,
} from "@ant-design/icons-vue";
import { reactive, toRefs, computed } from "vue";
import { useStore } from "vuex";
import Finder from "./components/finder.vue";
const Components = {
finder: Finder,
};
const state = reactive({
searchValue: "",
current: ["finder"],
});
const store = useStore();
const init = () => store.dispatch("init");
init();
const totalPlugins = computed(() => store.state.totalPlugins);
const { searchValue, current } = toRefs(state);
</script>
<style lang="less">
.market {
display: flex;
box-sizing: border-box;
align-items: flex-start;
width: 100%;
overflow: hidden;
background: #F3EFEF;
.left-menu {
width: 200px;
height: 100vh;
.search-container {
padding: 10px;
}
.ant-input-affix-wrapper {
border: none;
}
.ant-menu {
background: #F3EFEF;
.ant-menu-item-selected {
background-color: #E2E2E2;
color: #141414;
&:after {
display: none;
}
}
}
}
.container {
background: #fff;
width: calc(~'100% - 200px');
height: 100vh;
box-sizing: border-box;
padding: 10px 20px;
position: relative;
}
}
</style>

View File

@ -1,6 +1,13 @@
const path = require("path");
module.exports = {
css: { // 配置css模块
loaderOptions: { // 向预处理器 Loader 传递配置选项
less: { // 配置less其他样式解析用法一致
javascriptEnabled: true // 设置为true
}
}
},
outputDir: path.join(__dirname, "../public/feature"),
publicPath: process.env.NODE_ENV === "production" ? "" : "/",
};

10
public/preload.js Normal file
View File

@ -0,0 +1,10 @@
const {remote} = require("electron");
console.log(remote)
window.rubick = {
getLocalPlugins() {
return remote.getGlobal("LOCAL_PLUGINS").getLocalPlugins();
},
downloadPlugin(plugin) {
return remote.getGlobal("LOCAL_PLUGINS").downloadPlugin(plugin);
},
};

View File

@ -1,18 +1,19 @@
<template>
<webview
v-if="preload"
id="webview"
:src="`File://${pluginInfo.indexPath}`"
:preload="preload"
/>
</template>
<script setup lang="ts">
<script setup >
import { reactive, toRefs, onMounted } from "vue";
const state = reactive({
pluginInfo: {},
});
window.setPluginInfo = (pluginInfo: any) => {
window.setPluginInfo = (pluginInfo) => {
console.log(pluginInfo);
try {
state.pluginInfo = pluginInfo;
@ -21,14 +22,7 @@ window.setPluginInfo = (pluginInfo: any) => {
}
};
onMounted(() => {
const webview: any = document.querySelector("webview");
webview.addEventListener("dom-ready", () => {
webview.openDevTools();
});
});
const { pluginInfo } = toRefs(state);
const { pluginInfo, preload } = toRefs(state);
</script>
<style>

View File

@ -4,3 +4,5 @@ declare module '*.vue' {
const component: DefineComponent<{}, {}, any>
export default component
}
declare const __static: string

View File

@ -0,0 +1,7 @@
export default (): string => {
let localDataFile: any = process.env.HOME;
if (!localDataFile) {
localDataFile = process.env.LOCALAPPDATA;
}
return localDataFile;
};

View File

@ -0,0 +1,46 @@
import path from "path";
import fs from "fs";
import getLocalDataFile from "./getLocalDataFile";
import { app } from "electron";
import { PluginHandler } from "@/core";
const configPath = path.join(getLocalDataFile(), "./rubick-local-plugin.json");
const appPath = app.getPath("cache");
const baseDir = path.join(appPath, "./rubick-plugins");
const pluginInstance = new PluginHandler({
baseDir: baseDir,
});
global.LOCAL_PLUGINS = {
PLUGINS: [],
async downloadPlugin(plugin) {
await pluginInstance.install([plugin.name]);
global.LOCAL_PLUGINS.addPlugin(plugin);
return global.LOCAL_PLUGINS.PLUGINS;
},
getLocalPlugins() {
try {
if (!global.LOCAL_PLUGINS.PLUGINS.length) {
global.LOCAL_PLUGINS.PLUGINS = JSON.parse(
fs.readFileSync(configPath, "utf-8")
);
}
return global.LOCAL_PLUGINS.PLUGINS;
} catch (e) {
global.LOCAL_PLUGINS.PLUGINS = [];
return global.LOCAL_PLUGINS.PLUGINS;
}
},
addPlugin(plugin) {
let has = false;
global.LOCAL_PLUGINS.PLUGINS.some((p) => {
has = p.name === plugin.name;
return has;
});
if (!has) {
global.LOCAL_PLUGINS.PLUGINS.unshift(plugin);
fs.writeFileSync(configPath, JSON.stringify(global.LOCAL_PLUGINS.PLUGINS));
}
},
};

View File

@ -31,7 +31,7 @@ async function getAppIcon(
const existsnone = fs.existsSync(iconnone);
if (exists) return true;
if (existsnone) return false;
const appName: string = appPath.split("/").pop() ?? "";
const appName: string = appPath.split("/").pop() || "";
const extname: string = path.extname(appName);
const appSubStr: string = appName.split(extname)[0];
const path1 = path.join(appPath, `/Contents/Resources/App.icns`);

View File

@ -34,7 +34,8 @@ class AdapterHandler {
);
}
this.baseDir = options.baseDir;
this.registry = options.registry ?? "https://registry.npm.taobao.org";
console.log(this.baseDir);
this.registry = options.registry || "https://registry.npm.taobao.org";
}
/**
@ -68,8 +69,8 @@ class AdapterHandler {
}
// 安装并启动插件
async install(adapters: Array<string>, opts?: any) {
const installCmd = opts.isDev ? "link" : "install";
async install(adapters: Array<string>) {
const installCmd = "install";
// 安装
await this.execCommand(installCmd, adapters);
}

View File

@ -9,7 +9,7 @@ export default () => {
const createWindow = async () => {
win = new BrowserWindow({
height: 600,
height: 60,
useContentSize: true,
resizable: true,
width: 800,

View File

@ -12,31 +12,22 @@ export default () => {
const createWindow = (plugin) => {
win = new BrowserWindow({
autoHideMenuBar: true,
width: 800,
height: 800,
width: 850,
height: 700,
alwaysOnTop: true,
resizable: false,
focusable: true,
show: false,
webPreferences: {
enableRemoteModule: true,
webSecurity: false,
nodeIntegration: true,
contextIsolation: false,
devTools: true,
webviewTag: true,
preload: `${__static}/preload.js`,
},
});
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
win.loadURL("http://localhost:8081" as string);
} else {
// Load the index.html when not in development
win.loadURL(`file://${__static}/runner/index.html`);
}
win.webContents.on("dom-ready", () => {
win.webContents.executeJavaScript(`window.setPluginInfo(${JSON.stringify(plugin)})`);
});
win.loadURL(plugin.indexPath);
win.once("ready-to-show", () => {
win.show();

View File

@ -8,7 +8,6 @@ const API: any = {
win.setSize(800, height || 60);
},
openPlugin({plugin}) {
console.log(plugin);
runnerInstance.init(plugin);
},
};

View File

@ -5,6 +5,7 @@ import commonConst from "../common/utils/commonConst";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import API from "./common/api";
import "../common/utils/localPlugin";
class App {
private windowCreator: { init: () => void; getWindow: () => BrowserWindow };

View File

@ -1,11 +1,21 @@
import { reactive, toRefs, nextTick } from "vue";
import { nativeImage, remote } from "electron";
import { reactive, toRefs, toRaw } from "vue";
import { nativeImage, remote, ipcRenderer } from "electron";
import { appSearch, PluginHandler } from "@/core";
import path from "path";
import throttle from "lodash.throttle";
import commonConst from "@/common/utils/commonConst";
const appPath = remote.app.getPath("cache");
function searchKeyValues(lists, value) {
return lists.filter((item) => {
if (typeof item === "string") {
return item.toLowerCase().indexOf(value.toLowerCase()) >= 0;
}
return item.type.toLowerCase().indexOf(value.toLowerCase()) >= 0;
});
}
const createPluginManager = (): any => {
const baseDir = path.join(appPath, "./rubick-plugins");
const pluginInstance = new PluginHandler({
@ -17,6 +27,7 @@ const createPluginManager = (): any => {
plugins: [],
options: [],
searchValue: "",
localPlugins: [],
});
const initPlugins = async () => {
@ -35,9 +46,44 @@ const createPluginManager = (): any => {
const value = e.target.value;
state.searchValue = value;
if (!value) return;
// todo 先搜索 plugin
// todo 再搜索 app
state.localPlugins = remote.getGlobal("LOCAL_PLUGINS").getLocalPlugins();
let options: any = [];
// todo 先搜索 plugin
state.localPlugins.forEach((plugin) => {
const feature = plugin.features;
feature.forEach((fe) => {
const cmds = searchKeyValues(fe.cmds, value);
options = [
...options,
...cmds.map((cmd) => ({
name: cmd,
value: "plugin",
icon: plugin.logo,
desc: fe.explain,
type: plugin.pluginType,
click: () => {
const pluginPath = path.resolve(
pluginInstance.baseDir,
"node_modules",
plugin.name
);
ipcRenderer.sendSync("msg-trigger", {
type: "openPlugin",
plugin: {
...toRaw(plugin),
indexPath: `file://${path.join(
pluginPath,
"./",
plugin.main
)}`,
},
});
},
})),
];
});
});
// todo 再搜索 app
const descMap = new Map();
options = [
...options,
@ -77,7 +123,9 @@ const createPluginManager = (): any => {
const pluginInfo = await pluginInstance.getAdapterInfo(pluginName, pluginPath);
return {
...pluginInfo,
indexPath: path.join(pluginPath, "../", pluginInfo.main),
indexPath: commonConst.dev()
? "http://localhost:8080/#/"
: `file://${path.join(pluginPath, "../", pluginInfo.main)}`,
};
};