perf: 🍻 Update base copilot

This commit is contained in:
viarotel 2023-11-21 16:37:02 +08:00
parent 3dab0cc833
commit 5ac5ee6e97
17 changed files with 444 additions and 29 deletions

63
copilot/App.vue Normal file
View File

@ -0,0 +1,63 @@
<template>
<div class="flex flex-col absolute inset-fix-0 h-full overflow-hidden">
<div class="py-4 px-4 flex items-center flex-none">
<a class="block" :href="escrcpyURL" target="_blank">
<img src="@electron/resources/build/logo.png" class="h-9" alt="" />
</a>
<div class="pl-2 text-sm">
Escrcpy Copilot
</div>
</div>
<div class="flex-1 h-0 overflow-hidden bg-gray-100">
<el-tabs v-model="tabValue" class="el-tabs-flex" @tab-click="onTabClick">
<el-tab-pane
v-for="(item, index) of tabModel"
:key="index"
:label="item.label"
:name="item.value"
lazy
class=""
>
<component :is="item.value" />
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import Transmission from './components/Transmission/index.vue'
export default {
components: {
Transmission,
},
data() {
return {
escrcpyURL: 'https://github.com/viarotel-org/escrcpy',
tabValue: 'Transmission',
tabModel: [
{
label: '传输助手',
value: 'Transmission',
},
],
}
},
methods: {
onTabClick() {},
},
}
</script>
<style lang="postcss" scoped>
:deep() {
.el-tabs__header {
@apply bg-white px-4;
}
.el-tabs__nav-wrap::after {
@apply bg-transparent;
}
}
</style>

View File

