完成vue的切换

master
yutent 2022-10-16 22:59:44 +08:00
parent 4cb6e54aed
commit 633ae92a71
15 changed files with 361 additions and 289 deletions

View File

@ -12,6 +12,8 @@
"name": "yutent", "name": "yutent",
"email": "yutent.io@gmail.com" "email": "yutent.io@gmail.com"
}, },
"devDependencies": {}, "devDependencies": {
"@bytedo/vue-live": "0.0.5"
},
"dependencies": {} "dependencies": {}
} }

View File

@ -1,9 +1,11 @@
<template> <template>
<Sidebar></Sidebar> <Sidebar />
<h1>hello vue-live!</h1> <router-view />
</template> </template>
<script> <script>
import fetch from '@/lib/fetch'
import Sidebar from './components/sidebar.vue' import Sidebar from './components/sidebar.vue'
export default { export default {
@ -13,7 +15,14 @@ export default {
}, },
mounted() { mounted() {
console.log(this.$store) this.getVersion()
},
methods: {
getVersion() {
fetch('/version').then(r => {
this.$store.version = r.version
})
}
} }
} }
</script> </script>

View File

@ -5,10 +5,11 @@
<nav class="nav-list"> <nav class="nav-list">
<a <a
class="item" class="item"
v-for="(it, i) in navs" v-for="it in navs"
:class="{ active: i === tab }" :key="it.path"
@click="changeTab(i)" :class="{ active: it.path === tab }"
:text="it" @click="changeTab(it)"
:text="it.title"
> >
</a> </a>
</nav> </nav>
@ -22,14 +23,21 @@
export default { export default {
data() { data() {
return { return {
navs: ['代理', '规则', '连接', '订阅', '设置'], navs: [
tab: +localStorage.getItem('tab') || 0 { title: '代理', path: '/proxies' },
{ title: '规则', path: '/rules' },
{ title: '连接', path: '/connects' },
{ title: '订阅', path: '/remote' },
{ title: '设置', path: '/config' }
],
tab: localStorage.getItem('tab') || '/proxies'
} }
}, },
methods: { methods: {
changeTab(idx) { changeTab(it) {
this.tab = idx this.tab = it.path
localStorage.setItem('tab', idx) localStorage.setItem('tab', it.path)
this.$router.push(it.path)
} }
} }
} }

View File

