diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json index 74a9e1d..385207c 100644 --- a/.eslintrc-auto-import.json +++ b/.eslintrc-auto-import.json @@ -79,6 +79,7 @@ "watch": true, "watchEffect": true, "watchPostEffect": true, - "watchSyncEffect": true + "watchSyncEffect": true, + "ElMessage": true } } diff --git a/.vscode/settings.json b/.vscode/settings.json index ffb9835..6c650fc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,7 +30,7 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "i18n-ally.localesPaths": ["src/locales/index.js", "src/locales/languages"], - "i18n-ally.sourceLanguage": "en", + "i18n-ally.sourceLanguage": "zh-CN", "i18n-ally.keystyle": "nested", "i18n-ally.extract.ignored": [ "Switch", diff --git a/README-CN.md b/README-CN.md index 84fcafa..07802d6 100644 --- a/README-CN.md +++ b/README-CN.md @@ -76,7 +76,10 @@ Windows 及 Linux 端内部集成了 Gnirehtet, 用于提供 PC 到安卓设 ### 批量处理 +- 批量截取屏幕 - 批量安装应用 +- 批量文件管理 +- 批量执行脚本 ### 控制模式 @@ -84,6 +87,7 @@ Windows 及 Linux 端内部集成了 Gnirehtet, 用于提供 PC 到安卓设 - 录制 - OTG - 摄像 +- 灵活启动 ### 设备交互栏 @@ -98,6 +102,7 @@ Windows 及 Linux 端内部集成了 Gnirehtet, 用于提供 PC 到安卓设 - 重启设备 - 安装应用 - 文件管理 +- 执行脚本 - 反向供网(Gnirehtet) - 多屏协同 @@ -203,8 +208,8 @@ Windows 及 Linux 端内部集成了 Gnirehtet, 用于提供 PC 到安卓设 15. 支持批量连接历史设备功能 ✅ 16. 支持使用内置终端执行自定义命令 ✅ 17. 支持设备自动执行镜像 ✅ -18. 添加批量安装应用功能 ✅ -19. 支持更多批量处理功能 🚧 +18. 支持灵活启动镜像 ✅ +19. 支持常用批量功能 ✅ 20. 支持对设备进行分组 🚧 21. 添加文件传输助手功能 🚧 22. 支持通过界面从设备下载选中的文件 🚧 diff --git a/README.md b/README.md index 518fe32..ff7a4d2 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,10 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master ### Batch Processing -- Batch installation application +- Batch Interception Screen +- Batch Installation Application +- Batch File Management +- Batch Execution Script ### Control Model @@ -82,6 +85,7 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master - Recording - OTG - Camera +- Custom ### Device Interaction Bar @@ -96,6 +100,7 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master - Reboot - Install APP - File Manager +- Execution Script - Gnirehtet - Mirror Group @@ -200,9 +205,9 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master 14. Add more features to device interaction bar: file push, screen rotation, audio control etc ✅ 15. Support bulk connecting to historical devices ✅ 16. Support to use built-in terminals to execute custom commands ✅ -17. Supports automatic execution of mirror on devices ✅ -18. Add batch installation application function ✅ -19. Support more batch processing functions 🚧 +17. Support automatic execution of mirror on devices ✅ +18. Support for custom startup mirroring ✅ +19. Support common batch processing function ✅ 20. Support the device to group 🚧 21. Add file transmission assistant function 🚧 22. Support GUI-based selective file downloads from devices 🚧 diff --git a/auto-imports.d.ts b/auto-imports.d.ts index 0ce0ca8..9c51698 100644 --- a/auto-imports.d.ts +++ b/auto-imports.d.ts @@ -6,6 +6,7 @@ export {} declare global { const EffectScope: typeof import('vue')['EffectScope'] + const ElMessage: typeof import('element-plus/es')['ElMessage'] const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] const computed: typeof import('vue')['computed'] const createApp: typeof import('vue')['createApp'] diff --git a/components.d.ts b/components.d.ts index 20b6b0f..ab50cdb 100644 --- a/components.d.ts +++ b/components.d.ts @@ -7,15 +7,6 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { - About: typeof import('./src/components/About/index.vue')['default'] - AppInstall: typeof import('./src/components/Device/components/BatchActions/AppInstall/index.vue')['default'] - AppSearch: typeof import('./src/components/AppSearch/index.vue')['default'] - AudioCodecSelect: typeof import('./src/components/Preference/components/AudioCodecSelect/index.vue')['default'] - BatchActions: typeof import('./src/components/Device/components/BatchActions/index.vue')['default'] - Camera: typeof import('./src/components/Device/components/MoreDropdown/components/Camera/index.vue')['default'] - ControlBar: typeof import('./src/components/Device/components/ControlBar/index.vue')['default'] - Device: typeof import('./src/components/Device/index.vue')['default'] - DisplaySelect: typeof import('./src/components/Preference/components/DisplaySelect/index.vue')['default'] ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete'] ElButton: typeof import('element-plus/es')['ElButton'] ElCol: typeof import('element-plus/es')['ElCol'] @@ -43,28 +34,6 @@ declare module 'vue' { ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] - FileManage: typeof import('./src/components/Device/components/ControlBar/FileManage/index.vue')['default'] - Gnirehtet: typeof import('./src/components/Device/components/ControlBar/Gnirehtet/index.vue')['default'] - KeyboardInjectSelect: typeof import('./src/components/Preference/components/KeyboardInjectSelect/index.vue')['default'] - LanguageSelect: typeof import('./src/components/Preference/components/LanguageSelect/index.vue')['default'] - LoadingIcon: typeof import('./src/components/Device/components/LoadingIcon/index.vue')['default'] - MirrorAction: typeof import('./src/components/Device/components/MirrorAction/index.vue')['default'] - MirrorGroup: typeof import('./src/components/Device/components/ControlBar/MirrorGroup/index.vue')['default'] - MoreDropdown: typeof import('./src/components/Device/components/MoreDropdown/index.vue')['default'] - Otg: typeof import('./src/components/Device/components/MoreDropdown/components/Otg/index.vue')['default'] - PairDialog: typeof import('./src/components/Device/components/Wireless/PairDialog/index.vue')['default'] - PathInput: typeof import('./src/components/Preference/components/PathInput/index.vue')['default'] - Preference: typeof import('./src/components/Preference/index.vue')['default'] - Record: typeof import('./src/components/Device/components/MoreDropdown/components/Record/index.vue')['default'] - Remark: typeof import('./src/components/Device/components/Remark/index.vue')['default'] - Rotation: typeof import('./src/components/Device/components/ControlBar/Rotation/index.vue')['default'] - Screenshot: typeof import('./src/components/Device/components/ControlBar/Screenshot/index.vue')['default'] - TerminalAction: typeof import('./src/components/Device/components/TerminalAction/index.vue')['default'] - TerminalDialog: typeof import('./src/components/Device/components/TerminalAction/components/TerminalDialog/index.vue')['default'] - VideoCodecSelect: typeof import('./src/components/Preference/components/VideoCodecSelect/index.vue')['default'] - Volume: typeof import('./src/components/Device/components/ControlBar/Volume/index.vue')['default'] - Wireless: typeof import('./src/components/Device/components/Wireless/index.vue')['default'] - WirelessAction: typeof import('./src/components/Device/components/WirelessAction/index.vue')['default'] } export interface ComponentCustomProperties { vLoading: typeof import('element-plus/es')['ElLoadingDirective'] diff --git a/electron/exposes/adbkit/index.js b/electron/exposes/adbkit/index.js index ec5471c..6d7ced3 100644 --- a/electron/exposes/adbkit/index.js +++ b/electron/exposes/adbkit/index.js @@ -198,8 +198,8 @@ const push = async ( progress?.(stats) }) - res.on('end', (ret) => { - resolve(ret) + res.on('end', () => { + resolve(savePath) }) res.on('error', (err) => { diff --git a/electron/main.js b/electron/main.js index 76b0e9a..f037f6d 100644 --- a/electron/main.js +++ b/electron/main.js @@ -27,8 +27,6 @@ log.initialize({ preload: true }) const debug = !!appStore.get('common.debug') -log.info('Debug Status:', debug) - if (!debug) { log.warn( 'Debug Tips:', diff --git a/src/App.vue b/src/App.vue index 16fc657..746ac26 100644 --- a/src/App.vue +++ b/src/App.vue @@ -33,7 +33,7 @@ import { ElMessageBox } from 'element-plus' import Device from './components/Device/index.vue' import Preference from './components/Preference/index.vue' import About from './components/About/index.vue' -import AppSearch from './components/AppSearch/index.vue' +import AppSearch from './components/Search/index.vue' import { useThemeStore } from '$/store/theme/index.js' import { usePreferenceStore } from '$/store/preference/index.js' diff --git a/src/components/Device/components/BatchActions/AppInstall/index.vue b/src/components/Device/components/BatchActions/AppInstall/index.vue deleted file mode 100644 index 7aeef33..0000000 --- a/src/components/Device/components/BatchActions/AppInstall/index.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - - - diff --git a/src/components/Device/components/BatchActions/Application/index.vue b/src/components/Device/components/BatchActions/Application/index.vue new file mode 100644 index 0000000..6453d4f --- /dev/null +++ b/src/components/Device/components/BatchActions/Application/index.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/components/Device/components/BatchActions/FileManage/index.vue b/src/components/Device/components/BatchActions/FileManage/index.vue new file mode 100644 index 0000000..eaaa4fc --- /dev/null +++ b/src/components/Device/components/BatchActions/FileManage/index.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/src/components/Device/components/BatchActions/Screenshot/index.vue b/src/components/Device/components/BatchActions/Screenshot/index.vue new file mode 100644 index 0000000..353f2d5 --- /dev/null +++ b/src/components/Device/components/BatchActions/Screenshot/index.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/components/Device/components/BatchActions/Shell/index.vue b/src/components/Device/components/BatchActions/Shell/index.vue new file mode 100644 index 0000000..39de422 --- /dev/null +++ b/src/components/Device/components/BatchActions/Shell/index.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/src/components/Device/components/BatchActions/index.vue b/src/components/Device/components/BatchActions/index.vue index 6671654..566b065 100644 --- a/src/components/Device/components/BatchActions/index.vue +++ b/src/components/Device/components/BatchActions/index.vue @@ -1,5 +1,5 @@ - diff --git a/src/components/Device/components/ControlBar/AppInstall/index.vue b/src/components/Device/components/ControlBar/Application/index.vue similarity index 57% rename from src/components/Device/components/ControlBar/AppInstall/index.vue rename to src/components/Device/components/ControlBar/Application/index.vue index b08b706..d7cb061 100644 --- a/src/components/Device/components/ControlBar/AppInstall/index.vue +++ b/src/components/Device/components/ControlBar/Application/index.vue @@ -5,7 +5,7 @@ diff --git a/src/components/Device/components/ControlBar/Gnirehtet/index.vue b/src/components/Device/components/ControlBar/Gnirehtet/index.vue index a3e2623..4881bd1 100644 --- a/src/components/Device/components/ControlBar/Gnirehtet/index.vue +++ b/src/components/Device/components/ControlBar/Gnirehtet/index.vue @@ -7,7 +7,7 @@ @@ -48,9 +48,7 @@ export default { try { await this.$gnirehtet.run(this.device.id) await sleep() - this.$message.success( - this.$t('device.control.gnirehtet.start.success'), - ) + this.$message.success(this.$t('device.control.gnirehtet.start.success')) } catch (error) { this.$message.warning(error.message || 'Start service failure') diff --git a/src/components/Device/components/ControlBar/Screenshot/index.vue b/src/components/Device/components/ControlBar/Screenshot/index.vue index 1353f20..09bcb72 100644 --- a/src/components/Device/components/ControlBar/Screenshot/index.vue +++ b/src/components/Device/components/ControlBar/Screenshot/index.vue @@ -1,12 +1,10 @@ + + diff --git a/src/components/Device/components/ControlBar/index.vue b/src/components/Device/components/ControlBar/index.vue index fd250ae..6fa4f62 100644 --- a/src/components/Device/components/ControlBar/index.vue +++ b/src/components/Device/components/ControlBar/index.vue @@ -66,22 +66,24 @@ + + diff --git a/src/components/Device/components/MoreDropdown/components/Custom/index.vue b/src/components/Device/components/MoreDropdown/components/Custom/index.vue new file mode 100644 index 0000000..a92bc14 --- /dev/null +++ b/src/components/Device/components/MoreDropdown/components/Custom/index.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/src/components/Device/components/MoreDropdown/components/Record/index.vue b/src/components/Device/components/MoreDropdown/components/Record/index.vue index 28ef644..7392554 100644 --- a/src/components/Device/components/MoreDropdown/components/Record/index.vue +++ b/src/components/Device/components/MoreDropdown/components/Record/index.vue @@ -51,7 +51,7 @@ export default { await recording - this.onRecordSuccess(savePath) + await this.handleSuccess(savePath) } catch (error) { console.error('record.args', args) @@ -79,24 +79,10 @@ export default { return value }, - async onRecordSuccess(savePath) { - try { - await this.$confirm( - this.$t('device.record.success.message'), - this.$t('device.record.success.title'), - { - confirmButtonText: this.$t('common.confirm'), - cancelButtonText: this.$t('common.cancel'), - closeOnClickModal: false, - type: 'success', - }, - ) - - await this.$electron.ipcRenderer.invoke('show-item-in-folder', savePath) - } - catch (error) { - console.warn(error) - } + async handleSuccess(savePath) { + return this.$message.success( + `${this.$t('device.record.success.title')}: ${savePath}`, + ) }, }, } diff --git a/src/components/Device/components/MoreDropdown/index.vue b/src/components/Device/components/MoreDropdown/index.vue index 9197deb..d5be13a 100644 --- a/src/components/Device/components/MoreDropdown/index.vue +++ b/src/components/Device/components/MoreDropdown/index.vue @@ -41,17 +41,20 @@ import Record from './components/Record/index.vue' import Otg from './components/Otg/index.vue' import Camera from './components/Camera/index.vue' +import Custom from './components/Custom/index.vue' export default { components: { Record, Otg, Camera, + Custom, }, props: { ...Record.props, ...Otg.props, ...Camera.props, + ...Custom.props, }, data() { return { @@ -68,6 +71,10 @@ export default { label: 'device.actions.more.camera.name', component: 'Camera', }, + { + label: 'device.actions.more.custom.name', + component: 'Custom', + }, ], } }, diff --git a/src/components/Device/components/TerminalAction/components/TerminalDialog/index.vue b/src/components/Device/components/TerminalAction/components/TerminalDialog/index.vue index 1254b3c..52b84f2 100644 --- a/src/components/Device/components/TerminalAction/components/TerminalDialog/index.vue +++ b/src/components/Device/components/TerminalAction/components/TerminalDialog/index.vue @@ -4,27 +4,26 @@ width="80%" :close-on-click-modal="false" :close-on-press-escape="true" + :destroy-on-close="true" class="overflow-hidden !rounded-md el-dialog-headless dark:border dark:border-gray-700" - @open="onOpen" + @closed="onClosed" > - @@ -37,108 +36,141 @@ - diff --git a/src/components/Preference/components/PreferenceForm/components/InputNumber/index.vue b/src/components/Preference/components/PreferenceForm/components/InputNumber/index.vue new file mode 100644 index 0000000..713d270 --- /dev/null +++ b/src/components/Preference/components/PreferenceForm/components/InputNumber/index.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/src/components/Preference/components/PathInput/index.vue b/src/components/Preference/components/PreferenceForm/components/InputPath/index.vue similarity index 92% rename from src/components/Preference/components/PathInput/index.vue rename to src/components/Preference/components/PreferenceForm/components/InputPath/index.vue index 1039224..abe3404 100644 --- a/src/components/Preference/components/PathInput/index.vue +++ b/src/components/Preference/components/PreferenceForm/components/InputPath/index.vue @@ -1,11 +1,11 @@ diff --git a/src/components/AppSearch/index.vue b/src/components/Search/index.vue similarity index 100% rename from src/components/AppSearch/index.vue rename to src/components/Search/index.vue diff --git a/src/locales/languages/en-US.json b/src/locales/languages/en-US.json index 3301185..38fa9bd 100644 --- a/src/locales/languages/en-US.json +++ b/src/locales/languages/en-US.json @@ -7,6 +7,7 @@ "common.open": "Open", "common.input.placeholder": "Please input", "common.success": "Operation successful", + "common.success.batch": "Batch operation success", "common.progress": "Starting", "common.loading": "Loading", "common.search": "Search", @@ -86,6 +87,7 @@ "device.actions.more.record.name": "Start Recording", "device.actions.more.otg.name": "Startup OTG", "device.actions.more.camera.name": "Startup Camera", + "device.actions.more.custom.name": "Custom Startup", "device.control.name": "Control", "device.control.more": "More Controls", @@ -98,9 +100,18 @@ "device.control.file.name": "File Manager", "device.control.file.push": "Push File", "device.control.file.push.placeholder": "Please select the file to push", + "device.control.file.push.loading": "Push file", + "device.control.file.push.success.name": "Push files successfully", "device.control.file.push.success": "Successfully pushed {totalCount} files to the /sdcard/Download/ directory of {deviceName}, {successCount} succeeded, and {failCount} failed", "device.control.file.push.success.single": "Files successfully pushed to the /sdcard/Download/ directory of {deviceName}", "device.control.file.push.error": "Failed to push the file, please check the file and try again", + "device.control.shell.name": "Execute Script", + "device.control.shell.tips": "Perform custom script through the ADB command", + "device.control.shell.select": "Please select the script you want to execute", + "device.control.shell.push.loading": "Push script", + "device.control.shell.push.success": "Push script success", + "device.control.shell.enter": "Please enter the Enter key to confirm the execution of the script", + "device.control.shell.success": "Script execution successfully", "device.control.capture": "Screenshot", "device.control.capture.progress": "Capturing screenshot for {deviceName}...", "device.control.capture.success.message": "Open screenshot location?", diff --git a/src/locales/languages/zh-CN.json b/src/locales/languages/zh-CN.json index 5058636..9db9a9b 100644 --- a/src/locales/languages/zh-CN.json +++ b/src/locales/languages/zh-CN.json @@ -7,6 +7,7 @@ "common.open": "打开", "common.input.placeholder": "请填写", "common.success": "操作成功", + "common.success.batch": "批量操作成功", "common.progress": "启动中", "common.loading": "加载中", "common.search": "搜索", @@ -86,6 +87,7 @@ "device.actions.more.record.name": "开始录制", "device.actions.more.otg.name": "启动OTG", "device.actions.more.camera.name": "启动摄像", + "device.actions.more.custom.name": "灵活启动", "device.control.name": "操作", "device.control.more": "设备交互", @@ -98,9 +100,18 @@ "device.control.file.name": "文件管理", "device.control.file.push": "推送文件", "device.control.file.push.placeholder": "请选择要推送的文件", + "device.control.file.push.loading": "推送文件中", + "device.control.file.push.success.name": "推送文件成功", "device.control.file.push.success": "已成功将 {totalCount} 个文件推送到 {deviceName} 的 /sdcard/Download/ 目录,{successCount} 成功,{failCount} 失败。", "device.control.file.push.success.single": "文件已成功推送到 {deviceName} 的 /sdcard/Download/ 目录", "device.control.file.push.error": "推送文件失败,请检查文件后重试", + "device.control.shell.name": "执行脚本", + "device.control.shell.tips": "通过 ADB 命令执行自定义脚本", + "device.control.shell.select": "请选择要执行的脚本", + "device.control.shell.push.loading": "推送脚本中", + "device.control.shell.push.success": "推送脚本成功", + "device.control.shell.enter": "请输入回车键确认执行该脚本", + "device.control.shell.success": "脚本执行成功", "device.control.capture": "截取屏幕", "device.control.capture.progress": "正在截取 {deviceName} 的屏幕快照...", "device.control.capture.success.message": "是否前往截屏位置进行查看?", diff --git a/src/locales/languages/zh-TW.json b/src/locales/languages/zh-TW.json index 6cb4f7d..72dced2 100644 --- a/src/locales/languages/zh-TW.json +++ b/src/locales/languages/zh-TW.json @@ -7,6 +7,7 @@ "common.open": "開啟", "common.input.placeholder": "請輸入", "common.success": "操作成功", + "common.success.batch": "批量操作成功", "common.progress": "啟動中", "common.loading": "載入中", "common.search": "搜尋", @@ -86,6 +87,7 @@ "device.actions.more.record.name": "開始錄製", "device.actions.more.otg.name": "啟動 OTG", "device.actions.more.camera.name": "啟動鏡頭", + "device.actions.more.custom.name": "靈活啟動", "device.control.name": "操作", "device.control.more": "裝置互動", @@ -98,9 +100,18 @@ "device.control.file.name": "檔案管理", "device.control.file.push": "推送檔案", "device.control.file.push.placeholder": "請選擇要推送的檔案", + "device.control.file.push.loading": "推送檔案中", + "device.control.file.push.success.name": "推送檔案成功", "device.control.file.push.success": "已成功將 {totalCount} 個檔案推送到 {deviceName} 的 /sdcard/Download/ 目錄,{successCount} 成功,{failCount} 失敗。", "device.control.file.push.success.single": "檔案已成功推送到 {deviceName} 的 /sdcard/Download/ 目錄", "device.control.file.push.error": "推送檔案失敗,請檢查檔案後重試", + "device.control.shell.name": "執行腳本", + "device.control.shell.tips": "透過 ADB 命令執行自訂腳本", + "device.control.shell.select": "請選擇要執行的腳本", + "device.control.shell.push.loading": "推送腳本中", + "device.control.shell.push.success": "推送腳本成功", + "device.control.shell.enter": "請輸入回車鍵確認執行該腳本", + "device.control.shell.success": "腳本執行成功", "device.control.capture": "擷取螢幕", "device.control.capture.progress": "正在擷取 {deviceName} 的螢幕快照...", "device.control.capture.success.message": "是否前往截圖位置進行檢視?", diff --git a/src/plugins/auto.js b/src/plugins/auto.js index 0e9c6c2..4e63297 100644 --- a/src/plugins/auto.js +++ b/src/plugins/auto.js @@ -16,6 +16,7 @@ export default () => { }), useAutoComponents({ resolvers, + dirs: 'none', }), ] } diff --git a/src/components/Device/components/LoadingIcon/index.vue b/src/plugins/element-plus/components/EleIconLoading/index.vue similarity index 84% rename from src/components/Device/components/LoadingIcon/index.vue rename to src/plugins/element-plus/components/EleIconLoading/index.vue index 074ba74..1388dfd 100644 --- a/src/components/Device/components/LoadingIcon/index.vue +++ b/src/plugins/element-plus/components/EleIconLoading/index.vue @@ -6,7 +6,7 @@ diff --git a/src/plugins/element-plus/index.js b/src/plugins/element-plus/index.js index 7874d74..d1ad307 100644 --- a/src/plugins/element-plus/index.js +++ b/src/plugins/element-plus/index.js @@ -1,19 +1,31 @@ -import * as ElementPlusIcons from '@element-plus/icons-vue' - -import { ElLoading, ElMessage, ElMessageBox } from 'element-plus' import 'element-plus/theme-chalk/el-loading.css' import 'element-plus/theme-chalk/el-message.css' import 'element-plus/theme-chalk/el-message-box.css' +import 'element-plus/theme-chalk/el-badge.css' import 'element-plus/theme-chalk/dark/css-vars.css' import './restyle.css' +import * as ElementPlusIcons from '@element-plus/icons-vue' + +import { ElLoading, ElMessage, ElMessageBox } from 'element-plus' + +import EleIconLoading from './components/EleIconLoading/index.vue' + export default { install(app) { for (const [key, component] of Object.entries(ElementPlusIcons)) { app.component(key, component) } + ElMessage.loading = (message, options = {}) => + ElMessage({ + duration: 0, + ...options, + message, + icon: EleIconLoading, + }) + app.use(ElMessage) app.use(ElMessageBox) app.use(ElLoading) diff --git a/src/plugins/element-plus/restyle.css b/src/plugins/element-plus/restyle.css index 9c5f54f..0ccfe4f 100644 --- a/src/plugins/element-plus/restyle.css +++ b/src/plugins/element-plus/restyle.css @@ -59,3 +59,16 @@ @apply !p-0; } } + +.el-dialog-beautify { + @apply !rounded-lg; + + .el-dialog__title { + @apply relative; + + &::before { + content: ''; + @apply absolute inset-x-0 bottom-0 h-2 bg-primary-500/30; + } + } +} diff --git a/src/store/preference/helpers/index.js b/src/store/preference/helpers/index.js index 3568847..3f7f2a1 100644 --- a/src/store/preference/helpers/index.js +++ b/src/store/preference/helpers/index.js @@ -30,12 +30,14 @@ export function getModelMap(data = model) { return value } -export function getDefaultData(parentId) { +export function getDefaultData(parentId, iteratee) { const modelMap = getModelMap() + iteratee = iteratee ?? (value => value) + const value = Object.entries(modelMap).reduce((obj, [key, data]) => { if (!parentId || data.parentId === parentId) { - obj[key] = data.value + obj[key] = iteratee(data.value) } return obj }, {}) diff --git a/src/store/preference/index.js b/src/store/preference/index.js index c7fcd73..5d4d8b3 100644 --- a/src/store/preference/index.js +++ b/src/store/preference/index.js @@ -54,15 +54,9 @@ export const usePreferenceStore = defineStore({ getters: {}, actions: { getDefaultData, + init(scope = this.deviceScope) { - let data = mergeConfig(getDefaultData(), getStoreData()) - - if (scope !== 'global') { - data = mergeConfig(data, getStoreData(replaceIP(scope))) - } - - this.data = data - + this.data = this.getData(scope) return this.data }, setScope(value) { @@ -127,7 +121,12 @@ export const usePreferenceStore = defineStore({ this.init() }, getData(scope = this.deviceScope) { - const value = this.init(scope) + let value = mergeConfig(getDefaultData(), getStoreData()) + + if (scope !== 'global') { + value = mergeConfig(value, getStoreData(replaceIP(scope))) + } + return value }, @@ -135,7 +134,7 @@ export const usePreferenceStore = defineStore({ scope = this.deviceScope, { isRecord = false, isCamera = false, isOtg = false, excludes = [] } = {}, ) { - const data = this.getData(scope) + const data = typeof scope === 'object' ? scope : this.getData(scope) if (!data) { return '' @@ -182,12 +181,14 @@ export const usePreferenceStore = defineStore({ return arr }, []) - if (this.data.scrcpyAppend) { - valueList.push(...this.data.scrcpyAppend.split(' ')) + if (data.scrcpyAppend) { + valueList.push(...data.scrcpyAppend.split(' ')) } const value = valueList.join(' ') + // console.log('value', value) + return value }, getModel(path) { diff --git a/src/store/preference/model/audio/index.js b/src/store/preference/model/audio/index.js index d853bb1..8ea85c1 100644 --- a/src/store/preference/model/audio/index.js +++ b/src/store/preference/model/audio/index.js @@ -66,7 +66,7 @@ export default { audioBuffer: { label: 'preferences.audio.audio-buffer.name', field: '--audio-buffer', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.audio.audio-buffer.placeholder', append: 'ms', @@ -74,7 +74,7 @@ export default { audioOutputBuffer: { label: 'preferences.audio.audio-output-buffer.name', field: '--audio-output-buffer', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.audio.audio-output-buffer.placeholder', append: 'ms', diff --git a/src/store/preference/model/camera/index.js b/src/store/preference/model/camera/index.js index 247e536..090154b 100644 --- a/src/store/preference/model/camera/index.js +++ b/src/store/preference/model/camera/index.js @@ -34,7 +34,7 @@ export default { cameraFps: { label: 'preferences.camera.camera-fps.name', field: '--camera-fps', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.camera.camera-fps.placeholder', append: 'fps', diff --git a/src/store/preference/model/common/index.js b/src/store/preference/model/common/index.js index 15426fc..b299b7c 100644 --- a/src/store/preference/model/common/index.js +++ b/src/store/preference/model/common/index.js @@ -28,6 +28,9 @@ export default { value: 'system', }, ], + props: { + clearable: false, + }, }, language: { label: 'common.language.name', diff --git a/src/store/preference/model/record/index.js b/src/store/preference/model/record/index.js index 4a4568d..441471b 100644 --- a/src/store/preference/model/record/index.js +++ b/src/store/preference/model/record/index.js @@ -35,7 +35,7 @@ export default { timeLimit: { label: 'preferences.record.time-limit.name', field: '--time-limit', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.record.time-limit.placeholder', append: 's', diff --git a/src/store/preference/model/video/index.js b/src/store/preference/model/video/index.js index dbc91ed..c471c92 100644 --- a/src/store/preference/model/video/index.js +++ b/src/store/preference/model/video/index.js @@ -31,7 +31,7 @@ export default { maxSize: { label: 'preferences.video.resolution.name', field: '--max-size', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.video.resolution.placeholder', }, @@ -46,7 +46,7 @@ export default { maxFps: { label: 'preferences.video.refresh-rate.name', field: '--max-fps', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.video.refresh-rate.placeholder', append: 'fps', @@ -133,7 +133,7 @@ export default { displayBuffer: { label: 'preferences.video.video-buffer.name', field: '--display-buffer', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.video.video-buffer.placeholder', append: 'ms', @@ -141,7 +141,7 @@ export default { v4l2Buffer: { label: 'preferences.video.receiver-buffer.name', field: '--v4l2-buffer', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.video.receiver-buffer.placeholder', append: 'ms', diff --git a/src/store/preference/model/window/index.js b/src/store/preference/model/window/index.js index ef8a705..6e1c7f8 100644 --- a/src/store/preference/model/window/index.js +++ b/src/store/preference/model/window/index.js @@ -6,7 +6,7 @@ export default { windowWidth: { label: 'preferences.window.size.width', field: '--window-width', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.window.size.width.placeholder', tips: 'preferences.window.size.width.tips', @@ -14,7 +14,7 @@ export default { windowHeight: { label: 'preferences.window.size.height', field: '--window-height', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.window.size.height.placeholder', tips: 'preferences.window.size.height.tips', @@ -22,14 +22,14 @@ export default { windowX: { label: 'preferences.window.position.x', field: '--window-x', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.window.position.x.placeholder', }, windowY: { label: 'preferences.window.position.y', field: '--window-y', - type: 'Input.number', + type: 'InputNumber', value: undefined, placeholder: 'preferences.window.position.y.placeholder', }, diff --git a/src/styles/css/desktop.css b/src/styles/css/desktop.css index 0ef191f..4b44d67 100644 --- a/src/styles/css/desktop.css +++ b/src/styles/css/desktop.css @@ -7,6 +7,7 @@ html { @screen sm { ::-webkit-scrollbar { width: 8px; + height: 8px; } ::-webkit-scrollbar-track { diff --git a/src/utils/device/index.js b/src/utils/device/index.js new file mode 100644 index 0000000..de48390 --- /dev/null +++ b/src/utils/device/index.js @@ -0,0 +1,66 @@ +import { ElMessage } from 'element-plus' +import { allSettled } from '$/utils' +/** + * 选择并将文件发送到设备 + */ +export async function selectAndSendFileToDevice( + deviceId, + { + files, + multiSelections = false, + extensions = ['*'], + selectText = window.t('device.control.file.push.placeholder'), + loadingText = window.t('device.control.file.push.loading'), + successText = window.t('device.control.file.push.success.name'), + } = {}, +) { + if (!files) { + try { + const properties = ['openFile'] + + if (multiSelections) { + properties.push('multiSelections') + } + + files = await window.electron.ipcRenderer.invoke('show-open-dialog', { + properties, + filters: [ + { + name: selectText, + extensions, + }, + ], + }) + } + catch (error) { + throw new Error(error.message?.match(/Error: (.*)/)?.[1] || error.message) + } + } + + const closeMessage = ElMessage.loading(loadingText).close + + const successFiles = [] + const failFiles = [] + + await allSettled(files, async (item) => { + const ret = await window.adbkit.push(deviceId, item).catch((e) => { + console.warn(e?.message) + failFiles.push(`${deviceId}-${item}`) + }) + + if (ret) { + successFiles.push(ret) + } + }) + + if (failFiles.length) { + closeMessage() + throw new Error(`Push file failed: ${failFiles.join(',')}`) + } + + closeMessage() + + ElMessage.success({ message: successText, grouping: true }) + + return successFiles +} diff --git a/src/utils/index.js b/src/utils/index.js index 90c6352..163a721 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -52,3 +52,40 @@ export function keyByValue(data, key = 'key', valueKey = 'value') { return value } + +/** + * 对列表中的每个项目执行给定的迭代器函数,并返回一个 Promise, + * 该 Promise 在所有迭代完成时解决,无论它们是成功还是失败。 + * + * @param {Array} list - 要迭代的项目数组。 + * @param {Function} iterator - 对列表中每个项目执行的函数。 + * 它应该返回一个 Promise 或者可以是一个异步函数。 + * @param {*} iterator.item - 当前正在处理的列表项。 + * @param {number} iterator.index - 当前正在处理的项目的索引。 + * @param {Array} iterator.array - 正在处理的原始数组。 + * @returns {Promise>} 一个 Promise,解析为一个对象数组, + * 描述输入数组中每个 promise 的结果。 + * @throws {TypeError} 如果第一个参数不是数组或第二个参数不是函数。 + * + * @example + * const list = [1, 2, 3, 4, 5]; + * const iterator = async (item) => { + * if (item % 2 === 0) { + * return item * 2; + * } else { + * throw new Error('奇数'); + * } + * }; + * allSettled(list, iterator).then(console.log); + */ +export function allSettled(list = [], iterator) { + const promises = [] + + for (let index = 0; index < list.length; index++) { + const item = list[index] + + promises.push(iterator(item)) + } + + return Promise.allSettled(promises) +}