perf: 💄 窗口控制及交互逻辑优化
@ -189,6 +189,13 @@
|
|||||||
4. 按下 `Ctrl` + `Shift` + `I` 进入开发者工具,并查看是否有任何报错信息。
|
4. 按下 `Ctrl` + `Shift` + `I` 进入开发者工具,并查看是否有任何报错信息。
|
||||||
5. 如果有报错,请截图并在 [反馈问题](https://github.com/viarotel-org/escrcpy/issues) 页面中提交您的问题。
|
5. 如果有报错,请截图并在 [反馈问题](https://github.com/viarotel-org/escrcpy/issues) 页面中提交您的问题。
|
||||||
|
|
||||||
|
### macOS 关闭窗口选择最小化到托盘后顶部右侧状态栏找不到图标
|
||||||
|
|
||||||
|
> 这个一般是状态栏图标过多导致无法展示 Escrcpy 的图标 推荐用以下工具解决
|
||||||
|
|
||||||
|
- [iBar](https://www.better365.cn/ibar.html)
|
||||||
|
- [Bartender](https://www.macbartender.com/)
|
||||||
|
|
||||||
## 获得帮助
|
## 获得帮助
|
||||||
|
|
||||||
> 因为是开源项目 全靠爱发电 所以支持有限 更新节奏不固定
|
> 因为是开源项目 全靠爱发电 所以支持有限 更新节奏不固定
|
||||||
|
@ -10,14 +10,31 @@ export default (mainWindow) => {
|
|||||||
tray.destroy()
|
tray.destroy()
|
||||||
tray = null
|
tray = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
app.dock.show()
|
||||||
|
}
|
||||||
|
|
||||||
mainWindow.show()
|
mainWindow.show()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hideApp = () => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
app.dock.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.hide()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const quitApp = () => {
|
const quitApp = () => {
|
||||||
app.isQuiting = true
|
app.isQuiting = true
|
||||||
|
|
||||||
app.quit()
|
app.quit()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +44,7 @@ export default (mainWindow) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else if (response === 1) {
|
else if (response === 1) {
|
||||||
mainWindow.hide()
|
hideApp()
|
||||||
|
|
||||||
tray = new Tray(trayPath)
|
tray = new Tray(trayPath)
|
||||||
|
|
||||||
|
@ -20,10 +20,6 @@ window.addEventListener('beforeunload', () => {
|
|||||||
appStore.onDidChange('scrcpy.global.adbPath', async (value, oldValue) => {
|
appStore.onDidChange('scrcpy.global.adbPath', async (value, oldValue) => {
|
||||||
console.log('onDidChange.scrcpy.global.adbPath', value)
|
console.log('onDidChange.scrcpy.global.adbPath', value)
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === oldValue) {
|
if (value === oldValue) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -37,7 +33,7 @@ appStore.onDidChange('scrcpy.global.adbPath', async (value, oldValue) => {
|
|||||||
client = null
|
client = null
|
||||||
}
|
}
|
||||||
|
|
||||||
client = Adb.createClient({ bin: value })
|
client = Adb.createClient({ bin: value || adbPath })
|
||||||
})
|
})
|
||||||
|
|
||||||
const shell = async command => exec(`${adbPath} ${command}`)
|
const shell = async command => exec(`${adbPath} ${command}`)
|
||||||
|
@ -4,10 +4,11 @@ import { adbPath, scrcpyPath } from '@electron/configs/index.js'
|
|||||||
|
|
||||||
const shell = async (command, { stdout, stderr } = {}) => {
|
const shell = async (command, { stdout, stderr } = {}) => {
|
||||||
const spawnPath = appStore.get('scrcpy.global.scrcpyPath') || scrcpyPath
|
const spawnPath = appStore.get('scrcpy.global.scrcpyPath') || scrcpyPath
|
||||||
|
const ADB = appStore.get('scrcpy.global.adbPath') || adbPath
|
||||||
const args = command.split(' ')
|
const args = command.split(' ')
|
||||||
|
|
||||||
const scrcpyProcess = spawn(spawnPath, args, {
|
const scrcpyProcess = spawn(spawnPath, args, {
|
||||||
env: { ...process.env, ADB: adbPath },
|
env: { ...process.env, ADB },
|
||||||
shell: true,
|
shell: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -37,8 +37,13 @@ function createWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
show: false,
|
// 这里设置的图标仅在开发模式生效,打包后将使用应用程序图标
|
||||||
|
...(!app.isPackaged
|
||||||
|
? {
|
||||||
icon,
|
icon,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
show: false,
|
||||||
width: 1000,
|
width: 1000,
|
||||||
height: 700,
|
height: 700,
|
||||||
minWidth: 1000,
|
minWidth: 1000,
|
||||||
@ -85,12 +90,20 @@ app.on('window-all-closed', () => {
|
|||||||
mainWindow = null
|
mainWindow = null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 仅 macOS 有这个事件
|
||||||
app.on('activate', () => {
|
app.on('activate', () => {
|
||||||
// On OS X it's common to re-create a window in the app when the
|
|
||||||
// dock icon is clicked and there are no other windows open.
|
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
createWindow()
|
createWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (app.isHidden()) {
|
||||||
|
app.show()
|
||||||
|
app.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!app.dock.isVisible()) {
|
||||||
|
app.dock.show()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
|
BIN
electron/resources/extra/tray-raw.png
Normal file
After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 226 B After Width: | Height: | Size: 242 B |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 445 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1008 B |
15
src/App.vue
@ -25,6 +25,11 @@ export default {
|
|||||||
Preference,
|
Preference,
|
||||||
About,
|
About,
|
||||||
},
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
$app: this,
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tabsModel: [
|
tabsModel: [
|
||||||
@ -42,6 +47,7 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
activeTab: 'Device',
|
activeTab: 'Device',
|
||||||
|
renderTab: '',
|
||||||
rendered: true,
|
rendered: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -80,15 +86,20 @@ export default {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
isRender(item) {
|
isRender(item) {
|
||||||
if (this.activeTab === item.prop) {
|
if (this.renderTab === item.prop) {
|
||||||
return this.rendered
|
return this.rendered
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
async reRender() {
|
async reRender(other) {
|
||||||
|
this.renderTab = other || this.activeTab
|
||||||
|
|
||||||
this.rendered = false
|
this.rendered = false
|
||||||
await this.$nextTick()
|
await this.$nextTick()
|
||||||
this.rendered = true
|
this.rendered = true
|
||||||
|
|
||||||
|
this.renderTab = ''
|
||||||
},
|
},
|
||||||
async onTabChange(prop) {
|
async onTabChange(prop) {
|
||||||
switch (prop) {
|
switch (prop) {
|
||||||
|
@ -36,11 +36,11 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
:icon="loading ? '' : 'Refresh'"
|
:icon="loading ? '' : 'Refresh'"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="getDeviceData"
|
@click="handleRefresh"
|
||||||
>
|
>
|
||||||
刷新设备
|
刷新设备
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="warning" icon="RefreshRight" @click="handleReset">
|
<el-button type="warning" icon="RefreshRight" @click="handleRestart">
|
||||||
重启服务
|
重启服务
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@ -172,6 +172,7 @@ export default {
|
|||||||
ControlBar,
|
ControlBar,
|
||||||
Remark,
|
Remark,
|
||||||
},
|
},
|
||||||
|
inject: ['$app'],
|
||||||
data() {
|
data() {
|
||||||
const adbCache = storage.get('adbCache') || {}
|
const adbCache = storage.get('adbCache') || {}
|
||||||
return {
|
return {
|
||||||
@ -216,6 +217,35 @@ export default {
|
|||||||
scrcpyArgs(...args) {
|
scrcpyArgs(...args) {
|
||||||
return this.$store.scrcpy.getStringConfig(...args)
|
return this.$store.scrcpy.getStringConfig(...args)
|
||||||
},
|
},
|
||||||
|
handleRefresh() {
|
||||||
|
this.getDeviceData({ resetResolve: true })
|
||||||
|
},
|
||||||
|
async handleReset(depType = 'scrcpy') {
|
||||||
|
try {
|
||||||
|
await this.$confirm(
|
||||||
|
`
|
||||||
|
<div>通常情况下,这可能是因为更新 Escrcpy 后,缓存的依赖配置不兼容所导致的,是否重置依赖配置?</div>
|
||||||
|
<div class="text-red-500">注意:重置后,之前保存的依赖配置将会被清除,因此建议在执行重置操作之前备份您的配置。</div>
|
||||||
|
`,
|
||||||
|
'操作失败',
|
||||||
|
{
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
confirmButtonText: '重置依赖配置',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
closeOnClickModal: false,
|
||||||
|
type: 'warning',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
this.$store.scrcpy.resetDeps(depType)
|
||||||
|
this.$app.reRender('Preference')
|
||||||
|
this.$message.success('操作成功,请重新尝试。')
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (error.message) {
|
||||||
|
console.warn(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
onStdout() {},
|
onStdout() {},
|
||||||
toggleRowExpansion(...params) {
|
toggleRowExpansion(...params) {
|
||||||
this.$refs.elTable.toggleRowExpansion(...params)
|
this.$refs.elTable.toggleRowExpansion(...params)
|
||||||
@ -269,6 +299,8 @@ export default {
|
|||||||
if (error.message) {
|
if (error.message) {
|
||||||
this.$message.warning(error.message)
|
this.$message.warning(error.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.handleReset()
|
||||||
}
|
}
|
||||||
row.$recordLoading = false
|
row.$recordLoading = false
|
||||||
},
|
},
|
||||||
@ -289,6 +321,7 @@ export default {
|
|||||||
if (error.message) {
|
if (error.message) {
|
||||||
this.$message.warning(error.message)
|
this.$message.warning(error.message)
|
||||||
}
|
}
|
||||||
|
this.handleReset()
|
||||||
}
|
}
|
||||||
row.$loading = false
|
row.$loading = false
|
||||||
},
|
},
|
||||||
@ -310,7 +343,7 @@ export default {
|
|||||||
onPairSuccess() {
|
onPairSuccess() {
|
||||||
this.handleConnect()
|
this.handleConnect()
|
||||||
},
|
},
|
||||||
handleReset() {
|
handleRestart() {
|
||||||
this.$electron.ipcRenderer.send('restart-app')
|
this.$electron.ipcRenderer.send('restart-app')
|
||||||
},
|
},
|
||||||
async handleConnect() {
|
async handleConnect() {
|
||||||
@ -326,7 +359,7 @@ export default {
|
|||||||
storage.set('adbCache', this.formData)
|
storage.set('adbCache', this.formData)
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
this.handleError(error.message)
|
this.handleError(error?.message || error?.cause?.message || error)
|
||||||
}
|
}
|
||||||
this.connectLoading = false
|
this.connectLoading = false
|
||||||
},
|
},
|
||||||
@ -339,7 +372,8 @@ export default {
|
|||||||
<div>1. IP地址或端口号错误</div>
|
<div>1. IP地址或端口号错误</div>
|
||||||
<div>2. 设备未与当前电脑配对成功</div>
|
<div>2. 设备未与当前电脑配对成功</div>
|
||||||
<div>3. 电脑网络和提供的设备网络IP不在同一个局域网中</div>
|
<div>3. 电脑网络和提供的设备网络IP不在同一个局域网中</div>
|
||||||
<div>4. 其他未知错误</div>
|
<div>4. adb 依赖路径错误</div>
|
||||||
|
<div>5. 其他未知错误</div>
|
||||||
`,
|
`,
|
||||||
'连接设备失败',
|
'连接设备失败',
|
||||||
{
|
{
|
||||||
@ -371,7 +405,7 @@ export default {
|
|||||||
row.$stopLoading = false
|
row.$stopLoading = false
|
||||||
},
|
},
|
||||||
|
|
||||||
async getDeviceData() {
|
async getDeviceData({ resetResolve = false } = {}) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
await sleep(500)
|
await sleep(500)
|
||||||
try {
|
try {
|
||||||
@ -392,6 +426,10 @@ export default {
|
|||||||
this.$message.warning(error?.message || error?.cause?.message)
|
this.$message.warning(error?.message || error?.cause?.message)
|
||||||
}
|
}
|
||||||
this.deviceList = []
|
this.deviceList = []
|
||||||
|
|
||||||
|
if (resetResolve) {
|
||||||
|
this.handleReset('adb')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.loadingText = '正在获取设备列表...'
|
this.loadingText = '正在获取设备列表...'
|
||||||
|
@ -191,6 +191,7 @@ import { useScrcpyStore } from '@/store/index.js'
|
|||||||
import LoadingIcon from '@/components/Device/ControlBar/LoadingIcon/index.vue'
|
import LoadingIcon from '@/components/Device/ControlBar/LoadingIcon/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
inject: ['$app'],
|
||||||
data() {
|
data() {
|
||||||
const scrcpyStore = useScrcpyStore()
|
const scrcpyStore = useScrcpyStore()
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ import { replaceIP } from '@/utils/index.js'
|
|||||||
|
|
||||||
const $appStore = window.appStore
|
const $appStore = window.appStore
|
||||||
|
|
||||||
|
const { adbPath, scrcpyPath } = window.electron?.configs || {}
|
||||||
|
|
||||||
function mergeConfig(object, sources, { debug = false } = {}) {
|
function mergeConfig(object, sources, { debug = false } = {}) {
|
||||||
const customizer = (objValue, srcValue) => {
|
const customizer = (objValue, srcValue) => {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
@ -89,6 +91,21 @@ export const useScrcpyStore = defineStore({
|
|||||||
|
|
||||||
this.init()
|
this.init()
|
||||||
},
|
},
|
||||||
|
resetDeps(type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'adb':
|
||||||
|
$appStore.set('scrcpy.global.adbPath', '')
|
||||||
|
break
|
||||||
|
case 'scrcpy':
|
||||||
|
$appStore.set('scrcpy.global.scrcpyPath', '')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
$appStore.set('scrcpy.global.adbPath', '')
|
||||||
|
$appStore.set('scrcpy.global.scrcpyPath', '')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
setScope(value) {
|
setScope(value) {
|
||||||
this.scope = replaceIP(value)
|
this.scope = replaceIP(value)
|
||||||
$appStore.set('scrcpy.scope', this.scope)
|
$appStore.set('scrcpy.scope', this.scope)
|
||||||
@ -127,7 +144,20 @@ export const useScrcpyStore = defineStore({
|
|||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
setConfig(data, scope = this.scope) {
|
setConfig(data, scope = this.scope) {
|
||||||
$appStore.set(`scrcpy.${replaceIP(scope)}`, { ...data })
|
const cloneData = cloneDeep(data)
|
||||||
|
|
||||||
|
// console.log('adbPath', adbPath)
|
||||||
|
// console.log('scrcpyPath', scrcpyPath)
|
||||||
|
|
||||||
|
if (data.adbPath === adbPath) {
|
||||||
|
delete cloneData.adbPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.scrcpyPath === scrcpyPath) {
|
||||||
|
delete cloneData.scrcpyPath
|
||||||
|
}
|
||||||
|
|
||||||
|
$appStore.set(`scrcpy.${replaceIP(scope)}`, cloneData)
|
||||||
|
|
||||||
this.init(scope)
|
this.init(scope)
|
||||||
},
|
},
|
||||||
|
@ -17,9 +17,9 @@ export default () => {
|
|||||||
type: 'input.path',
|
type: 'input.path',
|
||||||
value: adbPath,
|
value: adbPath,
|
||||||
tips: '用于连接设备的 adb 的地址,注意:该选项不受针对于单个设备配置的影响',
|
tips: '用于连接设备的 adb 的地址,注意:该选项不受针对于单个设备配置的影响',
|
||||||
placeholder: '请选择 adb',
|
placeholder: '请设置 adb 路径',
|
||||||
properties: ['openFile'],
|
properties: ['openFile'],
|
||||||
filters: [{ name: '请选择 adb', extensions: ['*'] }],
|
filters: [{ name: '请设置 adb 路径', extensions: ['*'] }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'scrcpy 路径',
|
label: 'scrcpy 路径',
|
||||||
@ -27,9 +27,9 @@ export default () => {
|
|||||||
type: 'input.path',
|
type: 'input.path',
|
||||||
value: scrcpyPath,
|
value: scrcpyPath,
|
||||||
tips: '用于控制设备的 scrcpy 的地址,注意:该选项不受针对于单个设备配置的影响',
|
tips: '用于控制设备的 scrcpy 的地址,注意:该选项不受针对于单个设备配置的影响',
|
||||||
placeholder: '请选择 scrcpy',
|
placeholder: '请设置 scrcpy 路径',
|
||||||
properties: ['openFile'],
|
properties: ['openFile'],
|
||||||
filters: [{ name: '请选择 scrcpy', extensions: ['*'] }],
|
filters: [{ name: '请设置 scrcpy 路径', extensions: ['*'] }],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|