@ -1 +0,0 @@
.tab-content{flex:1;flex-shrink:0;display:flex;flex-direction:column;height:540px;padding:16px}.tab-content .field{display:flex;align-items:center;width:100%;height:36px;margin-top:12px}.tab-content .field .label{width:128px;font-weight:bold;color:var(--color-grey-3)}.tab-content .field .full{flex:1}.tab-content.configs{width:49%;margin-left:1%}.tab-content.remote wc-button,.tab-content.rules wc-button{width:64px;min-width:64px;margin-left:16px}.tab-content.remote .scroll,.tab-content.rules .scroll{overflow:hidden;flex:1;margin-top:24px}.tab-content.remote .card,.tab-content.rules .card{margin:0 auto}.tab-content.remote .list,.tab-content.rules .list{word-break:break-all}.tab-content.remote .list .item,.tab-content.rules .list .item{display:flex;align-items:center;justify-content:space-between;height:36px;padding:0 16px;border-bottom:1px solid var(--color-plain-1);font-family:Menlo,"Courier New",Courier,monospace;transition:background .2s linear}.tab-content.remote .list .item:hover,.tab-content.rules .list .item:hover{background-color:var(--color-plain-1)}.tab-content.remote .list span.type,.tab-content.rules .list span.type{padding:2px 3px;border-radius:2px;background-color:var(--color-blue-1);color:#fff}.card{flex:1;flex-shrink:0;display:flex;flex-direction:column;flex-wrap:wrap;width:100%;max-height:100%;padding:12px;margin:0 auto 24px;border:0;border-radius:4px;background-color:#fff;box-shadow:0 0 8px rgba(0,0,0,.075)}.card legend{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;color:var(--color-blue-1);font-weight:bold}

View File

@ -29,97 +29,9 @@
<span class="version">{{version}}</span> <span class="version">{{version}}</span>
</aside> </aside>
<main class="tab-content" :if="tab === 0"></main>
<main class="tab-content rules" :if="tab === 1">
<fieldset class="card">
<legend>规则列表</legend>
<wc-scroll class="scroll">
<ul class="list">
<li class="item" :for="i it in rules">
<span>{{it.type}}</span>
<span>{{it.payload}}</span>
<span class="type">{{it.proxy}}</span>
</li>
</ul>
</wc-scroll>
</fieldset>
</main>
<main class="tab-content" :if="tab === 2"></main>
<main class="tab-content remote" :if="tab === 3">
<fieldset class="card">
<legend>订阅配置</legend>
<section class="field">
<span class="label">订阅地址</span>
<wc-input class="full" :duplex="remote.link"></wc-input>
<wc-button type="info" :disabled="!remote.link" @click="updateRemote">更新</wc-button>
</section>
<wc-scroll class="scroll">
<ul class="list">
<li class="item" :for="i it in remote.list">
{{i + 1}}.
<span>{{it[0]}}</span>
<span class="type">{{it[1]}}</span>
</li>
</ul>
</wc-scroll>
</fieldset>
</main>
<main class="tab-content configs" :if="tab === 4">
<fieldset class="card">
<legend>系统设置</legend>
<section class="field">
<span class="label">开机启动Clash</span>
<wc-switch disabled></wc-switch>
</section>
<section class="field">
<span class="label">设置为系统代理</span>
<wc-switch disabled></wc-switch>
</section>
<section class="field">
<span class="label">允许局域网的连接</span>
<wc-switch :duplex="configs.allowLan"></wc-switch>
</section>
</fieldset>
<fieldset class="card">
<legend>Clash设置</legend>
<section class="field">
<span class="label">代理模式</span>
<wc-radio-group :duplex="configs.proxy">
<wc-radio value="global">全局</wc-radio>
<wc-radio value="rule">规则</wc-radio>
<wc-radio value="direct">直连</wc-radio>
</wc-radio-group>
</section>
<section class="field">
<span class="label">Socks5 代理端口</span>
<wc-input size="small" :duplex="configs.socks5"></wc-input>
</section>
<section class="field">
<span class="label">HTTP 代理端口</span>
<wc-input size="small" :duplex="configs.http"></wc-input>
</section>
<section class="field">
<span class="label">混合代理端口</span>
<wc-input size="small" :duplex="configs.mixed"></wc-input>
</section>
</fieldset>
</main>
</div> </div>
</body> </body>

View File

@ -1,168 +0,0 @@
/**
*
* @author yutent<yutent.io@gmail.com>
* @date 2022/05/16 00:38:48
*/
import Anot from '//unpkg.yutent.top/anot/dist/anot.js'
import fetch from '//unpkg.yutent.top/@bytedo/fetch/dist/index.js'
import '//unpkg.yutent.top/@bytedo/wcui/dist/layer/index.js'
import '//unpkg.yutent.top/@bytedo/wcui/dist/form/switch.js'
import '//unpkg.yutent.top/@bytedo/wcui/dist/form/radio.js'
import '//unpkg.yutent.top/@bytedo/wcui/dist/form/button.js'
fetch.inject.response(r => r.json())
Anot({
$id: 'app',
state: {
version: '1.10.0',
navs: ['代理', '规则', '连接', '订阅', '设置'],
tab: +Anot.ls('tab') || 0,
contrl: {
host: '//127.0.0.1:',
port: 6767,
key: ''
},
rules: [],
remote: {
link: Anot.ls('remote_link') || '',
list: []
},
configs: {
proxy: 'rule',
http: 0,
socks5: 0,
mixed: 7890,
allowLan: false
}
},
mounted() {
if (this.contrl.port) {
fetch.BASE_URL = this.contrl.host + this.contrl.port
} else {
return layer
.prompt('请输入Clash本地管理端口', (v, done) => {
let n = +v.trim()
if (n === n) {
done()
}
})
.then(v => {
Anot.ls('web_port', v)
location.reload()
})
.catch(e => {
location.reload()
})
}
this.getVersion()
this.getConfig()
this.getRule()
},
methods: {
getVersion() {
fetch('/version').then(r => {
this.version = r.version
})
},
getConfig() {
fetch('/configs').then(r => {
console.log(r)
Object.assign(this.configs, {
proxy: r.mode,
http: r.port,
socks5: r['socks-port'],
mixed: r['mixed-port'],
allowLan: r['allow-lan']
})
// this.version = r.version
})
},
getRule() {
fetch('/rules').then(r => {
console.log(r)
this.rules = r.rules
})
},
updateRemote() {
if (this.remote.link) {
if (!/^(https?:)?\/\//.test(this.remote.link)) {
return layer.toast('订阅地址格式不正确', 'error')
}
Anot.ls('remote_link', this.remote.link)
} else {
return
}
let txt = ''
let names = ['DIRECT']
let r = (Anot.ss('temp') || '').trim()
this.remote.list = r.split('\n').map(it => {
let tmp = decodeURIComponent(it).split('#')
let tmp2 = new URL(tmp[0])
let protocol = tmp2.protocol.slice(0, -1)
let path = tmp2.pathname.slice(2)
let info = { name: tmp.pop(), type: protocol, udp: true }
path = path.split(':')
info.port = path.pop()
path = path[0].split('@')
info.server = path.pop()
switch (protocol) {
case 'ss':
path = atob(path[0]).split(':')
console.log(path)
Object.assign(info, {
cipher: path[0],
password: path[1]
})
break
case 'trojan':
Object.assign(info, {
password: path[0],
sni: tmp2.searchParams.get('sni'),
'skip-cert-verify': true
})
break
}
names.push(info.name)
txt += ` - { name: ${info.name}, type: ${info.type}, server: ${info.server}, port: ${
info.port
}, password: ${info.password}, udp: true, ${info.type === 'ss' ? 'cipher' : 'sni'}: ${
info.type === 'ss' ? info.cipher : info.sni
}}\n`
return [info.name, info.type]
})
// window.foo = txt
// window.bar = names.join(', ')
// // console.log(txt)
return
window
.fetch(this.remote.link, { cors: true })
.then(r => r.text())
.then(r => {
r = atob(r).trim()
Anot.ss('temp', r)
layer.toast('订阅更新成功', 'success')
this.remote.list = r.split('\n').map(it => {
let tmp = decodeURIComponent(it).split('#')
return [tmp.pop(), tmp[0]?.split('://').shift()]
})
})
}
}
})

9
src/lib/fetch.js Normal file
View File

@ -0,0 +1,9 @@
import fetch from 'fetch'
fetch.BASE_URL = '//127.0.0.1:6767'
fetch.inject.response(function (r) {
return r.json()
})
export default fetch

View File

@ -6,6 +6,13 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import '@/assets/app.scss'
import '//unpkg.yutent.top/@bytedo/wcui/dist/layer/index.js'
import '//unpkg.yutent.top/@bytedo/wcui/dist/form/switch.js'
import '//unpkg.yutent.top/@bytedo/wcui/dist/form/radio.js'
import '//unpkg.yutent.top/@bytedo/wcui/dist/form/button.js'
import App from './app.vue' import App from './app.vue'
import store from './store' import store from './store'

View File

@ -4,26 +4,42 @@
* @date 2022/10/09 18:09:20 * @date 2022/10/09 18:09:20
*/ */
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHashHistory } from 'vue-router'
import Proxies from './views/proxies.vue'
import Rules from './views/rules.vue'
import Config from './views/config.vue'
import Connects from './views/connects.vue'
import Remote from './views/remote.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHashHistory(),
routes: [ routes: [
// { {
// path: '/login', path: '/proxies',
// name: 'login', name: 'proxies',
// component: Login component: Proxies
// }, },
// { {
// path: '/', path: '/rules',
// name: 'main', name: 'rules',
// component: Main component: Rules
// }, },
// { {
// path: '/room', path: '/connects',
// name: 'room', name: 'connects',
// component: Room component: Connects
// } },
{
path: '/remote',
name: 'remote',
component: Remote
},
{
path: '/config',
name: 'config',
component: Config
}
] ]
}) })

