diff --git a/.eslintrc.js b/.eslintrc.js index 4677461..7c54cd8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,7 +4,11 @@ module.exports = { 'no-unused-vars': 'off', 'eqeqeq': 'off', 'prefer-promise-reject-errors': 'off', + 'antfu/top-level-function': 'off', + 'import/default': 'off', + + 'vue/no-mutating-props': 'off', }, } diff --git a/README.md b/README.md index 0732467..31232f1 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ - Adb 路径 - Scrcpy 路径 -- 文件存储路径(音视频及截图保存在这里) +- 文件存储路径(音视频录制及设备截图都保存在这里) ### 视频控制 @@ -98,7 +98,6 @@ ### 音视频录制 -- 文件保存路径 - 录制视频格式 ### 音频控制 diff --git a/electron/events/handles/index.js b/electron/events/handles/index.js index c36b65b..08547dc 100644 --- a/electron/events/handles/index.js +++ b/electron/events/handles/index.js @@ -6,72 +6,58 @@ export default () => { 'show-open-dialog', async (event, { preset = '', ...options } = {}) => { // console.log('options', options) - try { - const res = await dialog.showOpenDialog(options) - // console.log('showOpenDialog.res', res) - if (res.canceled) { - return false - } - const filePaths = res.filePaths + const res = await dialog + .showOpenDialog(options) + .catch(e => console.warn(e)) - switch (preset) { - case 'replaceFile': - await fs.copy(filePaths[0], options.filePath, { overwrite: true }) - break - } + if (res.canceled) { + throw new Error('用户取消操作') + } - return filePaths + if (!res.filePaths.length) { + throw new Error('获取目录或文件路径失败') } - catch (error) { - console.warn(error?.message || error) - return false + + const filePaths = res.filePaths + + switch (preset) { + case 'replaceFile': + await fs.copy(filePaths[0], options.filePath, { overwrite: true }) + break } + + return filePaths }, ) ipcMain.handle('open-path', async (event, pathValue) => { - try { - await shell.openPath(pathValue) - return true - } - catch (error) { - console.warn(error?.message || error) - return false - } + return shell.openPath(pathValue) }) ipcMain.handle('show-item-in-folder', async (event, filePath) => { - try { - await shell.showItemInFolder(filePath) - return true - } - catch (error) { - console.warn(error?.message || error) - return false - } + return shell.showItemInFolder(filePath) }) ipcMain.handle( 'show-save-dialog', async (event, { filePath = '', ...options } = {}) => { - try { - const result = await dialog.showSaveDialog({ + const res = await dialog + .showSaveDialog({ ...options, }) - if (!result || result.canceled || !result.filePath) { - return false - } + .catch(e => console.warn(e)) - const destinationPath = result.filePath - - await fs.copy(filePath, destinationPath) - - return true + if (res.canceled) { + throw new Error('用户取消操作') } - catch (error) { - console.error(error?.message || error) - return false + + if (!res.filePath) { + throw new Error('获取文件路径失败') } + + const destinationPath = res.filePath + + await fs.copy(filePath, destinationPath) }, ) } diff --git a/electron/exposes/adbkit/index.js b/electron/exposes/adbkit/index.js index 680afae..78e1a84 100644 --- a/electron/exposes/adbkit/index.js +++ b/electron/exposes/adbkit/index.js @@ -4,10 +4,9 @@ import path from 'node:path' import fs from 'node:fs' import dayjs from 'dayjs' import { Adb } from '@devicefarmer/adbkit' +import appStore from '@electron/helpers/store.js' import { adbPath } from '@electron/configs/index.js' -// console.log('adbPath', adbPath) - const exec = util.promisify(child_process.exec) let client = null @@ -18,6 +17,15 @@ window.addEventListener('beforeunload', () => { } }) +appStore.onDidChange('scrcpy.adbPath', async (value) => { + console.log('onDidChange.scrcpy.adbPath.value', value) + if (client) { + await client.kill().catch(e => console.warn(e)) + client = null + } + client = Adb.createClient({ bin: value }) +}) + const shell = async command => exec(`${adbPath} ${command}`) const getDevices = async () => client.listDevicesWithPaths() const deviceShell = async (id, command) => client.getDevice(id).shell(command) @@ -100,8 +108,8 @@ const watch = async (callback) => { } export default () => { - client = Adb.createClient({ bin: adbPath }) - // console.log('client', client) + client = Adb.createClient({ bin: appStore.get('scrcpy.adbPath') || adbPath }) + console.log('client', client) return { shell, diff --git a/electron/exposes/index.js b/electron/exposes/index.js index 869c5ca..fd91216 100644 --- a/electron/exposes/index.js +++ b/electron/exposes/index.js @@ -1,7 +1,7 @@ import path from 'node:path' +import store from '@electron/helpers/store.js' import * as configs from '@electron/configs/index.js' -import store from './store/index.js' import electron from './electron/index.js' import adbkit from './adbkit/index.js' import scrcpy from './scrcpy/index.js' @@ -10,7 +10,7 @@ export default { init(expose) { expose('nodePath', path) - expose('appStore', store()) + expose('appStore', store) expose('electron', { ...electron(), diff --git a/electron/exposes/scrcpy/index.js b/electron/exposes/scrcpy/index.js index 0d430f4..4ca4670 100644 --- a/electron/exposes/scrcpy/index.js +++ b/electron/exposes/scrcpy/index.js @@ -1,12 +1,17 @@ import { spawn } from 'node:child_process' +import appStore from '@electron/helpers/store.js' import { adbPath, scrcpyPath } from '@electron/configs/index.js' const shell = async (command, { stdout, stderr } = {}) => { const args = command.split(' ') - const scrcpyProcess = spawn(scrcpyPath, args, { - env: { ...process.env, ADB: adbPath }, - shell: true, - }) + const scrcpyProcess = spawn( + appStore.get('scrcpy.scrcpyPath') || scrcpyPath, + args, + { + env: { ...process.env, ADB: adbPath }, + shell: true, + }, + ) scrcpyProcess.stdout.on('data', (data) => { const stringData = data.toString() diff --git a/electron/exposes/store/index.js b/electron/exposes/store/index.js deleted file mode 100644 index 7707d8c..0000000 --- a/electron/exposes/store/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import Store from 'electron-store' -import { createProxy } from '@electron/helpers/index' - -export default () => { - const appStore = new Store() - - appStore.onDidAnyChange(() => { - console.log('reload.appStore.onDidAnyChange', appStore.store) - }) - - return { - ...createProxy(appStore, [ - 'set', - 'get', - 'delete', - 'clear', - 'reset', - 'has', - 'onDidChange', - 'onDidAnyChange', - 'openInEditor', - ]), - ...appStore, - getAll: () => appStore.store, - setAll: value => (appStore.store = value), - } -} diff --git a/electron/helpers/store.js b/electron/helpers/store.js new file mode 100644 index 0000000..34a47d1 --- /dev/null +++ b/electron/helpers/store.js @@ -0,0 +1,25 @@ +import Store from 'electron-store' +import { createProxy } from './index.js' + +const appStore = new Store() + +appStore.onDidAnyChange(() => { + console.log('appStore.onDidAnyChange', appStore.store) +}) + +export default { + ...createProxy(appStore, [ + 'set', + 'get', + 'delete', + 'clear', + 'reset', + 'has', + 'onDidChange', + 'onDidAnyChange', + 'openInEditor', + ]), + ...appStore, + getAll: () => appStore.store, + setAll: value => (appStore.store = value), +} diff --git a/electron/main.js b/electron/main.js index c416d48..74e8dbe 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1,17 +1,15 @@ import path from 'node:path' import { BrowserWindow, app, shell } from 'electron' -import Store from 'electron-store' import { electronApp, optimizer } from '@electron-toolkit/utils' // packaged.js 必须位于非依赖项的顶部 import './helpers/packaged.js' +import './helpers/store.js' import { icnsLogoPath, icoLogoPath, logoPath } from './configs/index.js' import events from './events/index.js' -const appStore = new Store() - // The built directory structure // // ├─┬─┬ dist diff --git a/src/App.vue b/src/App.vue index 661bae9..5c0f2d7 100644 --- a/src/App.vue +++ b/src/App.vue @@ -8,7 +8,7 @@ :name="item.prop" lazy > - + @@ -42,13 +42,31 @@ export default { }, ], activeTab: 'Device', + rendered: true, } }, created() { this.$store.scrcpy.init() }, methods: { - onTabChange(prop) {}, + isRender(item) { + if (this.activeTab === item.prop) { + return this.rendered + } + return true + }, + async reRender() { + this.rendered = false + await this.$nextTick() + this.rendered = true + }, + async onTabChange(prop) { + switch (prop) { + case 'Device': + this.reRender() + break + } + }, }, } diff --git a/src/components/Device/ControlBar/index.vue b/src/components/Device/ControlBar/index.vue index 9aacd04..34a135d 100644 --- a/src/components/Device/ControlBar/index.vue +++ b/src/components/Device/ControlBar/index.vue @@ -84,13 +84,19 @@ export default { }, methods: { async handleInstall(device) { - const files = await this.$electron.ipcRenderer.invoke( - 'show-open-dialog', - { + let files = null + + try { + files = await this.$electron.ipcRenderer.invoke('show-open-dialog', { properties: ['openFile', 'multiSelections'], filters: [{ name: '请选择要安装的应用', extensions: ['apk'] }], - }, - ) + }) + } + catch (error) { + if (error.message) { + this.$message.warning(error.message) + } + } if (!files) { return false @@ -174,7 +180,10 @@ export default { closeOnClickModal: false, type: 'success', }) - this.$electron.ipcRenderer.invoke('show-item-in-folder', savePath) + await this.$electron.ipcRenderer.invoke( + 'show-item-in-folder', + savePath, + ) } catch (error) { if (error.message) { diff --git a/src/components/Device/Remark/index.vue b/src/components/Device/Remark/index.vue new file mode 100644 index 0000000..ce6ab4f --- /dev/null +++ b/src/components/Device/Remark/index.vue @@ -0,0 +1,55 @@ + + + + + + + + + {{ device.$remark || "备注" }} + + + + + + + + + + + diff --git a/src/components/Device/index.vue b/src/components/Device/index.vue index 7a96a24..fe7bc71 100644 --- a/src/components/Device/index.vue +++ b/src/components/Device/index.vue @@ -63,6 +63,7 @@ label="设备 ID" show-overflow-tooltip align="left" + width="200" /> + WIFI + + - + { + this.unAdbWatch = await this.$adb.watch(async (type, ret) => { console.log('adb.watch.ret', ret) - this.getDeviceData() + if (ret && ret.id) { + this.getDeviceData() + } if (type === 'add' && !isIPWithPort(ret.id) && ret.$host) { this.formData = { @@ -207,6 +214,11 @@ export default { } }) }, + beforeUnmount() { + if (this.unAdbWatch) { + this.unAdbWatch() + } + }, methods: { onStdout() {}, toggleRowExpansion(...params) { @@ -242,7 +254,10 @@ export default { type: 'success', }) - this.$electron.ipcRenderer.invoke('show-item-in-folder', savePath) + await this.$electron.ipcRenderer.invoke( + 'show-item-in-folder', + savePath, + ) } catch (error) { if (error.message) { @@ -362,15 +377,16 @@ export default { $recordLoading: false, $stopLoading: false, $unauthorized: item.type === 'unauthorized', - $wireless: isIPWithPort(item.id), + $wifi: isIPWithPort(item.id), + $remark: this.$store.device.getRemark(item.id), })) || [] console.log('getDeviceData.data', this.deviceList) } catch (error) { - console.log(error) - if (error.message) { - this.$message.warning(error.message) + console.warn(error) + if (error?.message || error?.cause?.message) { + this.$message.warning(error?.message || error?.cause?.message) } this.deviceList = [] } diff --git a/src/components/Preference/index.vue b/src/components/Preference/index.vue index e76c1e1..01abee1 100644 --- a/src/components/Preference/index.vue +++ b/src/components/Preference/index.vue @@ -90,19 +90,24 @@ + > + + + + value.replaceAll('.', '_') + +export const useDeviceStore = defineStore({ + id: 'app-device', + state() { + return { + config: {}, + } + }, + getters: {}, + actions: { + removeDot, + init() { + this.config = { + ...($appStore.get('device') || {}), + } + + return this.config + }, + setConfig(value, key = 'device') { + $appStore.set(key, value) + this.init() + }, + setRemark(deviceId, value) { + $appStore.set(`device.${removeDot(deviceId)}.remark`, value) + this.init() + }, + getRemark(deviceId) { + const value = $appStore.get(`device.${removeDot(deviceId)}.remark`) + return value + }, + }, +}) diff --git a/src/store/index.js b/src/store/index.js index a085abc..067b3a3 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,7 +1,8 @@ import { createPinia } from 'pinia' import { useScrcpyStore } from './scrcpy/index.js' +import { useDeviceStore } from './device/index.js' -export { useScrcpyStore } +export { useScrcpyStore, useDeviceStore } export default { install(app) { @@ -10,6 +11,7 @@ export default { app.use(store) app.config.globalProperties.$store = { scrcpy: useScrcpyStore(), + device: useDeviceStore(), } }, } diff --git a/src/store/scrcpy/index.js b/src/store/scrcpy/index.js index 83e247d..1e07e5e 100644 --- a/src/store/scrcpy/index.js +++ b/src/store/scrcpy/index.js @@ -64,7 +64,7 @@ export const useScrcpyStore = defineStore({ }, []) .join(' ') - console.log('stringifyConfig.value', value) + // console.log('stringifyConfig.value', value) return value }, @@ -79,10 +79,10 @@ export const useScrcpyStore = defineStore({ return this.config }, - updateConfig(data) { + setConfig(data) { const pickConfig = pickBy(data, value => !!value) - console.log('pickConfig', pickConfig) + // console.log('pickConfig', pickConfig) $appStore.set('scrcpy', pickConfig)