@ -0,0 +1,55 @@
<template>
<Message v-bind="messageProps">
<slot></slot>
</Message>
</template>
<script>
import Message from './index.vue'
export default {
components: {
Message,
},
props: {
type: {
type: String,
default: '',
},
content: {
type: String,
default: '',
},
},
computed: {
messageProps() {
let value = {}
switch (this.type) {
case 'server':
value = {
name: 'PC',
content: this.content,
avatar: 'computer',
}
break
case 'client':
value = {
name: this.$attrs.name || '我的手机',
content: this.content,
avatar: 'mobile',
reversed: true,
}
break
default:
value = this.$attrs
break
}
return value
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,81 @@
<template>
<div :class="reversed ? 'flex-row-reverse' : ''" class="flex items-start">
<img :src="avatarURL" alt="" class="w-12 h-12 flex-none" />
<div
class="flex flex-col"
:class="reversed ? 'mr-4 pl-16 items-end' : 'ml-4 pr-16'"
>
<div v-if="!reversed" class="mt-1 text-base">
{{ name }}
</div>
<div
class="mt-2 shadow-el-light px-4 py-2 rounded-lg break-all overflow-hidden relative"
>
<slot>
<span class="pr-1">
{{ content }}
</span>
<el-icon v-if="loading" class="is-loading relative top-[2px]">
<Loading />
</el-icon>
</slot>
</div>
</div>
</div>
</template>
<script>
import logoURL from '@electron/resources/build/logo.png'
import userURL from '@/assets/user.png'
import mobileURL from '@/assets/mobile.png'
import computerURL from '@/assets/computer.png'
export default {
props: {
reversed: {
type: Boolean,
default: false,
},
avatar: {
type: String,
default: 'logo',
},
name: {
type: String,
default: '',
},
content: {
type: String,
default: '',
},
loading: {
type: Boolean,
default: false,
},
},
computed: {
avatarURL() {
let value = ''
switch (this.avatar) {
case 'logo':
value = logoURL
break
case 'user':
value = userURL
break
case 'mobile':
value = mobileURL
break
case 'computer':
value = computerURL
break
}
return value
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,177 @@
<template>
<div class="h-full flex flex-col">
<div
ref="chats"
class="flex-1 h-0 space-y-4 pb-4 px-4 overflow-auto scroll-smooth"
>
<Message
v-for="(item, index) of messageList"
v-bind="{
type: item.type,
content: item.content,
}"
:key="index"
:loading="item.$loading"
>
</Message>
</div>
<div class="flex-none px-4 py-2 bg-white">
<el-input
v-model="inputValue"
placeholder="请输入想要发送的消息"
@keyup.enter="handleSubmit"
>
<template #append>
<el-button
icon="Promotion"
:loading="loading"
@click="handleSubmit"
/>
</template>
</el-input>
</div>
</div>
</template>
<script>
import Message from './Message/Preset.vue'
export default {
components: {
Message,
},
data() {
return {
messageList: [],
inputValue: '',
loading: false,
}
},
async created() {
await this.getMessageData()
await this.$nextTick()
this.handleScroll()
},
methods: {
handleScroll() {
const chatsEl = this.$refs.chats
chatsEl.scrollTop = chatsEl.scrollHeight
},
async handleSubmit() {
if (!this.inputValue) {
return false
}
this.loading = true
const newMessage = {
type: 'client',
content: this.inputValue,
$loading: true,
}
this.messageList.push(newMessage)
const params = {}
const res = await this.$mockAPI(params)
this.loading = false
if (res.success) {
this.inputValue = ''
newMessage.$loading = false
await this.$nextTick()
this.handleScroll()
}
},
async getMessageData() {
const params = {
imitate: [
{
type: 'server',
content: '你好啊',
},
{
type: 'client',
content: '你也好啊',
},
{
type: 'server',
content: '你好啊',
},
{
type: 'client',
content: '你也好啊',
},
{
type: 'server',
content: '你好啊',
},
{
type: 'client',
content: '你也好啊',
},
{
type: 'server',
content: '你好啊',
},
{
type: 'client',
content: '你也好啊',
},
{
type: 'server',
content: '你好啊',
},
{
type: 'client',
content: '你也好啊',
},
{
type: 'server',
content: '你好啊',
},
{
type: 'client',
content: '你也好啊',
},
{
type: 'server',
content: '你好啊',
},
{
type: 'client',
content: '你也好啊',
},
{
type: 'server',
content: '你好啊',
},
{
type: 'client',
content: '你也好啊',
},
{
type: 'server',
content: '你好啊',
},
{
type: 'client',
content: '你也好啊',
},
],
}
const res = await this.$mockAPI(params)
if (res.success) {
this.messageList = res.data.map(item => ({
...item,
$loading: false,
}))
}
},
},
}
</script>
<style></style>

View File

@ -1,10 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="dark"> <html lang="en" class="">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/logo.ico" /> <link rel="icon" href="/logo.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Escrcpy-Server</title> <title>Escrcpy Copilot</title>
</head> </head>
<body class=""> <body class="">
<div id="app"></div> <div id="app"></div>

37
copilot/main.js Normal file
View File

@ -0,0 +1,37 @@
import { createApp, toRaw } from 'vue'
import App from './App.vue'
import { i18n, t } from '@/locales/index.js'
import plugins from '@/plugins/index.js'
import icons from '@/icons/index.js'
import { replaceIP, restoreIP } from '@/utils/index.js'
import 'virtual:uno.css'
import '@/styles/index.js'
const app = createApp(App)
app.use(plugins)
app.use(icons)
app.use(i18n)
window.t = t
app.config.globalProperties.$replaceIP = replaceIP
app.config.globalProperties.$restoreIP = restoreIP
app.config.globalProperties.$toRaw = toRaw
app.config.globalProperties.$mockAPI = ({ imitate = {}, delay = 500 } = {}) =>
new Promise((resolve) => {
setTimeout(() => {
resolve({
code: '0000',
data: imitate,
success: true,
})
}, delay)
})
app.mount('#app')

View File

@ -7,14 +7,14 @@ export default async (mainWindow) => {
const app = new Hono() const app = new Hono()
app.notFound((c) => { app.notFound((c) => {
return c.text('Escrcpy server 404', 404) return c.text('Escrcpy copilot 404', 404)
}) })
const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL
if (VITE_DEV_SERVER_URL) { if (VITE_DEV_SERVER_URL) {
app.get('/', ctx => app.get('/', ctx =>
ctx.redirect(`${VITE_DEV_SERVER_URL}server/index.html`), ctx.redirect(`${VITE_DEV_SERVER_URL}copilot/index.html`),
) )
} }
else { else {
@ -23,7 +23,7 @@ export default async (mainWindow) => {
serveStatic({ serveStatic({
root: relative('./', process.env.DIST), root: relative('./', process.env.DIST),
rewriteRequestPath: (path) => { rewriteRequestPath: (path) => {
return path.replace(/^\//, '/server') return path.replace(/^\//, '/copilot')
}, },
}), }),
) )

View File

@ -14,7 +14,7 @@ import { icnsLogoPath, icoLogoPath, logoPath } from './configs/index.js'
import events from './events/index.js' import events from './events/index.js'
import server from './server/index.js' import copilot from './copilot/index.js'
log.initialize({ preload: true }) log.initialize({ preload: true })
@ -104,7 +104,7 @@ function createWindow() {
events(mainWindow) events(mainWindow)
server(mainWindow) copilot(mainWindow)
} }
app.whenReady().then(() => { app.whenReady().then(() => {

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="dark"> <html lang="en" class="">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/logo.ico" /> <link rel="icon" href="/logo.ico" />

View File

@ -3,11 +3,12 @@
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["src/*"], "@/*": ["src/*"],
"@copilot/*": ["copilot/*"],
"@root/*": ["*"], "@root/*": ["*"],
"@electron/*": ["electron/*"], "@electron/*": ["electron/*"],
"@renderer/*": ["src/*"] "@renderer/*": ["src/*"]
} }
}, },
"exclude": ["node_modules", "dist", "dist-electron", "dist-release"], "exclude": ["node_modules", "dist", "dist-electron", "dist-release"],
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.vue", "electron"] "include": ["src", "electron", "copilot"]
} }

View File

@ -1,3 +0,0 @@
import { createApp } from 'vue'
console.log('createApp', createApp)

BIN
src/assets/computer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
src/assets/mobile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
src/assets/user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -2,7 +2,7 @@ import { createI18n } from 'vue-i18n'
import messages from '@intlify/unplugin-vue-i18n/messages' import messages from '@intlify/unplugin-vue-i18n/messages'
const locale const locale
= window.appStore.get('common.language') = window.appStore?.get('common.language')
|| window.electron?.process?.env?.LOCALE || window.electron?.process?.env?.LOCALE
// const locale = 'en_US' // const locale = 'en_US'

View File

@ -3,12 +3,14 @@ html {
} }
/* 自定义滚动条的外观 */ /* 自定义滚动条的外观 */
@screen sm {
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; width: 8px;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
background-color: theme("colors.gray.100"); background-color: theme('colors.gray.100');
@apply bg-gray-100 dark:bg-gray-800; @apply bg-gray-100 dark:bg-gray-800;
} }
@ -20,3 +22,4 @@ html {
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
@apply bg-gray-500 dark:bg-gray-300; @apply bg-gray-500 dark:bg-gray-300;
} }
}

View File

@ -35,13 +35,14 @@ export default params =>
rollupOptions: { rollupOptions: {
input: { input: {
main: resolve(__dirname, 'index.html'), main: resolve(__dirname, 'index.html'),
server: resolve(__dirname, 'server/index.html'), copilot: resolve(__dirname, 'copilot/index.html'),
}, },
}, },
}, },
resolve: { resolve: {
alias: { alias: {
'@': resolve('src'), '@': resolve('src'),
'@copilot': resolve('copilot'),
'@electron': resolve('electron'), '@electron': resolve('electron'),
}, },
}, },