100
src/views/config.vue Normal file
View File

@ -0,0 +1,100 @@
<template>
<main class="tab-content configs">
<fieldset class="card">
<legend>系统设置</legend>
<section class="field">
<span class="label">开机启动Clash</span>
<wc-switch disabled></wc-switch>
</section>
<section class="field">
<span class="label">设置为系统代理</span>
<wc-switch disabled></wc-switch>
</section>
<section class="field">
<span class="label">允许局域网的连接</span>
<wc-switch v-model="configs.allowLan"></wc-switch>
</section>
</fieldset>
<fieldset class="card">
<legend>Clash设置</legend>
<section class="field">
<span class="label">代理模式</span>
<wc-radio-group v-model="configs.proxy">
<wc-radio value="global">全局</wc-radio>
<wc-radio value="rule">规则</wc-radio>
<wc-radio value="direct">直连</wc-radio>
</wc-radio-group>
</section>
<section class="field">
<span class="label">Socks5 代理端口</span>
<wc-input size="small" v-model="configs.socks5"></wc-input>
</section>
<section class="field">
<span class="label">HTTP 代理端口</span>
<wc-input size="small" v-model="configs.http"></wc-input>
</section>
<section class="field">
<span class="label">混合代理端口</span>
<wc-input size="small" v-model="configs.mixed"></wc-input>
</section>
</fieldset>
</main>
</template>
<script>
import fetch from '@/lib/fetch'
export default {
data() {
return {
contrl: {
host: '//127.0.0.1:',
port: 6767,
key: ''
},
configs: {
proxy: 'rule',
http: 0,
socks5: 0,
mixed: 7890,
allowLan: false
}
}
},
mounted() {
this.getConfig()
},
methods: {
getVersion() {
fetch('/version').then(r => {
console.log(r)
this.$store.version = r.version
})
},
getConfig() {
fetch('/configs').then(r => {
console.log(r)
Object.assign(this.configs, {
proxy: r.mode,
http: r.port,
socks5: r['socks-port'],
mixed: r['mixed-port'],
allowLan: r['allow-lan']
})
// this.version = r.version
})
}
}
}
</script>

