Browse Source

Init project

develop
CareyWong 4 years ago
parent
commit
b3c30e6b74
  1. 2
      .browserslistrc
  2. 18
      .eslintrc.js
  3. 21
      .gitignore
  4. 24
      README.md
  5. 14
      babel.config.js
  6. 35
      package.json
  7. 2
      public/_redirects
  8. BIN
      public/favicon.ico
  9. 17
      public/index.html
  10. 5
      src/App.vue
  11. 79
      src/assets/css/element-ui.scss
  12. 31
      src/assets/css/element-variables.scss
  13. BIN
      src/assets/logo.png
  14. 59
      src/components/HelloWorld.vue
  15. 15
      src/main.js
  16. 6
      src/plugins/axios.js
  17. 6
      src/plugins/base64.js
  18. 4
      src/plugins/clipboard.js
  19. 20
      src/plugins/element-ui.js
  20. 4
      src/plugins/particles.js
  21. 20
      src/router/index.js
  22. 327
      src/views/Subconverter.vue
  23. 9
      vue.config.js
  24. 8882
      yarn.lock

2
.browserslistrc

@ -0,0 +1,2 @@
> 1%
last 2 versions

18
.eslintrc.js

@ -0,0 +1,18 @@
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/essential',
'eslint:recommended'
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'semi': 0
},
parserOptions: {
parser: 'babel-eslint'
}
}

21
.gitignore

@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
README.md

@ -0,0 +1,24 @@
# sub-web
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

14
babel.config.js

@ -0,0 +1,14 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}

35
package.json

@ -0,0 +1,35 @@
{
"name": "sub-web",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"atob": "^2.1.2",
"axios": "^0.19.1",
"btoa": "^1.2.1",
"core-js": "^3.4.4",
"element-ui": "^2.13.0",
"vue": "^2.6.10",
"vue-clipboard2": "^0.3.1",
"vue-router": "^3.1.3"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.1.0",
"@vue/cli-plugin-eslint": "^4.1.0",
"@vue/cli-plugin-router": "^4.1.0",
"@vue/cli-service": "^4.1.0",
"babel-eslint": "^10.0.3",
"babel-plugin-component": "^1.1.1",
"babel-plugin-import": "^1.13.0",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"node-sass": "^4.13.0",
"sass-loader": "^8.0.0",
"vue-particles": "^1.0.9",
"vue-template-compiler": "^2.6.10"
}
}

2
public/_redirects

@ -0,0 +1,2 @@
# 单页应用的 Netlify 设置
/* /index.html 200

BIN
public/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

17
public/index.html

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>sub-web</title>
</head>
<body>
<noscript>
<strong>We're sorry but sub-web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

5
src/App.vue

@ -0,0 +1,5 @@
<template>
<div id="app">
<router-view/>
</div>
</template>

79
src/assets/css/element-ui.scss

@ -0,0 +1,79 @@
// cover some element-ui styles
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
font-weight: 400 !important;
}
.el-upload {
input[type="file"] {
display: none !important;
}
}
.el-upload__input {
display: none;
}
.cell {
.el-tag {
margin-right: 0px;
}
}
.small-padding {
.cell {
padding-left: 5px;
padding-right: 5px;
}
}
.fixed-width {
.el-button--mini {
padding: 7px 10px;
width: 60px;
}
}
.status-col {
.cell {
padding: 0 10px;
text-align: center;
.el-tag {
margin-right: 0px;
}
}
}
// to fixed https://github.com/ElemeFE/element/issues/2461
.el-dialog {
transform: none;
left: 0;
position: relative;
margin: 0 auto;
}
// refine element ui upload
.upload-container {
.el-upload {
width: 100%;
.el-upload-dragger {
width: 100%;
height: 200px;
}
}
}
// dropdown
.el-dropdown-menu {
a {
display: block
}
}
// fix date-picker ui bug in filter-item
.el-range-editor.el-input__inner {
display: inline-flex !important;
}

31
src/assets/css/element-variables.scss

@ -0,0 +1,31 @@
/**
* I think element-ui's default theme color is too light for long-term use.
* So I modified the default color and you can modify it to your liking.
**/
/* theme color */
$--color-primary: #304156;
$--color-success: #65C934;
$--color-warning: #E6A23C;
$--color-danger: #F56C6C;
// $--color-info: #1E1E1E;
$--button-font-weight: 400;
// $--color-text-regular: #1f2d3d;
$--border-color-light: #dfe4ed;
$--border-color-lighter: #e6ebf5;
$--table-border:1px solid#dfe6ec;
/* icon font path, required */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
theme: $--color-primary;
}

BIN
src/assets/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

59
src/components/HelloWorld.vue

