mirror of
https://gitee.com/viarotel-org/escrcpy.git
synced 2024-12-02 03:07:35 +08:00
feat: 🚀 新增支持导入及导出配置
This commit is contained in:
parent
7a59329ebb
commit
326a133460
10
README.md
10
README.md
@ -58,9 +58,15 @@
|
||||
- 截取屏幕
|
||||
- 安装应用
|
||||
|
||||
## 高级配置
|
||||
## 偏好设置
|
||||
|
||||
> 持续完善中 目前支持 Scrcpy 中以下常用配置
|
||||
> 持续完善中 目前支持以下常用配置
|
||||
|
||||
### 自定义
|
||||
|
||||
- Adb 路径
|
||||
- Scrcpy 路径
|
||||
- 文件存储路径(音视频及截图保存在这里)
|
||||
|
||||
### 视频控制
|
||||
|
||||
|
@ -1,21 +1,33 @@
|
||||
import fs from 'fs-extra'
|
||||
import { dialog, ipcMain, shell } from 'electron'
|
||||
|
||||
export default () => {
|
||||
ipcMain.handle('show-open-dialog', async (event, params) => {
|
||||
// console.log('params', params)
|
||||
try {
|
||||
const res = await dialog.showOpenDialog(params)
|
||||
// console.log('showOpenDialog.res', res)
|
||||
if (res.canceled) {
|
||||
ipcMain.handle(
|
||||
'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
|
||||
|
||||
switch (preset) {
|
||||
case 'replaceFile':
|
||||
await fs.copy(filePaths[0], options.filePath, { overwrite: true })
|
||||
break
|
||||
}
|
||||
|
||||
return filePaths
|
||||
}
|
||||
catch (error) {
|
||||
console.warn(error?.message || error)
|
||||
return false
|
||||
}
|
||||
return res.filePaths
|
||||
}
|
||||
catch (error) {
|
||||
console.warn(error?.message || error)
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
ipcMain.handle('open-path', async (event, pathValue) => {
|
||||
try {
|
||||
@ -38,4 +50,28 @@ export default () => {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle(
|
||||
'show-save-dialog',
|
||||
async (event, { filePath = '', ...options } = {}) => {
|
||||
try {
|
||||
const result = await dialog.showSaveDialog({
|
||||
...options,
|
||||
})
|
||||
if (!result || result.canceled || !result.filePath) {
|
||||
return false
|
||||
}
|
||||
|
||||
const destinationPath = result.filePath
|
||||
|
||||
await fs.copy(filePath, destinationPath)
|
||||
|
||||
return true
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error?.message || error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
import path from 'node:path'
|
||||
|
||||
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'
|
||||
@ -6,7 +9,14 @@ import scrcpy from './scrcpy/index.js'
|
||||
export default {
|
||||
init(expose) {
|
||||
expose('nodePath', path)
|
||||
expose('electron', electron())
|
||||
|
||||
expose('appStore', store())
|
||||
|
||||
expose('electron', {
|
||||
...electron(),
|
||||
configs,
|
||||
})
|
||||
|
||||
expose('adbkit', adbkit())
|
||||
expose('scrcpy', scrcpy())
|
||||
},
|
||||
|
27
electron/exposes/store/index.js
Normal file
27
electron/exposes/store/index.js
Normal file
@ -0,0 +1,27 @@
|
||||
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),
|
||||
}
|
||||
}
|
@ -25,3 +25,18 @@ export function exposeContext(key, value) {
|
||||
window[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个代理对象,将目标对象的指定方法转发并执行。
|
||||
*
|
||||
* @param {object} targetObject - 目标对象,包含要代理的方法。
|
||||
* @param {string[]} methodNames - 要代理的方法名称数组。
|
||||
* @returns {object} - 代理对象,包含转发的方法。
|
||||
*/
|
||||
export function createProxy(targetObject, methodNames) {
|
||||
return methodNames.reduce((proxyObj, methodName) => {
|
||||
proxyObj[methodName] = (...args) => targetObject[methodName](...args)
|
||||
|
||||
return proxyObj
|
||||
}, {})
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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 必须位于非依赖项的顶部
|
||||
@ -9,6 +10,8 @@ import { icnsLogoPath, icoLogoPath, logoPath } from './configs/index.js'
|
||||
|
||||
import events from './events/index.js'
|
||||
|
||||
const appStore = new Store()
|
||||
|
||||
// The built directory structure
|
||||
//
|
||||
// ├─┬─┬ dist
|
||||
|
@ -32,8 +32,10 @@
|
||||
"dayjs": "^1.11.10",
|
||||
"electron": "^26.1.0",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.4",
|
||||
"element-plus": "^2.4.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"pinia": "^2.1.7",
|
||||
"typescript": "^5.2.2",
|
||||
|
139
pnpm-lock.yaml
139
pnpm-lock.yaml
@ -43,12 +43,18 @@ devDependencies:
|
||||
electron-builder:
|
||||
specifier: ^24.6.4
|
||||
version: 24.6.4
|
||||
electron-store:
|
||||
specifier: ^8.1.0
|
||||
version: 8.1.0
|
||||
electron-updater:
|
||||
specifier: ^6.1.4
|
||||
version: 6.1.4
|
||||
element-plus:
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0(vue@3.3.4)
|
||||
fs-extra:
|
||||
specifier: ^11.1.1
|
||||
version: 11.1.1
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
@ -1566,6 +1572,17 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/ajv-formats@2.1.1(ajv@8.12.0):
|
||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||
peerDependencies:
|
||||
ajv: ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
ajv:
|
||||
optional: true
|
||||
dependencies:
|
||||
ajv: 8.12.0
|
||||
dev: true
|
||||
|
||||
/ajv-keywords@3.5.2(ajv@6.12.6):
|
||||
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
|
||||
peerDependencies:
|
||||
@ -1583,6 +1600,15 @@ packages:
|
||||
uri-js: 4.4.1
|
||||
dev: true
|
||||
|
||||
/ajv@8.12.0:
|
||||
resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
uri-js: 4.4.1
|
||||
dev: true
|
||||
|
||||
/ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
@ -1703,6 +1729,11 @@ packages:
|
||||
engines: {node: '>= 4.0.0'}
|
||||
dev: true
|
||||
|
||||
/atomically@1.7.0:
|
||||
resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==}
|
||||
engines: {node: '>=10.12.0'}
|
||||
dev: true
|
||||
|
||||
/autoprefixer@10.4.16(postcss@8.4.31):
|
||||
resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@ -2062,6 +2093,22 @@ packages:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
dev: true
|
||||
|
||||
/conf@10.2.0:
|
||||
resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
ajv: 8.12.0
|
||||
ajv-formats: 2.1.1(ajv@8.12.0)
|
||||
atomically: 1.7.0
|
||||
debounce-fn: 4.0.0
|
||||
dot-prop: 6.0.1
|
||||
env-paths: 2.2.1
|
||||
json-schema-typed: 7.0.3
|
||||
onetime: 5.1.2
|
||||
pkg-up: 3.1.0
|
||||
semver: 7.5.4
|
||||
dev: true
|
||||
|
||||
/config-file-ts@0.2.4:
|
||||
resolution: {integrity: sha512-cKSW0BfrSaAUnxpgvpXPLaaW/umg4bqg4k3GO1JqlRfpx+d5W0GDXznCMkWotJQek5Mmz1MJVChQnz3IVaeMZQ==}
|
||||
dependencies:
|
||||
@ -2172,6 +2219,13 @@ packages:
|
||||
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
||||
dev: true
|
||||
|
||||
/debounce-fn@4.0.0:
|
||||
resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
mimic-fn: 3.1.0
|
||||
dev: true
|
||||
|
||||
/debug@3.2.7:
|
||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||
peerDependencies:
|
||||
@ -2348,6 +2402,13 @@ packages:
|
||||
domhandler: 5.0.3
|
||||
dev: true
|
||||
|
||||
/dot-prop@6.0.1:
|
||||
resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
is-obj: 2.0.0
|
||||
dev: true
|
||||
|
||||
/dotenv-expand@5.1.0:
|
||||
resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==}
|
||||
dev: true
|
||||
@ -2403,6 +2464,13 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/electron-store@8.1.0:
|
||||
resolution: {integrity: sha512-2clHg/juMjOH0GT9cQ6qtmIvK183B39ZXR0bUoPwKwYHJsEF3quqyDzMFUAu+0OP8ijmN2CbPRAelhNbWUbzwA==}
|
||||
dependencies:
|
||||
conf: 10.2.0
|
||||
type-fest: 2.19.0
|
||||
dev: true
|
||||
|
||||
/electron-to-chromium@1.4.554:
|
||||
resolution: {integrity: sha512-Q0umzPJjfBrrj8unkONTgbKQXzXRrH7sVV7D9ea2yBV3Oaogz991yhbpfvo2LMNkJItmruXTEzVpP9cp7vaIiQ==}
|
||||
dev: true
|
||||
@ -3014,6 +3082,13 @@ packages:
|
||||
to-regex-range: 5.0.1
|
||||
dev: true
|
||||
|
||||
/find-up@3.0.0:
|
||||
resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
locate-path: 3.0.0
|
||||
dev: true
|
||||
|
||||
/find-up@4.1.0:
|
||||
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
||||
engines: {node: '>=8'}
|
||||
@ -3065,6 +3140,15 @@ packages:
|
||||
universalify: 2.0.0
|
||||
dev: true
|
||||
|
||||
/fs-extra@11.1.1:
|
||||
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
|
||||
engines: {node: '>=14.14'}
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
jsonfile: 6.1.0
|
||||
universalify: 2.0.0
|
||||
dev: true
|
||||
|
||||
/fs-extra@8.1.0:
|
||||
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
|
||||
engines: {node: '>=6 <7 || >=8'}
|
||||
@ -3495,6 +3579,11 @@ packages:
|
||||
engines: {node: '>=0.12.0'}
|
||||
dev: true
|
||||
|
||||
/is-obj@2.0.0:
|
||||
resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-path-inside@3.0.3:
|
||||
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
|
||||
engines: {node: '>=8'}
|
||||
@ -3570,6 +3659,14 @@ packages:
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
|
||||
/json-schema-traverse@1.0.0:
|
||||
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||
dev: true
|
||||
|
||||
/json-schema-typed@7.0.3:
|
||||
resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==}
|
||||
dev: true
|
||||
|
||||
/json-stable-stringify-without-jsonify@1.0.1:
|
||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||
dev: true
|
||||
@ -3650,6 +3747,14 @@ packages:
|
||||
engines: {node: '>=14'}
|
||||
dev: true
|
||||
|
||||
/locate-path@3.0.0:
|
||||
resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
p-locate: 3.0.0
|
||||
path-exists: 3.0.0
|
||||
dev: true
|
||||
|
||||
/locate-path@5.0.0:
|
||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||
engines: {node: '>=8'}
|
||||
@ -3800,6 +3905,11 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/mimic-fn@3.1.0:
|
||||
resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/mimic-response@1.0.1:
|
||||
resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
|
||||
engines: {node: '>=4'}
|
||||
@ -4037,6 +4147,13 @@ packages:
|
||||
yocto-queue: 0.1.0
|
||||
dev: true
|
||||
|
||||
/p-locate@3.0.0:
|
||||
resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
p-limit: 2.3.0
|
||||
dev: true
|
||||
|
||||
/p-locate@4.1.0:
|
||||
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
|
||||
engines: {node: '>=8'}
|
||||
@ -4088,6 +4205,11 @@ packages:
|
||||
resolution: {integrity: sha512-D66DG2nKx4Yoq66TMEyCUHlR2STGqO7vsBrX7tgyS9cfQyO6XD5JyzOiflwmWN6a4wbUAqpmHqmrxlTQVGZcbA==}
|
||||
dev: true
|
||||
|
||||
/path-exists@3.0.0:
|
||||
resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
|
||||
engines: {node: '>=4'}
|
||||
dev: true
|
||||
|
||||
/path-exists@4.0.0:
|
||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||
engines: {node: '>=8'}
|
||||
@ -4168,6 +4290,13 @@ packages:
|
||||
pathe: 1.1.1
|
||||
dev: true
|
||||
|
||||
/pkg-up@3.1.0:
|
||||
resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
find-up: 3.0.0
|
||||
dev: true
|
||||
|
||||
/plist@3.1.0:
|
||||
resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==}
|
||||
engines: {node: '>=10.4.0'}
|
||||
@ -4372,6 +4501,11 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/require-from-string@2.0.2:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/resolve-alpn@1.2.1:
|
||||
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
|
||||
dev: true
|
||||
@ -4870,6 +5004,11 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/type-fest@2.19.0:
|
||||
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
|
||||
engines: {node: '>=12.20'}
|
||||
dev: true
|
||||
|
||||
/typescript@4.9.5:
|
||||
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
|
||||
engines: {node: '>=4.2.0'}
|
||||
|
22
src/App.vue
22
src/App.vue
@ -15,33 +15,33 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Devices from './components/Devices/index.vue'
|
||||
import Advanced from './components/Advanced/index.vue'
|
||||
import AboutUs from './components/AboutUs/index.vue'
|
||||
import Device from './components/Device/index.vue'
|
||||
import Preference from './components/Preference/index.vue'
|
||||
import About from './components/About/index.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Devices,
|
||||
Advanced,
|
||||
AboutUs,
|
||||
Device,
|
||||
Preference,
|
||||
About,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabsModel: [
|
||||
{
|
||||
label: '设备列表',
|
||||
prop: 'Devices',
|
||||
prop: 'Device',
|
||||
},
|
||||
{
|
||||
label: '高级配置',
|
||||
prop: 'Advanced',
|
||||
label: '偏好设置',
|
||||
prop: 'Preference',
|
||||
},
|
||||
{
|
||||
label: '关于',
|
||||
prop: 'AboutUs',
|
||||
prop: 'About',
|
||||
},
|
||||
],
|
||||
activeTab: 'Devices',
|
||||
activeTab: 'Device',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -1,189 +0,0 @@
|
||||
<template>
|
||||
<div class="grid gap-6 pr-2">
|
||||
<el-card v-for="(item, index) of scrcpyModel" :key="index" shadow="hover" class="">
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1 w-0 truncate pl-2 text-base">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
<div class="flex-none pl-4">
|
||||
<el-button type="primary" text @click="handleReset(item.type)">
|
||||
恢复默认值
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="">
|
||||
<el-form ref="elForm" :model="scrcpyForm" label-width="135px" class="pr-8 pt-4">
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
v-for="(item_1, index_1) of getSubModel(item.type)"
|
||||
:key="index_1"
|
||||
:span="12"
|
||||
:offset="0"
|
||||
>
|
||||
<el-form-item :label="item_1.label" :prop="item_1.field">
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<el-tooltip
|
||||
v-if="item_1.tips"
|
||||
class=""
|
||||
effect="dark"
|
||||
:content="item_1.tips"
|
||||
placement="bottom"
|
||||
>
|
||||
<el-link
|
||||
class="mr-1 !text-base"
|
||||
icon="InfoFilled"
|
||||
type="warning"
|
||||
:underline="false"
|
||||
>
|
||||
</el-link>
|
||||
</el-tooltip>
|
||||
<span class="" :title="item_1.placeholder">{{ item_1.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-if="item_1.type === 'input'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="scrcpyForm[item_1.field]"
|
||||
class="!w-full"
|
||||
:title="item_1.placeholder"
|
||||
:placeholder="item_1.placeholder"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-input
|
||||
v-if="item_1.type === 'input.number'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model.number="scrcpyForm[item_1.field]"
|
||||
class="!w-full"
|
||||
:title="item_1.placeholder"
|
||||
:placeholder="item_1.placeholder"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-input
|
||||
v-if="item_1.type === 'input.directory'"
|
||||
v-bind="item_1.props || {}"
|
||||
:value="scrcpyForm[item_1.field]"
|
||||
readonly
|
||||
class="!w-full"
|
||||
clearable
|
||||
:placeholder="item_1.placeholder"
|
||||
:title="item_1.placeholder"
|
||||
@click="handleDirectory(item_1)"
|
||||
></el-input>
|
||||
<el-switch
|
||||
v-if="item_1.type === 'switch'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="scrcpyForm[item_1.field]"
|
||||
class="!w-full"
|
||||
clearable
|
||||
:title="item_1.placeholder"
|
||||
></el-switch>
|
||||
<el-select
|
||||
v-if="item_1.type === 'select'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="scrcpyForm[item_1.field]"
|
||||
:placeholder="item_1.placeholder"
|
||||
class="!w-full"
|
||||
clearable
|
||||
:title="item_1.placeholder"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item_2, index_2) in item_1.options"
|
||||
:key="index_2"
|
||||
:label="item_2.label"
|
||||
:value="item_2.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { debounce } from 'lodash-es'
|
||||
import { useScrcpyStore } from '@/store/index.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const scrcpyStore = useScrcpyStore()
|
||||
|
||||
return {
|
||||
scrcpyModel: [
|
||||
{
|
||||
label: '视频控制',
|
||||
type: 'video',
|
||||
},
|
||||
{
|
||||
label: '设备控制',
|
||||
type: 'device',
|
||||
},
|
||||
{
|
||||
label: '窗口控制',
|
||||
type: 'window',
|
||||
},
|
||||
{
|
||||
label: '音视频录制',
|
||||
type: 'record',
|
||||
},
|
||||
{
|
||||
label: '音频控制',
|
||||
type: 'audio',
|
||||
},
|
||||
],
|
||||
scrcpyForm: { ...scrcpyStore.config },
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
scrcpyForm: {
|
||||
handler() {
|
||||
this.handleSave()
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.handleSave = debounce(this.handleSave, 1000, { leading: false, trailing: true })
|
||||
},
|
||||
methods: {
|
||||
async handleDirectory({ field }) {
|
||||
const res = await this.$electron.ipcRenderer.invoke('show-open-dialog', {
|
||||
properties: ['openDirectory'],
|
||||
defaultPath: this.scrcpyForm[field],
|
||||
})
|
||||
|
||||
if (!res) {
|
||||
return false
|
||||
}
|
||||
|
||||
const value = this.$path.normalize(res[0])
|
||||
|
||||
this.scrcpyForm[field] = value
|
||||
},
|
||||
handleSave() {
|
||||
this.$store.scrcpy.updateConfig(this.scrcpyForm)
|
||||
this.$message.success('保存配置成功,将在下一次控制设备时生效')
|
||||
},
|
||||
getSubModel(type) {
|
||||
const value = this.$store.scrcpy.getModel(type)
|
||||
return value
|
||||
},
|
||||
handleReset(type) {
|
||||
this.scrcpyForm = { ...this.scrcpyForm, ...this.$store.scrcpy.getDefaultConfig(type) }
|
||||
this.$store.scrcpy.updateConfig(this.scrcpyForm)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
:deep(.el-card) {
|
||||
--el-card-padding: 8px;
|
||||
}
|
||||
</style>
|
@ -152,10 +152,7 @@ export default {
|
||||
const fileName = `${device.name}-screencap-${dayjs().format(
|
||||
'YYYY-MM-DD-HH-mm-ss',
|
||||
)}.png`
|
||||
const savePath = this.$path.resolve(
|
||||
this.scrcpyConfig['--record'],
|
||||
fileName,
|
||||
)
|
||||
const savePath = this.$path.resolve(this.scrcpyConfig.savePath, fileName)
|
||||
|
||||
try {
|
||||
await this.$adb.screencap(device.id, { savePath })
|
@ -213,7 +213,7 @@ export default {
|
||||
this.$refs.elTable.toggleRowExpansion(...params)
|
||||
},
|
||||
getRecordPath(row) {
|
||||
const basePath = this.scrcpyConfig['--record']
|
||||
const basePath = this.scrcpyConfig.savePath
|
||||
const recordFormat = this.scrcpyConfig['--record-format']
|
||||
const fileName = `${row.name || row.id}-recording-${dayjs().format(
|
||||
'YYYY-MM-DD-HH-mm-ss',
|
278
src/components/Preference/index.vue
Normal file
278
src/components/Preference/index.vue
Normal file
@ -0,0 +1,278 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<div class="pb-4 pr-2 flex items-center">
|
||||
<div class="">
|
||||
<el-button type="" plain @click="handleImport">
|
||||
导入配置
|
||||
</el-button>
|
||||
<el-button type="" plain @click="handleExport">
|
||||
导出配置
|
||||
</el-button>
|
||||
<el-button type="" plain @click="handleEdit">
|
||||
编辑配置
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-6 pr-2">
|
||||
<el-card
|
||||
v-for="(item, index) of scrcpyModel"
|
||||
:key="index"
|
||||
shadow="hover"
|
||||
class=""
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1 w-0 truncate pl-2 text-base">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
<div class="flex-none pl-4">
|
||||
<el-button type="primary" text @click="handleReset(item.type)">
|
||||
恢复默认值
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="">
|
||||
<el-form
|
||||
ref="elForm"
|
||||
:model="scrcpyForm"
|
||||
label-width="135px"
|
||||
class="pr-8 pt-4"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
v-for="(item_1, index_1) of getSubModel(item.type)"
|
||||
:key="index_1"
|
||||
:span="12"
|
||||
:offset="0"
|
||||
>
|
||||
<el-form-item :label="item_1.label" :prop="item_1.field">
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<el-tooltip
|
||||
v-if="item_1.tips"
|
||||
class=""
|
||||
effect="dark"
|
||||
:content="item_1.tips"
|
||||
placement="bottom"
|
||||
>
|
||||
<el-link
|
||||
class="mr-1 !text-base"
|
||||
icon="InfoFilled"
|
||||
type="warning"
|
||||
:underline="false"
|
||||
>
|
||||
</el-link>
|
||||
</el-tooltip>
|
||||
<span class="" :title="item_1.placeholder">{{
|
||||
item_1.label
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-if="item_1.type === 'input'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="scrcpyForm[item_1.field]"
|
||||
class="!w-full"
|
||||
:title="item_1.placeholder"
|
||||
:placeholder="item_1.placeholder"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-input
|
||||
v-if="item_1.type === 'input.number'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model.number="scrcpyForm[item_1.field]"
|
||||
class="!w-full"
|
||||
:title="item_1.placeholder"
|
||||
:placeholder="item_1.placeholder"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-input
|
||||
v-if="item_1.type === 'input.path'"
|
||||
v-bind="item_1.props || {}"
|
||||
:value="scrcpyForm[item_1.field]"
|
||||
readonly
|
||||
class="!w-full"
|
||||
clearable
|
||||
:placeholder="item_1.placeholder"
|
||||
:title="item_1.placeholder"
|
||||
@click="
|
||||
handleSelect(item_1, {
|
||||
properties: item_1.properties,
|
||||
filters: item_1.filters,
|
||||
})
|
||||
"
|
||||
></el-input>
|
||||
<el-switch
|
||||
v-if="item_1.type === 'switch'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="scrcpyForm[item_1.field]"
|
||||
class="!w-full"
|
||||
clearable
|
||||
:title="item_1.placeholder"
|
||||
></el-switch>
|
||||
<el-select
|
||||
v-if="item_1.type === 'select'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="scrcpyForm[item_1.field]"
|
||||
:placeholder="item_1.placeholder"
|
||||
class="!w-full"
|
||||
clearable
|
||||
:title="item_1.placeholder"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item_2, index_2) in item_1.options"
|
||||
:key="index_2"
|
||||
:label="item_2.label"
|
||||
:value="item_2.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { debounce } from 'lodash-es'
|
||||
import { useScrcpyStore } from '@/store/index.js'
|
||||
import LoadingIcon from '@/components/Device/ControlBar/LoadingIcon/index.vue'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const scrcpyStore = useScrcpyStore()
|
||||
|
||||
return {
|
||||
scrcpyModel: [
|
||||
{
|
||||
label: '自定义',
|
||||
type: 'custom',
|
||||
},
|
||||
{
|
||||
label: '视频控制',
|
||||
type: 'video',
|
||||
},
|
||||
{
|
||||
label: '设备控制',
|
||||
type: 'device',
|
||||
},
|
||||
{
|
||||
label: '窗口控制',
|
||||
type: 'window',
|
||||
},
|
||||
{
|
||||
label: '音视频录制',
|
||||
type: 'record',
|
||||
},
|
||||
{
|
||||
label: '音频控制',
|
||||
type: 'audio',
|
||||
},
|
||||
],
|
||||
scrcpyForm: { ...scrcpyStore.config },
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
scrcpyForm: {
|
||||
handler() {
|
||||
this.handleSave()
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.handleSave = debounce(this.handleSave, 1000, {
|
||||
leading: false,
|
||||
trailing: true,
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async handleImport() {
|
||||
const result = await this.$electron.ipcRenderer.invoke(
|
||||
'show-open-dialog',
|
||||
{
|
||||
preset: 'replaceFile',
|
||||
filePath: this.$appStore.path,
|
||||
filters: [{ name: '请选择要导入的配置文件', extensions: ['json'] }],
|
||||
},
|
||||
)
|
||||
|
||||
if (!result) {
|
||||
this.$message.warning('导入偏好配置失败')
|
||||
return
|
||||
}
|
||||
|
||||
this.$message.success('导入偏好配置成功')
|
||||
this.scrcpyForm = this.$store.scrcpy.init()
|
||||
},
|
||||
handleEdit() {
|
||||
this.$appStore.openInEditor()
|
||||
},
|
||||
async handleExport() {
|
||||
const messageEl = this.$message({
|
||||
message: ' 正在导出偏好配置中...',
|
||||
icon: LoadingIcon,
|
||||
duration: 0,
|
||||
})
|
||||
|
||||
const result = await this.$electron.ipcRenderer.invoke(
|
||||
'show-save-dialog',
|
||||
{
|
||||
defaultPath: 'escrcpy-configs.json',
|
||||
filePath: this.$appStore.path,
|
||||
filters: [
|
||||
{ name: '请选择配置文件要保存的位置', extensions: ['json'] },
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
messageEl.close()
|
||||
|
||||
if (result) {
|
||||
this.$message.success('导出偏好配置成功')
|
||||
}
|
||||
},
|
||||
async handleSelect({ field }, { properties, filters } = {}) {
|
||||
const res = await this.$electron.ipcRenderer.invoke('show-open-dialog', {
|
||||
properties,
|
||||
filters,
|
||||
defaultPath: this.scrcpyForm[field],
|
||||
})
|
||||
|
||||
if (!res) {
|
||||
return false
|
||||
}
|
||||
|
||||
const value = res[0]
|
||||
|
||||
this.scrcpyForm[field] = value
|
||||
},
|
||||
handleSave() {
|
||||
this.$store.scrcpy.updateConfig(this.scrcpyForm)
|
||||
this.$message.success('保存配置成功,将在下一次控制设备时生效')
|
||||
},
|
||||
getSubModel(type) {
|
||||
const value = this.$store.scrcpy.getModel(type)
|
||||
return value
|
||||
},
|
||||
handleReset(type) {
|
||||
this.scrcpyForm = {
|
||||
...this.scrcpyForm,
|
||||
...this.$store.scrcpy.getDefaultConfig(type),
|
||||
}
|
||||
this.$store.scrcpy.updateConfig(this.scrcpyForm)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
:deep(.el-card) {
|
||||
--el-card-padding: 8px;
|
||||
}
|
||||
</style>
|
@ -20,6 +20,7 @@ app.config.globalProperties.$electron = window.electron
|
||||
app.config.globalProperties.$adb = window.adbkit
|
||||
app.config.globalProperties.$scrcpy = window.scrcpy
|
||||
app.config.globalProperties.$path = window.nodePath
|
||||
app.config.globalProperties.$appStore = window.appStore
|
||||
|
||||
app.mount('#app').$nextTick(() => {
|
||||
// Remove Preload scripts loading
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { pickBy } from 'lodash-es'
|
||||
import * as scrcpyModel from './model/index.js'
|
||||
import storage from '@/utils/storages/index.js'
|
||||
|
||||
const $appStore = window.appStore
|
||||
|
||||
/**
|
||||
* 获取 Scrcpy 默认配置
|
||||
@ -34,7 +35,7 @@ export const useScrcpyStore = defineStore({
|
||||
model: scrcpyModel,
|
||||
defaultConfig: getDefaultConfig(),
|
||||
config: {},
|
||||
excludeKeys: ['--record', '--record-format'],
|
||||
excludeKeys: ['--record-format', 'savePath', 'adbPath', 'scrcpyPath'],
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
@ -73,7 +74,7 @@ export const useScrcpyStore = defineStore({
|
||||
init() {
|
||||
this.config = {
|
||||
...this.defaultConfig,
|
||||
...(storage.get('scrcpyConfig') || {}),
|
||||
...($appStore.get('scrcpy') || {}),
|
||||
}
|
||||
|
||||
return this.config
|
||||
@ -83,7 +84,7 @@ export const useScrcpyStore = defineStore({
|
||||
|
||||
console.log('pickConfig', pickConfig)
|
||||
|
||||
storage.set('scrcpyConfig', pickConfig)
|
||||
$appStore.set('scrcpy', pickConfig)
|
||||
|
||||
this.init()
|
||||
},
|
||||
|
37
src/store/scrcpy/model/custom/index.js
Normal file
37
src/store/scrcpy/model/custom/index.js
Normal file
@ -0,0 +1,37 @@
|
||||
export default () => {
|
||||
const $path = window.nodePath
|
||||
|
||||
const { adbPath, scrcpyPath } = window?.electron?.configs || {}
|
||||
|
||||
return [
|
||||
{
|
||||
label: '文件存储路径',
|
||||
type: 'input.path',
|
||||
field: 'savePath',
|
||||
value: $path.resolve('../'),
|
||||
placeholder: '默认值为执行应用的同级目录',
|
||||
tips: '截图和录制的音视频都存在这里',
|
||||
properties: ['openDirectory'],
|
||||
},
|
||||
{
|
||||
label: 'Adb 路径',
|
||||
field: 'adbPath',
|
||||
type: 'input.path',
|
||||
value: adbPath,
|
||||
tips: '用于连接设备的 adb.exe 的地址,注意:改变此选项时需要重启服务',
|
||||
placeholder: '请选择 Adb.exe',
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: '请选择 Adb.exe', extensions: ['exe'] }],
|
||||
},
|
||||
{
|
||||
label: 'Scrcpy 路径',
|
||||
field: 'scrcpyPath',
|
||||
type: 'input.path',
|
||||
value: scrcpyPath,
|
||||
tips: '用于控制设备的 Scrcpy.exe 的地址',
|
||||
placeholder: '请选择 Scrcpy.exe',
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: '请选择 Scrcpy.exe', extensions: ['exe'] }],
|
||||
},
|
||||
]
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
export { default as custom } from './custom/index.js'
|
||||
export { default as video } from './video/index.js'
|
||||
export { default as device } from './device/index.js'
|
||||
export { default as window } from './window/index.js'
|
||||
|
@ -1,15 +1,5 @@
|
||||
export default () => {
|
||||
const $path = window.nodePath
|
||||
|
||||
return [
|
||||
{
|
||||
label: '文件保存路径',
|
||||
type: 'input.directory',
|
||||
field: '--record',
|
||||
value: $path.resolve('../'),
|
||||
placeholder: '默认值为执行应用的同级目录',
|
||||
tips: '截图和录制的音视频都存在这里',
|
||||
},
|
||||
{
|
||||
label: '录制视频格式',
|
||||
type: 'select',
|
||||
|
Loading…
Reference in New Issue
Block a user