11
src/views/connects.vue Normal file
View File

@ -0,0 +1,11 @@
<template>
<main class="tab-content"></main>
</template>
<script>
export default {
data() {
return {}
}
}
</script>

11
src/views/proxies.vue Normal file
View File

@ -0,0 +1,11 @@
<template>
<main class="tab-content"></main>
</template>
<script>
export default {
data() {
return {}
}
}
</script>

114
src/views/remote.vue Normal file
View File

@ -0,0 +1,114 @@
<template>
<main class="tab-content remote">
<fieldset class="card">
<legend>订阅配置</legend>
<section class="field">
<span class="label">订阅地址</span>
<wc-input class="full" v-model="remote.link"></wc-input>
<wc-button type="info" :disabled="!remote.link" @click="updateRemote"></wc-button>
</section>
<wc-scroll class="scroll">
<ul class="list">
<li class="item" v-for="(it, i) in remote.list">
{{ i + 1 }}.
<span>{{ it[0] }}</span>
<span class="type">{{ it[1] }}</span>
</li>
</ul>
</wc-scroll>
</fieldset>
</main>
</template>
<script>
import fetch from '@/lib/fetch'
export default {
data() {
return {
remote: {
link: localStorage.getItem('remote_link') || '',
list: []
}
}
},
methods: {
updateRemote() {
if (this.remote.link) {
if (!/^(https?:)?\/\//.test(this.remote.link)) {
return layer.toast('订阅地址格式不正确', 'error')
}
localStorage.setItem('remote_link', this.remote.link)
} else {
return
}
let txt = ''
let names = ['DIRECT']
let r = (sessionStorage.getItem('temp') || '').trim()
this.remote.list = r.split('\n').map(it => {
let tmp = decodeURIComponent(it).split('#')
let tmp2 = new URL(tmp[0])
let protocol = tmp2.protocol.slice(0, -1)
let path = tmp2.pathname.slice(2)
let info = { name: tmp.pop(), type: protocol, udp: true }
path = path.split(':')
info.port = path.pop()
path = path[0].split('@')
info.server = path.pop()
switch (protocol) {
case 'ss':
path = atob(path[0]).split(':')
console.log(path)
Object.assign(info, {
cipher: path[0],
password: path[1]
})
break
case 'trojan':
Object.assign(info, {
password: path[0],
sni: tmp2.searchParams.get('sni'),
'skip-cert-verify': true
})
break
}
names.push(info.name)
txt += ` - { name: ${info.name}, type: ${info.type}, server: ${info.server}, port: ${
info.port
}, password: ${info.password}, udp: true, ${info.type === 'ss' ? 'cipher' : 'sni'}: ${
info.type === 'ss' ? info.cipher : info.sni
}}\n`
return [info.name, info.type]
})
window.foo = txt
window.bar = names.join(', ')
// // console.log(txt)
return
window
.fetch(this.remote.link, { cors: true })
.then(r => r.text())
.then(r => {
r = atob(r).trim()
sessionStorage.setItem('temp', r)
layer.toast('订阅更新成功', 'success')
this.remote.list = r.split('\n').map(it => {
let tmp = decodeURIComponent(it).split('#')
return [tmp.pop(), tmp[0]?.split('://').shift()]
})
})
}
}
}
</script>

42
src/views/rules.vue Normal file
View File

@ -0,0 +1,42 @@
<template>
<main class="tab-content rules">
<fieldset class="card">
<legend>规则列表</legend>
<wc-scroll class="scroll">
<ul class="list">
<li class="item" v-for="it in rules">
<span>{{ it.type }}</span>
<span>{{ it.payload }}</span>
<span class="type">{{ it.proxy }}</span>
</li>
</ul>
</wc-scroll>
</fieldset>
</main>
</template>
<script>
import fetch from '@/lib/fetch'
export default {
data() {
return {
rules: []
}
},
mounted() {
this.getRule()
},
methods: {
getRule() {
fetch('/rules').then(r => {
console.log(r)
this.rules = r.rules
})
}
}
}
</script>