@ -0,0 +1,59 @@
<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-eslint" target="_blank" rel="noopener">eslint</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>
export default {
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>

15
src/main.js

@ -0,0 +1,15 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
require(`@/plugins/element-ui`)
require(`@/plugins/clipboard`)
require(`@/plugins/base64`)
require(`@/plugins/particles`)
require(`@/plugins/axios`)
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')

6
src/plugins/axios.js

@ -0,0 +1,6 @@
import Vue from 'vue'
import axios from "axios"
axios.defaults.timeout = 5000 //请求超时的时间设定
Vue.prototype.$axios = axios

6
src/plugins/base64.js

@ -0,0 +1,6 @@
import Vue from 'vue'
import btoa from 'btoa'
import atob from 'atob'
Vue.prototype.$btoa = (string) => btoa(string)
Vue.prototype.$atob = (string) => atob(string)

4
src/plugins/clipboard.js

@ -0,0 +1,4 @@
import Vue from 'vue'
import clipboard from 'vue-clipboard2'
Vue.use(clipboard)

20
src/plugins/element-ui.js

@ -0,0 +1,20 @@
import Vue from 'vue'
import Element from 'element-ui'
import locale from 'element-ui/lib/locale/lang/zh-CN'
// import '@/assets/css/element-ui.scss'
// import '@/assets/css/element-element-variables.scss'
Vue.use(Element, {
locale,
size: 'small'
})
Vue.use(Element.Loading.directive);
Vue.prototype.$loading = Element.Loading.service;
Vue.prototype.$msgbox = Element.MessageBox;
Vue.prototype.$alert = Element.MessageBox.alert;
Vue.prototype.$confirm = Element.MessageBox.confirm;
Vue.prototype.$prompt = Element.MessageBox.prompt;
Vue.prototype.$notify = Element.Notification;
Vue.prototype.$message = Element.Message;

4
src/plugins/particles.js

@ -0,0 +1,4 @@
import Vue from 'vue'
import VueParticles from 'vue-particles'
Vue.use(VueParticles)

20
src/router/index.js

@ -0,0 +1,20 @@
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "SubConverter",
component: () => import("../views/Subconverter.vue")
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
export default router;

327
src/views/Subconverter.vue

@ -0,0 +1,327 @@
<template>
<div>
<el-row style="margin-top: 10px">
<el-col>
<el-card>
<div slot="header">Subscription Converter</div>
<el-container>
<el-form
:model="form"
label-width="120px"
label-position="left"
style="width: 100%"
>
<el-form-item label="模式设置:">
<el-radio v-model="advanced" label="1">基础模式</el-radio>
<el-radio v-model="advanced" label="2">进阶模式</el-radio>
</el-form-item>
<el-form-item label="订阅链接:">
<el-input
v-model="form.sourceSubUrl"
type="textarea"
rows="3"
placeholder="支持订阅或ss/ssr/vmess单链接。多个链接请每行一个或用 | 分隔"
/>
</el-form-item>
<el-form-item label="客户端:">
<el-select v-model="form.clientType" style="width: 100%">
<el-option
v-for="(v, k) in options.clientTypes"
:key="k"
:label="k"
:value="v"
/>
</el-select>
</el-form-item>
<div v-if="advanced === '2'">
<el-form-item label="emoji:">
<el-radio v-model="form.emoji" label="true"></el-radio>
<el-radio v-model="form.emoji" label="false"></el-radio>
</el-form-item>
<el-form-item label="后端地址:">
<el-input
ref="backend"
v-model="form.customBackend"
placeholder="动动小手,(建议)自行搭建后端服务。例:http://127.0.0.1:25500?sub"
>
<el-button
slot="append"
@click="gotoGayhub"
icon="el-icon-link"
>前往项目仓库</el-button
>
</el-input>
</el-form-item>
<el-form-item label="远程配置:">
<el-select
v-model="form.remoteConfig"
allow-create
filterable
placeholder="请选择"
style="width: 100%"
>
<el-option-group
v-for="group in options.remoteConfig"
:key="group.label"
:label="group.label"
>
<el-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-option-group>
<el-button
slot="append"
@click="gotoRemoteConfig"
icon="el-icon-link"
>配置示例</el-button
>
</el-select>
</el-form-item>
<el-form-item label="IncludeRemarks:">
<el-input
v-model="form.includeRemarks"
placeholder="节点名包含的关键字,支持正则"
/>
</el-form-item>
<el-form-item label="ExcludeRemarks:">
<el-input
v-model="form.excludeRemarks"
placeholder="节点名不包含的关键字,支持正则"
/>
</el-form-item>
</div>
<div style="margin-top: 50px"></div>
<el-divider content-position="center">
<i class="el-icon-magic-stick" />
</el-divider>
<el-form-item label="定制订阅:">
<el-input class="copy-content" disabled v-model="customSubUrl">
<el-button
slot="append"
v-clipboard:copy="customSubUrl"
v-clipboard:success="onCopy"
ref="copy-btn"
icon="el-icon-document-copy"
>复制</el-button
>
</el-input>
</el-form-item>
<el-form-item
label-width="0px"
style="margin-top: 40px; text-align: center"
>
<el-button style="width: 120px" type="danger" @click="makeUrl"
>生成订阅链接</el-button
>
<el-button
style="width: 120px"
type="primary"
@click="clashInstall"
icon="el-icon-connection"
>一键导入Clash</el-button
>
<el-button
style="width: 120px"
type="primary"
@click="surgeInstall"
icon="el-icon-connection"
>一键导入Surge</el-button
>
</el-form-item>
</el-form>
</el-container>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
const remoteConfigSample =
"https://raw.githubusercontent.com/tindy2013/subconverter/master/base/example_external_config.ini";
const gayhubRelease = "https://github.com/tindy2013/subconverter/releases";
const defaultBackend = "https://api.wcc.best/sub?";
export default {
data() {
return {
advanced: "2",
options: {
clientTypes: {
clash: "clash",
clashr: "clashr",
surge2: "surge&ver=2",
surge3: "surge&ver=3",
surge4: "surge&ver=4",
quantumult: "quan",
quantumultx: "quanx",
surfboard: "surfboard",
ss: "ss",
ssr: "ssr",
ssd: "ssd",
v2ray: "v2ray"
},
customBaseRules: [
"ClashBaseRule",
"SurgeBaseRule",
"SurfboardRuleBase"
],
remoteConfig: [
{
label: "universal",
options: [
{
label: "No-Urltest",
value:
"https://careywong-public-docs.oss-cn-shanghai.aliyuncs.com/subconverter/universal/no-urltest.ini"
},
{
label: "Urltest",
value:
"https://careywong-public-docs.oss-cn-shanghai.aliyuncs.com/subconverter/universal/urltest.ini"
}
]
},
{
label: "customized",
options: [
{
label: "Maying",
value:
"https://careywong-public-docs.oss-cn-shanghai.aliyuncs.com/subconverter/customized/maying.ini"
},
{
label: "Nexitally",
value:
"https://careywong-public-docs.oss-cn-shanghai.aliyuncs.com/subconverter/customized/nexitally.ini"
}
]
},
{
label: "Special",
options: [
{
label: "NeteaseUnblock",
value:
"https://careywong-public-docs.oss-cn-shanghai.aliyuncs.com/subconverter/special/netease.ini"
}
]
}
]
},
form: {
sourceSubUrl: "",
clientType: "",
emoji: "true",
customBackend: "",
remoteConfig: "",
excludeRemarks: "",
includeRemarks: "",
ClashBaseRule: "",
SurgeBaseRule: "",
SurfboardRuleBase: "",
rename_node: "",
ruleset: ""
},
loading: false,
customSubUrl: ""
};
},
created() {
document.title = "Subscription Converter";
},
mounted() {
this.form.clientType = "clashr";
},
methods: {
onCopy() {
this.$message.success("Copied!");
},
gotoGayhub() {
window.open(gayhubRelease);
},
gotoRemoteConfig() {
window.open(remoteConfigSample);
},
createFilter(queryString) {
return restaurant => {
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) ===
0
);
};
},
clashInstall() {
if (this.customSubUrl === "") {
this.$message.error("请先填写必填项,生成订阅链接");
return false;
}
const url = "clash://install-config?url=";
window.open(url + encodeURIComponent(this.customSubUrl));
},
surgeInstall() {
if (this.customSubUrl === "") {
this.$message.error("请先填写必填项,生成订阅链接");
return false;
}
const url = "surge://install-config?url=";
window.open(url + this.customSubUrl);
},
makeUrl() {
if (this.form.sourceSubUrl === "" || this.form.clientType === "") {
this.$message.error("订阅链接与客户端为必填项");
return false;
}
let backend =
this.form.customBackend === ""
? defaultBackend
: this.form.customBackend;
let sourceSub = this.form.sourceSubUrl;
sourceSub = sourceSub.replace(/[\n|\r|\n\r]/g, "|");
this.customSubUrl =
backend +
"target=" +
this.form.clientType +
"&url=" +
encodeURIComponent(sourceSub);
if (this.advanced === "2") {
if (this.form.remoteConfig !== "") {
this.customSubUrl +=
"&config=" + encodeURIComponent(this.form.remoteConfig);
}
if (this.form.emoji === "false") {
this.customSubUrl += "&emoji=" + this.form.emoji;
}
if (this.form.excludeRemarks !== "") {
this.customSubUrl +=
"&exclude=" + encodeURIComponent(this.form.excludeRemarks);
}
if (this.form.includeRemarks !== "") {
this.customSubUrl +=
"&include=" + encodeURIComponent(this.form.includeRemarks);
}
}
this.$copyText(this.customSubUrl);
this.$message.success("定制订阅已复制到剪切板");
}
}
};
</script>

9
vue.config.js

@ -0,0 +1,9 @@
module.exports = {
css: {
loaderOptions: {
less: {
javascriptEnabled: true
}
}
}
};

8882
yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save