mirror of
https://github.com/viarotel-org/escrcpy.git
synced 2024-11-23 23:21:02 +01:00
feat: 🚀 新增支持 深色模式、国际化语言、运行日志等功能
This commit is contained in:
parent
894b581988
commit
4b13f5892b
26
.vscode/settings.json
vendored
26
.vscode/settings.json
vendored
@ -29,17 +29,31 @@
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/locales/index.js",
|
||||
"src/locales/languages"
|
||||
],
|
||||
"i18n-ally.sourceLanguage": "zh",
|
||||
"i18n-ally.localesPaths": ["src/locales/index.js", "src/locales/languages"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.extract.ignored": [
|
||||
"Switch",
|
||||
"${item.id}(${item.$name}${\r\n item.$remark ? `,${item.$remark}` : ''\r\n })",
|
||||
",${item.$remark}",
|
||||
"${row.$remark ? `${row.$remark}-` : ''}${\r\n row.$name\r\n }-${this.$replaceIP(row.id)}-recording-${dayjs().format(\r\n 'YYYY-MM-DD-HH-mm-ss',\r\n )}.${recordFormat}",
|
||||
"--serial=${row.id} --window-title=${\r\n row.$remark ? `${row.$remark}-` : ''\r\n }${row.$name}-${\r\n row.id\r\n }-🎥录制中... --record=${savePath} ${this.scrcpyArgs(row.id)}",
|
||||
"--serial=${row.id} --window-title=${\r\n row.$remark ? `${row.$remark}-` : ''\r\n }${row.$name}-${row.id} ${this.scrcpyArgs(row.id)}"
|
||||
"--serial=${row.id} --window-title=${\r\n row.$remark ? `${row.$remark}-` : ''\r\n }${row.$name}-${row.id} ${this.scrcpyArgs(row.id)}",
|
||||
"${device.$remark ? `${device.$remark}-` : ''}${\r\n device.$name\r\n }-${this.$replaceIP(device.id)}-screencap-${dayjs().format(\r\n 'YYYY-MM-DD-HH-mm-ss',\r\n )}.png",
|
||||
"input keyevent KEYCODE_APP_SWITCH",
|
||||
"input keyevent KEYCODE_HOME",
|
||||
"input keyevent KEYCODE_BACK",
|
||||
"Back",
|
||||
"Notification",
|
||||
"cmd statusbar expand-notifications",
|
||||
"input keyevent KEYCODE_POWER",
|
||||
"Crop",
|
||||
"&",
|
||||
"\r\n {{\r\n loading ",
|
||||
"& percent\r\n ? `${$t(\"about.update.progress\")}...(${percent.toFixed(1)}%)`\r\n : $t(\"about.update\")\r\n }}\r\n ",
|
||||
"\r\n Supported by\r\n\r\n ",
|
||||
"Viarotel",
|
||||
"\r\n\r\n v{{ version }}\r\n ",
|
||||
"pair ${this.formData.host}:${this.formData.port} ${this.formData.pair}"
|
||||
]
|
||||
}
|
||||
|
@ -133,7 +133,7 @@
|
||||
7. 定制化,支持对单个设备进行独立配置 ✅
|
||||
8. 添加 macOS 及 linux 操作系统的支持 ✅
|
||||
9. 支持国际化 ✅
|
||||
10. 对深色模式的支持 🚧
|
||||
10. 对深色模式的支持 ✅
|
||||
11. 添加对游戏的增强功能,如游戏键位映射 🚧
|
||||
|
||||
## 常见问题
|
||||
|
@ -1,7 +1,10 @@
|
||||
import fs from 'fs-extra'
|
||||
import { dialog, ipcMain, shell } from 'electron'
|
||||
import themeHandles from './theme/index.js'
|
||||
|
||||
export default (mainWindow) => {
|
||||
themeHandles(mainWindow)
|
||||
|
||||
export default () => {
|
||||
ipcMain.handle(
|
||||
'show-open-dialog',
|
||||
async (event, { preset = '', ...options } = {}) => {
|
||||
@ -11,11 +14,11 @@ export default () => {
|
||||
.catch(e => console.warn(e))
|
||||
|
||||
if (res.canceled) {
|
||||
throw new Error('用户取消操作')
|
||||
throw new Error('User cancel operation')
|
||||
}
|
||||
|
||||
if (!res.filePaths.length) {
|
||||
throw new Error('获取目录或文件路径失败')
|
||||
throw new Error('Get the directory or file path failure')
|
||||
}
|
||||
|
||||
const filePaths = res.filePaths
|
||||
@ -48,11 +51,11 @@ export default () => {
|
||||
.catch(e => console.warn(e))
|
||||
|
||||
if (res.canceled) {
|
||||
throw new Error('用户取消操作')
|
||||
throw new Error('User cancel operation')
|
||||
}
|
||||
|
||||
if (!res.filePath) {
|
||||
throw new Error('获取文件路径失败')
|
||||
throw new Error('Failure to obtain the file path')
|
||||
}
|
||||
|
||||
const destinationPath = res.filePath
|
||||
|
26
electron/events/handles/theme/index.js
Normal file
26
electron/events/handles/theme/index.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { ipcMain, nativeTheme } from 'electron'
|
||||
|
||||
export default (mainWindow) => {
|
||||
const appTheme = {
|
||||
value() {
|
||||
return nativeTheme.themeSource
|
||||
},
|
||||
update(value) {
|
||||
nativeTheme.themeSource = value
|
||||
},
|
||||
isDark() {
|
||||
return nativeTheme.shouldUseDarkColors
|
||||
},
|
||||
}
|
||||
|
||||
Object.entries(appTheme).forEach(([key, handler]) => {
|
||||
ipcMain.handle(`app-theme-${key}`, (_, value) => handler(value))
|
||||
})
|
||||
|
||||
nativeTheme.on('updated', () => {
|
||||
mainWindow.webContents.send('app-theme-change', {
|
||||
isDark: appTheme.isDark(),
|
||||
value: appTheme.value(),
|
||||
})
|
||||
})
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
import { Menu, Tray, app, dialog } from 'electron'
|
||||
import { trayPath } from '@electron/configs/index.js'
|
||||
import appStore from '@electron/helpers/store.js'
|
||||
import { executeI18n } from '@electron/helpers/index.js'
|
||||
|
||||
export default (mainWindow) => {
|
||||
const t = value => executeI18n(mainWindow, value)
|
||||
|
||||
let tray = null
|
||||
|
||||
const showApp = () => {
|
||||
@ -38,7 +41,7 @@ export default (mainWindow) => {
|
||||
return true
|
||||
}
|
||||
|
||||
const closeApp = (response) => {
|
||||
const closeApp = async (response) => {
|
||||
if (response === 0) {
|
||||
quitApp()
|
||||
return true
|
||||
@ -56,20 +59,20 @@ export default (mainWindow) => {
|
||||
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '打开',
|
||||
label: await t('common.open'),
|
||||
click: () => {
|
||||
showApp()
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '重启服务',
|
||||
label: await t('common.restart'),
|
||||
click: () => {
|
||||
app.relaunch()
|
||||
quitApp()
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '退出',
|
||||
label: await t('close.quit'),
|
||||
click: () => {
|
||||
quitApp()
|
||||
},
|
||||
@ -101,11 +104,15 @@ export default (mainWindow) => {
|
||||
|
||||
const { response, checkboxChecked } = await dialog.showMessageBox({
|
||||
type: 'question',
|
||||
buttons: ['退出', '最小化到托盘', '取消退出'],
|
||||
title: '提示',
|
||||
message: '确定要退出吗?',
|
||||
buttons: [
|
||||
await t('close.quit'),
|
||||
await t('close.minimize'),
|
||||
await t('close.quit.cancel'),
|
||||
],
|
||||
title: await t('common.tips'),
|
||||
message: await t('close.message'),
|
||||
checkboxChecked: false,
|
||||
checkboxLabel: '是否记住选择?',
|
||||
checkboxLabel: await t('close.remember'),
|
||||
})
|
||||
|
||||
// console.log('response', response)
|
||||
|
@ -4,9 +4,7 @@ import { autoUpdater } from 'electron-updater'
|
||||
import { devPublishPath } from '@electron/configs/index.js'
|
||||
|
||||
export default (mainWindow) => {
|
||||
// dev-start, 这里是为了在本地做应用升级测试使用,正式环境请务必删除
|
||||
// if (is.dev && process.env.ELECTRON_RENDERER_URL) {
|
||||
if (is.dev && process.env.VITE_DEV_SERVER_URL) {
|
||||
if (is.dev) {
|
||||
autoUpdater.updateConfigPath = devPublishPath
|
||||
Object.defineProperty(app, 'isPackaged', {
|
||||
get() {
|
||||
|
@ -113,7 +113,7 @@ const display = async (deviceId) => {
|
||||
value = uniq(mapValue)
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error?.message || error)
|
||||
console.warn(error?.message || error)
|
||||
}
|
||||
|
||||
console.log('display.deviceId.value', value)
|
||||
|
@ -14,10 +14,10 @@ export default {
|
||||
init(expose) {
|
||||
expose('nodePath', path)
|
||||
|
||||
expose('appStore', store)
|
||||
|
||||
expose('appLog', log)
|
||||
|
||||
expose('appStore', store)
|
||||
|
||||
expose('electron', {
|
||||
...electron(),
|
||||
configs,
|
||||
|
@ -1,8 +1,13 @@
|
||||
import log from '@electron/helpers/log.js'
|
||||
|
||||
import appStore from './store.js'
|
||||
import { createProxy } from './index.js'
|
||||
|
||||
Object.assign(console, {
|
||||
...createProxy(log.functions, log.levels),
|
||||
raw: console.log,
|
||||
})
|
||||
const debug = appStore.get('common.debug') || false
|
||||
|
||||
if (debug) {
|
||||
Object.assign(console, {
|
||||
...createProxy(log.functions, log.levels),
|
||||
raw: console.log,
|
||||
})
|
||||
}
|
||||
|
@ -42,3 +42,15 @@ export function createProxy(targetObject, methodNames) {
|
||||
return proxyObj
|
||||
}, {})
|
||||
}
|
||||
|
||||
export async function executeI18n(mainWindow, value) {
|
||||
try {
|
||||
return await mainWindow.webContents.executeJavaScript(
|
||||
`window.t('${value}')`,
|
||||
)
|
||||
}
|
||||
catch (error) {
|
||||
console.warn(error?.message || error)
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { electronApp, optimizer } from '@electron-toolkit/utils'
|
||||
|
||||
// process.js 必须位于非依赖项的顶部
|
||||
import './helpers/process.js'
|
||||
import './helpers/store.js'
|
||||
import appStore from './helpers/store.js'
|
||||
|
||||
import log from './helpers/log.js'
|
||||
import './helpers/console.js'
|
||||
@ -15,7 +15,16 @@ import events from './events/index.js'
|
||||
|
||||
log.initialize({ preload: true })
|
||||
|
||||
console.log('Successfully initialized the Escrcpy logging system.')
|
||||
const debug = !!appStore.get('common.debug')
|
||||
|
||||
log.info('Debug Status:', debug)
|
||||
|
||||
if (!debug) {
|
||||
log.warn(
|
||||
'Debug Tips:',
|
||||
'如果需要生成并查看运行日志请在偏好设置页面启动调试功能',
|
||||
)
|
||||
}
|
||||
|
||||
// The built directory structure
|
||||
//
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/logo.ico" />
|
||||
|
@ -4,7 +4,7 @@
|
||||
<el-tab-pane
|
||||
v-for="(item, index) of tabsModel"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:label="$t(item.label)"
|
||||
:name="item.prop"
|
||||
lazy
|
||||
>
|
||||
@ -29,15 +29,15 @@ export default {
|
||||
return {
|
||||
tabsModel: [
|
||||
{
|
||||
label: this.$t('device.list'),
|
||||
label: 'device.list',
|
||||
prop: 'Device',
|
||||
},
|
||||
{
|
||||
label: this.$t('preferences.name'),
|
||||
label: 'preferences.name',
|
||||
prop: 'Preference',
|
||||
},
|
||||
{
|
||||
label: this.$t('about.name'),
|
||||
label: 'about.name',
|
||||
prop: 'About',
|
||||
},
|
||||
],
|
||||
@ -47,6 +47,7 @@ export default {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.theme.init()
|
||||
this.$store.preference.init()
|
||||
this.showTips()
|
||||
},
|
||||
|
@ -4,7 +4,7 @@
|
||||
<img src="@electron/resources/build/logo.png" class="h-48" alt="" />
|
||||
</a>
|
||||
|
||||
<div class="pt-4 text-xl text-center italic text-gray-700">
|
||||
<div class="pt-4 text-xl text-center italic text-gray-700 dark:text-white">
|
||||
{{ $t("about.description") }}
|
||||
</div>
|
||||
|
||||
@ -68,7 +68,7 @@ export default {
|
||||
})
|
||||
},
|
||||
onUpdateError() {
|
||||
this.$electron.ipcRenderer.on('update-error', async (event, ret) => {
|
||||
this.$electron.ipcRenderer.on('update-error', async (_, ret) => {
|
||||
this.loading = false
|
||||
console.log('onUpdateError.ret', ret)
|
||||
try {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="bg-primary-100 -my-[8px]">
|
||||
<div class="bg-primary-100 dark:bg-gray-800 -my-[8px]">
|
||||
<el-button
|
||||
v-for="(item, index) in controlModel"
|
||||
:key="index"
|
||||
@ -36,46 +36,45 @@ export default {
|
||||
return {
|
||||
controlModel: [
|
||||
{
|
||||
label: this.$t('device.operates.switch'),
|
||||
label: this.$t('device.control.switch'),
|
||||
elIcon: 'Switch',
|
||||
command: 'input keyevent KEYCODE_APP_SWITCH',
|
||||
},
|
||||
{
|
||||
label: this.$t('device.operates.home'),
|
||||
label: this.$t('device.control.home'),
|
||||
elIcon: 'HomeFilled',
|
||||
command: 'input keyevent KEYCODE_HOME',
|
||||
},
|
||||
{
|
||||
label: this.$t('device.operates.return'),
|
||||
label: this.$t('device.control.return'),
|
||||
elIcon: 'Back',
|
||||
command: 'input keyevent KEYCODE_BACK',
|
||||
},
|
||||
{
|
||||
label: this.$t('device.operates.notification'),
|
||||
label: this.$t('device.control.notification'),
|
||||
elIcon: 'Notification',
|
||||
command: 'cmd statusbar expand-notifications',
|
||||
tips: '打开下拉菜单选项',
|
||||
tips: this.$t('device.control.notification.tips'),
|
||||
},
|
||||
{
|
||||
label: this.$t('device.operates.power'),
|
||||
label: this.$t('device.control.power'),
|
||||
elIcon: 'SwitchButton',
|
||||
command: 'input keyevent KEYCODE_POWER',
|
||||
tips: '可以用来开启或关闭屏幕',
|
||||
tips: this.$t('device.control.power.tips'),
|
||||
},
|
||||
{
|
||||
label: this.$t('device.operates.reboot'),
|
||||
label: this.$t('device.control.reboot'),
|
||||
elIcon: 'RefreshLeft',
|
||||
command: 'reboot',
|
||||
tips: '可以用来开启或关闭屏幕',
|
||||
},
|
||||
{
|
||||
label: this.$t('device.operates.capture'),
|
||||
label: this.$t('device.control.capture'),
|
||||
elIcon: 'Crop',
|
||||
handle: this.handleScreenCap,
|
||||
tips: '',
|
||||
},
|
||||
{
|
||||
label: this.$t('device.operates.install'),
|
||||
label: this.$t('device.control.install'),
|
||||
svgIcon: 'install',
|
||||
handle: this.handleInstall,
|
||||
tips: '',
|
||||
@ -94,7 +93,12 @@ export default {
|
||||
try {
|
||||
files = await this.$electron.ipcRenderer.invoke('show-open-dialog', {
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
filters: [{ name: '请选择要安装的应用', extensions: ['apk'] }],
|
||||
filters: [
|
||||
{
|
||||
name: this.$t('device.control.install.placeholder'),
|
||||
extensions: ['apk'],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
catch (error) {
|
||||
@ -108,7 +112,9 @@ export default {
|
||||
}
|
||||
|
||||
const messageEl = this.$message({
|
||||
message: ` 正在为 ${device.$name} 安装应用中...`,
|
||||
message: this.$t('device.control.install.progress', {
|
||||
deviceName: device.$name,
|
||||
}),
|
||||
icon: LoadingIcon,
|
||||
duration: 0,
|
||||
})
|
||||
@ -131,16 +137,25 @@ export default {
|
||||
if (successCount) {
|
||||
if (totalCount > 1) {
|
||||
this.$message.success(
|
||||
`已成功将应用安装到 ${device.$name} 中,共 ${totalCount}个,成功 ${successCount}个,失败 ${failCount}个`,
|
||||
this.$t('device.control.install.success', {
|
||||
deviceName: device.$name,
|
||||
totalCount,
|
||||
successCount,
|
||||
failCount,
|
||||
}),
|
||||
)
|
||||
}
|
||||
else {
|
||||
this.$message.success(`已成功将应用安装到 ${device.$name} 中`)
|
||||
this.$message.success(
|
||||
this.$t('device.control.install.success.single', {
|
||||
deviceName: device.$name,
|
||||
}),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
this.$message.warning('安装应用失败,请检查安装包后重试')
|
||||
this.$message.warning(this.$t('device.control.install.error'))
|
||||
},
|
||||
handleClick(row) {
|
||||
if (row.command) {
|
||||
@ -155,7 +170,9 @@ export default {
|
||||
},
|
||||
async handleScreenCap(device) {
|
||||
const messageEl = this.$message({
|
||||
message: ` 正在截取 ${device.$name} 的屏幕快照...`,
|
||||
message: this.$t('device.control.capture.progress', {
|
||||
deviceName: device.$name,
|
||||
}),
|
||||
icon: LoadingIcon,
|
||||
duration: 0,
|
||||
})
|
||||
@ -183,12 +200,17 @@ export default {
|
||||
},
|
||||
async handleScreencapSuccess(savePath) {
|
||||
try {
|
||||
await this.$confirm('是否前往截屏位置进行查看?', '录制成功', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
closeOnClickModal: false,
|
||||
type: 'success',
|
||||
})
|
||||
await this.$confirm(
|
||||
this.$t('device.control.capture.success.message'),
|
||||
this.$t('device.control.capture.success.message.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,
|
||||
@ -204,4 +226,8 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style lang="postcss" scoped>
|
||||
.el-button.is-disabled {
|
||||
@apply !dark:bg-gray-800;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,39 +1,45 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="无线配对"
|
||||
:title="$t('device.wireless.pair')"
|
||||
width="600"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="text-red-500 text-sm pb-8 pl-4">
|
||||
注意:可以在 开发者选项 -> 无线调试(可以点进去) -> 使用配对码配对设备
|
||||
中获取以下信息
|
||||
{{ $t("device.wireless.pair.tips") }}
|
||||
</div>
|
||||
|
||||
<el-form ref="elForm" :model="formData" label-width="100px">
|
||||
<el-form-item
|
||||
label="配对IP地址"
|
||||
:label="$t('device.wireless.pair.address')"
|
||||
prop="host"
|
||||
:rules="[{ required: true, message: '配对码不能为空' }]"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: $t('device.wireless.pair.address.message'),
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.host"
|
||||
placeholder="请输入配对IP地址"
|
||||
:placeholder="$t('common.input.placeholder')"
|
||||
class=""
|
||||
clearable
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="配对端口号"
|
||||
:label="$t('device.wireless.pair.port')"
|
||||
prop="port"
|
||||
:rules="[{ required: true, message: '配对码不能为空' }]"
|
||||
:rules="[
|
||||
{ required: true, message: $t('device.wireless.pair.port.message') },
|
||||
]"
|
||||
>
|
||||
<el-input
|
||||
v-model.number="formData.port"
|
||||
type="number"
|
||||
placeholder="请输入配对端口号"
|
||||
:placeholder="$t('common.input.placeholder')"
|
||||
:min="0"
|
||||
clearable
|
||||
class=""
|
||||
@ -41,14 +47,16 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="配对码"
|
||||
:label="$t('device.wireless.pair.code')"
|
||||
prop="pair"
|
||||
:rules="[{ required: true, message: '配对码不能为空' }]"
|
||||
:rules="[
|
||||
{ required: true, message: $t('device.wireless.pair.code.message') },
|
||||
]"
|
||||
>
|
||||
<el-input
|
||||
v-model.number="formData.pair"
|
||||
type="number"
|
||||
placeholder="请输入配对码"
|
||||
:placeholder="$t('common.input.placeholder')"
|
||||
:min="0"
|
||||
clearable
|
||||
class=""
|
||||
@ -58,10 +66,10 @@
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">
|
||||
取消
|
||||
{{ $t("common.cancel") }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">
|
||||
确定
|
||||
{{ $t("common.confirm") }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
@ -11,9 +11,7 @@
|
||||
<el-icon>
|
||||
<EditPen />
|
||||
</el-icon>
|
||||
<span class="pl-1">{{
|
||||
device.$remark || $t("device.remark")
|
||||
}}</span>
|
||||
<span class="pl-1">{{ device.$remark || $t("device.remark") }}</span>
|
||||
</div>
|
||||
</el-tag>
|
||||
</template>
|
||||
@ -22,7 +20,7 @@
|
||||
ref="elInput"
|
||||
v-model="device.$remark"
|
||||
class=""
|
||||
placeholder="请输入备注信息"
|
||||
:placeholder="$t('common.input.placeholder')"
|
||||
clearable
|
||||
@change="onChange"
|
||||
></el-input>
|
||||
|
@ -98,7 +98,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('device.operates.name')"
|
||||
:label="$t('device.control.name')"
|
||||
width="450"
|
||||
align="left"
|
||||
>
|
||||
@ -167,7 +167,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column type="expand">
|
||||
<template #header>
|
||||
<el-icon class="" :title="$t('device.operates.more')">
|
||||
<el-icon class="" :title="$t('device.control.more')">
|
||||
<Operation class="" />
|
||||
</el-icon>
|
||||
</template>
|
||||
@ -413,8 +413,8 @@ export default {
|
||||
{
|
||||
dangerouslyUseHTMLString: true,
|
||||
closeOnClickModal: false,
|
||||
confirmButtonText: this.$t('device.wireless.connect.error.confirm'),
|
||||
cancelButtonText: this.$t('device.wireless.connect.error.cancel'),
|
||||
confirmButtonText: this.$t('device.wireless.pair'),
|
||||
cancelButtonText: this.$t('common.cancel'),
|
||||
type: 'warning',
|
||||
},
|
||||
)
|
||||
|
53
src/components/Preference/LanguageSelect/index.vue
Normal file
53
src/components/Preference/LanguageSelect/index.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-bind="data.props || {}"
|
||||
v-model="locale"
|
||||
class="!w-full"
|
||||
:title="$t(data.placeholder)"
|
||||
:placeholder="$t(data.placeholder)"
|
||||
@change="onChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in options"
|
||||
:key="index"
|
||||
:label="$t(item.label)"
|
||||
:value="item.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { i18n } from '@/locales/index.js'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
value: '',
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
emits: ['update:model-value'],
|
||||
data() {
|
||||
const { locale, availableLocales } = i18n.global
|
||||
return {
|
||||
locale,
|
||||
options: availableLocales.map(item => ({
|
||||
label: item,
|
||||
value: item,
|
||||
})),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(value) {
|
||||
this.$emit('update:model-value', value)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -65,7 +65,7 @@
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1 w-0 truncate pl-2 text-base">
|
||||
{{ item.label }}
|
||||
{{ $t(item.label) }}
|
||||
</div>
|
||||
<div class="flex-none pl-4">
|
||||
<el-button type="primary" text @click="handleReset(parentId)">
|
||||
@ -83,19 +83,19 @@
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
v-for="(item_1, index_1) of getSubModel(item)"
|
||||
v-for="(item_1, index_1) of item?.children || {}"
|
||||
:key="index_1"
|
||||
:span="12"
|
||||
:offset="0"
|
||||
>
|
||||
<el-form-item :label="item_1.label" :prop="item_1.field">
|
||||
<el-form-item :label="$t(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"
|
||||
:content="$t(item_1.tips)"
|
||||
placement="bottom"
|
||||
>
|
||||
<el-link
|
||||
@ -106,8 +106,8 @@
|
||||
>
|
||||
</el-link>
|
||||
</el-tooltip>
|
||||
<span class="" :title="item_1.placeholder">{{
|
||||
item_1.label
|
||||
<span class="" :title="$t(item_1.placeholder)">{{
|
||||
$t(item_1.label)
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
@ -117,27 +117,29 @@
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="preferenceData[item_1.field]"
|
||||
class="!w-full"
|
||||
:title="item_1.placeholder"
|
||||
:placeholder="item_1.placeholder"
|
||||
:title="$t(item_1.placeholder)"
|
||||
:placeholder="$t(item_1.placeholder)"
|
||||
clearable
|
||||
></el-input>
|
||||
|
||||
<el-input
|
||||
v-else-if="item_1.type === 'Input.number'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model.number="preferenceData[item_1.field]"
|
||||
class="!w-full"
|
||||
:title="item_1.placeholder"
|
||||
:placeholder="item_1.placeholder"
|
||||
:title="$t(item_1.placeholder)"
|
||||
:placeholder="$t(item_1.placeholder)"
|
||||
clearable
|
||||
></el-input>
|
||||
|
||||
<el-input
|
||||
v-else-if="item_1.type === 'Input.path'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="preferenceData[item_1.field]"
|
||||
class="!w-full"
|
||||
clearable
|
||||
:placeholder="item_1.placeholder"
|
||||
:title="item_1.placeholder"
|
||||
class="!w-full"
|
||||
:title="$t(item_1.placeholder)"
|
||||
:placeholder="$t(item_1.placeholder)"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
||||
@ -151,32 +153,37 @@
|
||||
/>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-switch
|
||||
v-else-if="item_1.type === 'Switch'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="preferenceData[item_1.field]"
|
||||
class="!w-full"
|
||||
clearable
|
||||
:title="item_1.placeholder"
|
||||
:title="$t(item_1.placeholder)"
|
||||
></el-switch>
|
||||
|
||||
<el-select
|
||||
v-else-if="item_1.type === 'Select'"
|
||||
v-bind="item_1.props || {}"
|
||||
v-model="preferenceData[item_1.field]"
|
||||
:placeholder="item_1.placeholder"
|
||||
class="!w-full"
|
||||
clearable
|
||||
:title="item_1.placeholder"
|
||||
:title="$t(item_1.placeholder)"
|
||||
:placeholder="$t(item_1.placeholder)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item_2, index_2) in item_1.options"
|
||||
:key="index_2"
|
||||
:label="item_2.label"
|
||||
:label="$t(item_2.label)"
|
||||
:value="item_2.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<component
|
||||
:is="item_1.type"
|
||||
v-else
|
||||
v-model="preferenceData[item_1.field]"
|
||||
:data="item_1"
|
||||
></component>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -189,23 +196,26 @@
|
||||
|
||||
<script>
|
||||
import { debounce } from 'lodash-es'
|
||||
import LanguageSelect from './LanguageSelect/index.vue'
|
||||
import { usePreferenceStore } from '@/store/index.js'
|
||||
import LoadingIcon from '@/components/Device/ControlBar/LoadingIcon/index.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LanguageSelect,
|
||||
},
|
||||
data() {
|
||||
const preferenceStore = usePreferenceStore()
|
||||
|
||||
// console.raw('preferenceStore.data', preferenceStore.data)
|
||||
// console.raw('preferenceStore.model', preferenceStore.model)
|
||||
|
||||
return {
|
||||
preferenceModel: preferenceStore.model,
|
||||
preferenceData: preferenceStore.data,
|
||||
deviceScope: preferenceStore.deviceScope,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
preferenceModel() {
|
||||
return this.$store.preference.model || {}
|
||||
},
|
||||
scopeList() {
|
||||
const value = this.$store.device.list.map(item => ({
|
||||
...item,
|
||||
@ -225,23 +235,38 @@ export default {
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
preferenceData: {
|
||||
'preferenceData': {
|
||||
handler() {
|
||||
this.handleSave()
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
deviceScope: {
|
||||
async handler(value) {
|
||||
if (value === 'global') {
|
||||
this.$store.preference.resetModel()
|
||||
'preferenceData.theme': {
|
||||
handler(value) {
|
||||
this.$store.theme.update(value)
|
||||
},
|
||||
},
|
||||
'preferenceData.language': {
|
||||
handler(value) {
|
||||
console.log('preferenceData.language.value', value)
|
||||
console.log('locale', this.locale)
|
||||
this.locale = value
|
||||
},
|
||||
},
|
||||
// 列表设备发生变化后如果没有匹配到则默认选中 global
|
||||
'scopeList': {
|
||||
handler(value) {
|
||||
const someValue = value.some(
|
||||
item => this.$replaceIP(item.value) === this.deviceScope,
|
||||
)
|
||||
|
||||
if (someValue) {
|
||||
return false
|
||||
}
|
||||
|
||||
const display = await this.$adb.display(value)
|
||||
|
||||
this.$store.preference.setModelParams('video', { display })
|
||||
this.preferenceModel = this.$store.preference.model
|
||||
this.deviceScope = 'global'
|
||||
this.$store.preference.setScope(this.deviceScope)
|
||||
this.preferenceData = this.$store.preference.data
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
@ -251,6 +276,8 @@ export default {
|
||||
leading: false,
|
||||
trailing: true,
|
||||
})
|
||||
|
||||
this.getDisplay()
|
||||
},
|
||||
methods: {
|
||||
handleResetAll() {
|
||||
@ -258,9 +285,31 @@ export default {
|
||||
this.preferenceData = this.$store.preference.data
|
||||
},
|
||||
onScopeChange(value) {
|
||||
const replaceValue = this.$replaceIP(value)
|
||||
this.$store.preference.setScope(replaceValue)
|
||||
this.$store.preference.setScope(value)
|
||||
this.preferenceData = this.$store.preference.data
|
||||
|
||||
if (value === 'global') {
|
||||
this.$store.preference.resetModel()
|
||||
}
|
||||
|
||||
this.getDisplay()
|
||||
},
|
||||
async getDisplay() {
|
||||
if (this.deviceScope === 'global') {
|
||||
return false
|
||||
}
|
||||
|
||||
const display = await this.$adb.display(this.deviceScope)
|
||||
|
||||
const displayOptions = display.map(item => ({
|
||||
label: item,
|
||||
value: item,
|
||||
}))
|
||||
|
||||
this.$store.preference.setModel(
|
||||
'video.children.display.options',
|
||||
displayOptions,
|
||||
)
|
||||
},
|
||||
async handleImport() {
|
||||
try {
|
||||
@ -289,6 +338,7 @@ export default {
|
||||
handleEdit() {
|
||||
this.$appStore.openInEditor()
|
||||
},
|
||||
|
||||
async handleExport() {
|
||||
const messageEl = this.$message({
|
||||
message: this.$t('preferences.config.export.message'),
|
||||
@ -352,7 +402,7 @@ export default {
|
||||
getSubModel(item) {
|
||||
const data = item?.children() || []
|
||||
|
||||
console.raw(`getSubModel.${item.field}.data`, data)
|
||||
console.log(`getSubModel.${item.field}.data`, data)
|
||||
|
||||
return data
|
||||
},
|
||||
|
@ -1,15 +1,20 @@
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import messages from '@intlify/unplugin-vue-i18n/messages'
|
||||
|
||||
const locale = window.electron?.process?.env?.LOCALE
|
||||
const locale
|
||||
= window.appStore.get('common.language')
|
||||
|| window.electron?.process?.env?.LOCALE
|
||||
|
||||
// const locale = 'en_US'
|
||||
|
||||
// console.log('locale', locale)
|
||||
|
||||
export const i18n = createI18n({
|
||||
allowComposition: false,
|
||||
locale,
|
||||
fallbackLocale: 'en_US',
|
||||
messages,
|
||||
})
|
||||
// console.log('i18n', i18n)
|
||||
|
||||
export const t = i18n.global.t
|
||||
|
@ -1 +1,192 @@
|
||||
{}
|
||||
{
|
||||
"zh_CN": "中文",
|
||||
"en_US": "English",
|
||||
"common.cancel": "Cancel",
|
||||
"common.confirm": "Confirm",
|
||||
"common.restart": "Restart",
|
||||
"common.tips": "Tips",
|
||||
"common.open": "Open",
|
||||
"common.input.placeholder": "Please input",
|
||||
"close.quit": "Quit",
|
||||
"close.quit.cancel": "Cancel quit",
|
||||
"close.minimize": "Minimize to tray",
|
||||
"close.message": "Are you sure you want to quit?",
|
||||
"close.remember": "Remember this choice?",
|
||||
"device.list": "Devices",
|
||||
"device.list.loading": "Loading...",
|
||||
"device.list.empty": "No devices detected",
|
||||
"device.id": "Device ID",
|
||||
"device.name": "Device Name",
|
||||
"device.remark": "Remark",
|
||||
"device.permission.error": "Device permission error, please reconnect device and allow USB debugging",
|
||||
"device.wireless.name": "Wireless",
|
||||
"device.wireless.mode": "Wireless Mode",
|
||||
"device.wireless.connect.name": "Connect",
|
||||
"device.wireless.connect.error.title": "Connect failed",
|
||||
"device.wireless.connect.error.detail": "Error details",
|
||||
"device.wireless.connect.error.reasons[0]": "Possible reasons:",
|
||||
"device.wireless.connect.error.reasons[1]": "Incorrect IP or port",
|
||||
"device.wireless.connect.error.reasons[2]": "Device not paired",
|
||||
"device.wireless.connect.error.reasons[3]": "IP not in same subnet",
|
||||
"device.wireless.connect.error.reasons[4]": "adb path error",
|
||||
"device.wireless.connect.error.reasons[5]": "Other unknown error",
|
||||
"device.wireless.connect.error.confirm": "Wireless pair",
|
||||
"device.wireless.connect.error.cancel": "@:common.cancel",
|
||||
"device.wireless.connect.error.no-address": "Wireless debug address cannot be empty",
|
||||
"device.wireless.connect.success": "Connect success",
|
||||
"device.wireless.disconnect.start": "Disconnect",
|
||||
"device.wireless.disconnect.progress": "Disconnecting",
|
||||
"device.wireless.disconnect.success": "Disconnected",
|
||||
"device.wireless.pair": "Wireless Pair",
|
||||
"device.wireless.pair.tips": "Get the following info from Developer options -> Wireless debugging -> Pair device",
|
||||
"device.wireless.pair.address": "Pair IP Address",
|
||||
"device.wireless.pair.address.message": "Pair address cannot be empty",
|
||||
"device.wireless.pair.address.placeholder": "Input pair IP address",
|
||||
"device.wireless.pair.port": "Pair Port",
|
||||
"device.wireless.pair.port.message": "Pair port cannot be empty",
|
||||
"device.wireless.pair.port.placeholder": "Input pair port",
|
||||
"device.wireless.pair.code": "Pair Code",
|
||||
"device.wireless.pair.code.message": "Pair code cannot be empty",
|
||||
"device.wireless.pair.code.placeholder": "Input pair code",
|
||||
"device.reset.title": "Operation Failed",
|
||||
"device.reset.reasons[0]": "This is usually caused by incompatible cached dependencies after updating Escrcpy. Reset dependencies?",
|
||||
"device.reset.reasons[1]": "Note: Dependencies will be cleared after reset, please backup first.",
|
||||
"device.reset.confirm": "Reset Dependencies",
|
||||
"device.reset.cancel": "@:common.cancel",
|
||||
"device.reset.success": "Success, please try again",
|
||||
"device.refresh.name": "Refresh",
|
||||
"device.restart.name": "Restart",
|
||||
"device.log.name": "Logs",
|
||||
"device.mirror.start": "Mirror",
|
||||
"device.mirror.progress": "Mirroring",
|
||||
"device.record.start": "Record",
|
||||
"device.record.progress": "Recording",
|
||||
"device.record.success.title": "Record Success",
|
||||
"device.record.success.message": "Open record location?",
|
||||
"device.control.name": "Control",
|
||||
"device.control.more": "More Controls",
|
||||
"device.control.install": "Install App",
|
||||
"device.control.install.placeholder": "Select app to install",
|
||||
"device.control.install.progress": "Installing app to {deviceName}...",
|
||||
"device.control.install.success": "Successfully installed {totalCount} apps to {deviceName}, {successCount} succeeded, {failCount} failed",
|
||||
"device.control.install.success.single": "Successfully installed app to {deviceName}",
|
||||
"device.control.install.error": "Install failed, please check app and try again",
|
||||
"device.control.capture": "Screenshot",
|
||||
"device.control.capture.progress": "Capturing screenshot for {deviceName}...",
|
||||
"device.control.capture.success.message": "Open screenshot location?",
|
||||
"device.control.capture.success.message.title": "Screenshot Success",
|
||||
"device.control.reboot": "Reboot",
|
||||
"device.control.power": "Power",
|
||||
"device.control.power.tips": "Turn screen on/off",
|
||||
"device.control.notification": "Notification",
|
||||
"device.control.notification.tips": "Open notification panel",
|
||||
"device.control.return": "Return",
|
||||
"device.control.home": "Home",
|
||||
"device.control.switch": "Switch",
|
||||
"preferences.name": "Preferences",
|
||||
"preferences.reset": "Reset to Default",
|
||||
"preferences.scope.global": "Global",
|
||||
"preferences.scope.placeholder": "Preference scope",
|
||||
"preferences.scope.no-data": "No data",
|
||||
"preferences.scope.details[0]": "Set global or per-device preferences",
|
||||
"preferences.scope.details[1]": "Global: Apply to all devices",
|
||||
"preferences.scope.details[2]": "Per-device: Override global settings for one device",
|
||||
"preferences.config.import.name": "Import",
|
||||
"preferences.config.import.placeholder": "Select config file",
|
||||
"preferences.config.import.success": "Import success",
|
||||
"preferences.config.export.name": "Export",
|
||||
"preferences.config.export.message": "Export config",
|
||||
"preferences.config.export.placeholder": "Select export location",
|
||||
"preferences.config.export.success": "Export success",
|
||||
"preferences.config.edit.name": "Edit",
|
||||
"preferences.config.reset.name": "Reset",
|
||||
"preferences.config.save.placeholder": "Config saved",
|
||||
"preferences.common.name": "Common",
|
||||
"preferences.common.theme.name": "Theme",
|
||||
"preferences.common.theme.placeholder": "Set theme",
|
||||
"preferences.common.theme.options[0]": "Light Mode",
|
||||
"preferences.common.theme.options[1]": "Dark Mode",
|
||||
"preferences.common.theme.options[2]": "System Default",
|
||||
"preferences.common.debug.name": "Debug",
|
||||
"preferences.common.debug.placeholder": "Enable debug mode",
|
||||
"preferences.common.debug.tips": "Show debug info in log, disable to improve performance. Restart to take effect.",
|
||||
"preferences.common.file.name": "File Location",
|
||||
"preferences.common.file.placeholder": "Default is user's Desktop",
|
||||
"preferences.common.file.tips": "Location to save screenshots and recordings",
|
||||
"preferences.common.adb.name": "Adb Path",
|
||||
"preferences.common.adb.placeholder": "Set adb path",
|
||||
"preferences.common.adb.tips": "adb path to connect device",
|
||||
"preferences.common.scrcpy.name": "Scrcpy Path",
|
||||
"preferences.common.scrcpy.placeholder": "Set scrcpy path",
|
||||
"preferences.common.scrcpy.tips": "scrcpy path to connect device",
|
||||
"preferences.common.language.name": "Language",
|
||||
"preferences.common.language.placeholder": "Select language",
|
||||
"preferences.common.language.chinese": "中文",
|
||||
"preferences.common.language.english": "English",
|
||||
"preferences.video.name": "Video",
|
||||
"preferences.video.resolution.name": "Resolution",
|
||||
"preferences.video.resolution.placeholder": "Default is device resolution e.g. 1920",
|
||||
"preferences.video.bit.name": "Bit Rate",
|
||||
"preferences.video.bit.placeholder": "Default 4M, equal to 4000000",
|
||||
"preferences.video.refresh-rate.name": "Frame Rate",
|
||||
"preferences.video.refresh-rate.placeholder": "Default 60",
|
||||
"preferences.video.decoder.name": "Video Decoder",
|
||||
"preferences.video.decoder.placeholder": "Default h264",
|
||||
"preferences.video.encoder.name": "Video Encoder",
|
||||
"preferences.video.encoder.placeholder": "Default device encoder",
|
||||
"preferences.video.screen-rotation.name": "Rotation",
|
||||
"preferences.video.screen-rotation.placeholder": "Default device rotation",
|
||||
"preferences.video.screen-cropping.name": "Crop",
|
||||
"preferences.video.screen-cropping.placeholder": "Default no crop, format is 1224:1440:0:0",
|
||||
"preferences.video.multi-display.name": "Display",
|
||||
"preferences.video.multi-display.placeholder": "Default 0 (main display)",
|
||||
"preferences.video.video-buffering.name": "Video Buffering",
|
||||
"preferences.video.video-buffering.placeholder": "Default 0ms",
|
||||
"preferences.video.audio-buffering.name": "Audio Buffering",
|
||||
"preferences.video.audio-buffering.placeholder": "Default 0ms",
|
||||
"preferences.video.receiver-buffering.name": "Receiver Buffering (v412)",
|
||||
"preferences.video.receiver-buffering.placeholder": "Default 0ms",
|
||||
"preferences.video.disable.name": "Disable Video",
|
||||
"preferences.video.disable.placeholder": "Disable video stream",
|
||||
"preferences.device.name": "Device",
|
||||
"preferences.device.show-touch.name": "Show Touches",
|
||||
"preferences.device.show-touch.placeholder": "Enable touch feedback dots",
|
||||
"preferences.device.show-touch.tips": "Physical device only",
|
||||
"preferences.device.stay-awake.name": "Stay Awake",
|
||||
"preferences.device.stay-awake.placeholder": "Prevent device sleep",
|
||||
"preferences.device.stay-awake.tips": "Wired only",
|
||||
"preferences.device.control-in-close-screen.name": "Turn Off Screen",
|
||||
"preferences.device.control-in-close-screen.placeholder": "Turn off device screen when controlling",
|
||||
"preferences.device.control-end-video.name": "Turn Off at End",
|
||||
"preferences.device.control-end-video.placeholder": "Turn off screen when control ends",
|
||||
"preferences.device.control-in-stop-charging.name": "Stop Charging",
|
||||
"preferences.device.control-in-stop-charging.placeholder": "Stop charging when controlling",
|
||||
"preferences.device.control-in-stop-charging.tips": "May not work on some models",
|
||||
"preferences.window.name": "Window",
|
||||
"preferences.window.borderless.name": "Borderless",
|
||||
"preferences.window.borderless.placeholder": "Borderless control window",
|
||||
"preferences.window.full-screen.name": "Fullscreen",
|
||||
"preferences.window.full-screen.placeholder": "Fullscreen control window",
|
||||
"preferences.window.always-top.name": "Always on Top",
|
||||
"preferences.window.always-top.placeholder": "Keep control window topmost",
|
||||
"preferences.window.disable-screen-saver.name": "Disable Screensaver",
|
||||
"preferences.window.disable-screen-saver.placeholder": "Disable computer screensaver",
|
||||
"preferences.record.name": "Recording",
|
||||
"preferences.record.format.name": "Format",
|
||||
"preferences.record.format.placeholder": "Default *.mp4",
|
||||
"preferences.audio.name": "Audio",
|
||||
"preferences.audio.disable.name": "Disable Audio",
|
||||
"preferences.audio.disable.placeholder": "Disable audio stream",
|
||||
"about.name": "About",
|
||||
"about.description": "📱 Graphical Scrcpy to display and control Android, devices powered by Electron.",
|
||||
"about.update": "Check for Updates",
|
||||
"about.update-not-available": "Already latest version",
|
||||
"about.update-error.title": "Update check failed",
|
||||
"about.update-error.message": "You may need a proxy. Download manually from releases page?",
|
||||
"about.update-downloaded.title": "New version downloaded",
|
||||
"about.update-downloaded.message": "Restart to update now?",
|
||||
"about.update-downloaded.confirm": "Update",
|
||||
"about.update-available.title": "Update Available",
|
||||
"about.update-available.confirm": "Update",
|
||||
"about.update.progress": "Updating..."
|
||||
}
|
||||
|
@ -1,6 +1,17 @@
|
||||
{
|
||||
"zh_CN": "中文",
|
||||
"en_US": "English",
|
||||
"common.cancel": "取消",
|
||||
"common.confirm": "确认",
|
||||
"common.restart": "重启",
|
||||
"common.tips": "提示",
|
||||
"common.open": "打开",
|
||||
"common.input.placeholder": "请填写",
|
||||
"close.quit": "退出",
|
||||
"close.quit.cancel": "取消退出",
|
||||
"close.minimize": "最小化到托盘",
|
||||
"close.message": "确定要退出吗?",
|
||||
"close.remember": "是否记住选择?",
|
||||
"device.list": "设备列表",
|
||||
"device.list.loading": "努力加载中...",
|
||||
"device.list.empty": "没有检测到设备",
|
||||
@ -26,6 +37,17 @@
|
||||
"device.wireless.disconnect.start": "断开连接",
|
||||
"device.wireless.disconnect.progress": "正在断开",
|
||||
"device.wireless.disconnect.success": "断开连接成功",
|
||||
"device.wireless.pair": "无线配对",
|
||||
"device.wireless.pair.tips": "注意:可以在 开发者选项 -> 无线调试(可以点进去) -> 使用配对码配对设备中获取以下信息",
|
||||
"device.wireless.pair.address": "配对IP地址",
|
||||
"device.wireless.pair.address.message": "配对码不能为空",
|
||||
"device.wireless.pair.address.placeholder": "请输入配对IP地址",
|
||||
"device.wireless.pair.port": "配对端口号",
|
||||
"device.wireless.pair.port.message": "配对端口号不能为空",
|
||||
"device.wireless.pair.port.placeholder": "请输入配对端口号",
|
||||
"device.wireless.pair.code": "配对码",
|
||||
"device.wireless.pair.code.message": "配对码不能为空",
|
||||
"device.wireless.pair.code.placeholder": "请输入配对码",
|
||||
"device.reset.title": "操作失败",
|
||||
"device.reset.reasons[0]": "通常情况下,这可能是因为更新 Escrcpy 后,缓存的依赖配置不兼容所导致的,是否重置依赖配置?",
|
||||
"device.reset.reasons[1]": "注意:重置后,之前保存的依赖配置将会被清除,因此建议在执行重置操作之前备份您的配置。",
|
||||
@ -41,16 +63,26 @@
|
||||
"device.record.progress": "正在录制",
|
||||
"device.record.success.title": "录制成功",
|
||||
"device.record.success.message": "是否前往录制位置进行查看?",
|
||||
"device.operates.name": "操作",
|
||||
"device.operates.more": "设备交互",
|
||||
"device.operates.install": "安装应用",
|
||||
"device.operates.capture": "截取屏幕",
|
||||
"device.operates.reboot": "重启设备",
|
||||
"device.operates.power": "电源键",
|
||||
"device.operates.notification": "通知栏",
|
||||
"device.operates.return": "切换键",
|
||||
"device.operates.home": "切换键",
|
||||
"device.operates.switch": "切换键",
|
||||
"device.control.name": "操作",
|
||||
"device.control.more": "设备交互",
|
||||
"device.control.install": "安装应用",
|
||||
"device.control.install.placeholder": "请选择要安装的应用",
|
||||
"device.control.install.progress": "正在为 {deviceName} 安装应用中...",
|
||||
"device.control.install.success": "已成功将应用安装到 {deviceName} 中,共 {totalCount}个,成功 {successCount}个,失败 {failCount}个",
|
||||
"device.control.install.success.single": "已成功将应用安装到 {deviceName} 中",
|
||||
"device.control.install.error": "安装应用失败,请检查安装包后重试",
|
||||
"device.control.capture": "截取屏幕",
|
||||
"device.control.capture.progress": "正在截取 {deviceName} 的屏幕快照...",
|
||||
"device.control.capture.success.message": "是否前往截屏位置进行查看?",
|
||||
"device.control.capture.success.message.title": "截屏成功",
|
||||
"device.control.reboot": "重启设备",
|
||||
"device.control.power": "电源键",
|
||||
"device.control.power.tips": "可以用来开启或关闭屏幕",
|
||||
"device.control.notification": "通知栏",
|
||||
"device.control.notification.tips": "打开下拉菜单选项",
|
||||
"device.control.return": "返回键",
|
||||
"device.control.home": "主屏幕",
|
||||
"device.control.switch": "切换键",
|
||||
"preferences.name": "偏好设置",
|
||||
"preferences.reset": "恢复默认值",
|
||||
"preferences.scope.global": "全局",
|
||||
@ -68,55 +100,54 @@
|
||||
"preferences.config.export.success": "导出成功",
|
||||
"preferences.config.edit.name": "编辑",
|
||||
"preferences.config.reset.name": "重置",
|
||||
"preferences.config.save.name": "保存配置",
|
||||
"preferences.config.save.placeholder": "保存配置成功,将在下一次控制设备时生效",
|
||||
"preferences.common.name": "通用",
|
||||
"preferences.config.save.placeholder": "自动保存配置成功",
|
||||
"preferences.common.name": "通用设置",
|
||||
"preferences.common.theme.name": "主题",
|
||||
"preferences.common.theme.placeholder": "设置主题",
|
||||
"preferences.common.theme.options[0]": "浅色模式",
|
||||
"preferences.common.theme.options[1]": "深色模式",
|
||||
"preferences.common.theme.options[2]": "系统自适应",
|
||||
"preferences.common.debug.name": "调试",
|
||||
"preferences.common.debug.placeholder": "是否启动软件调试",
|
||||
"preferences.common.debug.tips": "启用后可以在运行日志中查看软件运行情况,一般不需要开启可能会影响性能,注意: 改变此选项需要重启软件后才能生效。",
|
||||
"preferences.common.file.name": "文件存储路径",
|
||||
"preferences.common.file.placeholder": "默认情况下放在用户桌面上",
|
||||
"preferences.common.file.tips": "截图和录制的音视频存放在这里",
|
||||
"preferences.common.adb.name": "adb 路径",
|
||||
"preferences.common.adb.placeholder": "请设置 adb 路径",
|
||||
"preferences.common.adb.tips": "用于连接设备的 adb 地址。注意:此选项不受单个设备配置的影响",
|
||||
"preferences.common.adb.tips": "用于连接设备的 adb 地址。",
|
||||
"preferences.common.scrcpy.name": "scrcpy 路径",
|
||||
"preferences.common.scrcpy.placeholder": "请设置 scrcpy 路径",
|
||||
"preferences.common.scrcpy.tips": "用于连接设备的 scrcpy 地址。注意:此选项不受单个设备配置的影响",
|
||||
"preferences.common.scrcpy.tips": "用于连接设备的 scrcpy 地址。",
|
||||
"preferences.common.language.name": "语言",
|
||||
"preferences.common.language.placeholder": "选择你需要的语言",
|
||||
"preferences.common.language.chinese": "中文",
|
||||
"preferences.common.language.english": "English",
|
||||
"preferences.video.name": "视频控制",
|
||||
"preferences.video.resolution.name": "分辨率",
|
||||
"preferences.video.resolution.placeholder": "默认值为设备分辨率,如 1920",
|
||||
"preferences.video.resolution.tips": "",
|
||||
"preferences.video.bit.name": "比特率",
|
||||
"preferences.video.bit.placeholder": "默认值为 4M,等同于 4000000",
|
||||
"preferences.video.bit.tips": "",
|
||||
"preferences.video.refresh-rate.name": "刷新率",
|
||||
"preferences.video.refresh-rate.placeholder": "默认值为 60",
|
||||
"preferences.video.refresh-rate.tips": "",
|
||||
"preferences.video.decoder.name": "视频解码器",
|
||||
"preferences.video.decoder.placeholder": "默认值为 h264",
|
||||
"preferences.video.decoder.tips": "",
|
||||
"preferences.video.encoder.name": "视频编码器",
|
||||
"preferences.video.encoder.placeholder": "默认值为设备默认编码器",
|
||||
"preferences.video.encoder.tips": "",
|
||||
"preferences.video.screen-rotation.name": "屏幕旋转",
|
||||
"preferences.video.screen-rotation.placeholder": "默认值为设备屏幕旋转角度",
|
||||
"preferences.video.screen-rotation.tips": "",
|
||||
"preferences.video.screen-cropping.name": "屏幕裁剪",
|
||||
"preferences.video.screen-cropping.placeholder": "默认不裁剪,格式为 1224:1440:0:0",
|
||||
"preferences.video.screen-cropping.tips": "",
|
||||
"preferences.video.multi-display.name": "多显示器",
|
||||
"preferences.video.multi-display.placeholder": "默认值为 0(主屏幕)",
|
||||
"preferences.video.multi-display.tips": "",
|
||||
"preferences.video.video-buffering.name": "视频缓冲",
|
||||
"preferences.video.video-buffering.placeholder": "默认值为 0ms",
|
||||
"preferences.video.video-buffering.tips": "",
|
||||
"preferences.video.audio-buffering.name": "音频缓冲",
|
||||
"preferences.video.audio-buffering.placeholder": "默认值为 0ms",
|
||||
"preferences.video.audio-buffering.tips": "",
|
||||
"preferences.video.receiver-buffering.name": "接收器缓冲(v412)",
|
||||
"preferences.video.receiver-buffering.placeholder": "默认值为 0ms",
|
||||
"preferences.video.receiver-buffering.tips": "",
|
||||
"preferences.video.disable.name": "禁用视频",
|
||||
"preferences.video.disable.placeholder": "开启后将禁用视频",
|
||||
"preferences.video.disable.tips": "",
|
||||
"preferences.device.name": "设备控制",
|
||||
"preferences.device.show-touch.name": "展示触摸点",
|
||||
"preferences.device.show-touch.placeholder": "开启后将打开开发者选项中的显示点按触摸反馈",
|
||||
@ -126,34 +157,26 @@
|
||||
"preferences.device.stay-awake.tips": "仅有线方式连接时有效",
|
||||
"preferences.device.control-in-close-screen.name": "控制时关闭屏幕",
|
||||
"preferences.device.control-in-close-screen.placeholder": "开启后控制设备时将自动关闭设备屏幕",
|
||||
"preferences.device.control-in-close-screen.tips": "",
|
||||
"preferences.device.control-end-video.name": "控制结束关闭屏幕",
|
||||
"preferences.device.control-end-video.placeholder": "开启后停止控制设备将自动关闭设备屏幕",
|
||||
"preferences.device.control-end-video.tips": "",
|
||||
"preferences.device.control-in-stop-charging.name": "控制时停止充电",
|
||||
"preferences.device.control-in-stop-charging.placeholder": "开启后控制设备时将停止充电",
|
||||
"preferences.device.control-in-stop-charging.tips": "某些机型上似乎不起作用",
|
||||
"preferences.window.name": "窗口控制",
|
||||
"preferences.window.borderless.name": "无边框模式",
|
||||
"preferences.window.borderless.placeholder": "开启后控制窗口将变为无边框模式",
|
||||
"preferences.window.borderless.tips": "",
|
||||
"preferences.window.full-screen.name": "全屏模式",
|
||||
"preferences.window.full-screen.placeholder": "开启后控制窗口将全屏显示模式",
|
||||
"preferences.window.full-screen.tips": "",
|
||||
"preferences.window.always-top.name": "始终位于顶部",
|
||||
"preferences.window.always-top.placeholder": "开启后控制窗口将始终位于顶部",
|
||||
"preferences.window.always-top.tips": "",
|
||||
"preferences.window.disable-screen-saver.name": "禁用屏幕保护程序",
|
||||
"preferences.window.disable-screen-saver.placeholder": "开启后将禁用计算机屏幕保护程序",
|
||||
"preferences.window.disable-screen-saver.tips": "",
|
||||
"preferences.record.name": "音视频录制",
|
||||
"preferences.record.format.name": "录制视频格式",
|
||||
"preferences.record.format.placeholder": "默认为 *.mp4 格式",
|
||||
"preferences.record.format.tips": "",
|
||||
"preferences.audio.name": "音频控制",
|
||||
"preferences.audio.disable.name": "禁用音频",
|
||||
"preferences.audio.disable.placeholder": "开启后将禁用音频功能",
|
||||
"preferences.audio.disable.tips": "",
|
||||
"about.name": "关于",
|
||||
"about.description": "📱 使用图形化的 Scrcpy 显示和控制您的 Android 设备,由 Electron 驱动",
|
||||
"about.update": "检查并更新",
|
||||
|
@ -8,7 +8,7 @@ import store from './store/index.js'
|
||||
import plugins from './plugins/index.js'
|
||||
import icons from './icons/index.js'
|
||||
|
||||
import { i18n } from './locales/index.js'
|
||||
import { i18n, t } from './locales/index.js'
|
||||
|
||||
import { replaceIP } from '@/utils/index.js'
|
||||
|
||||
@ -24,13 +24,14 @@ app.use(plugins)
|
||||
app.use(icons)
|
||||
|
||||
app.use(i18n)
|
||||
window.t = t
|
||||
|
||||
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.config.globalProperties.$appStore = window.appStore
|
||||
app.config.globalProperties.$appLog = window.appLog
|
||||
|
||||
app.config.globalProperties.$replaceIP = replaceIP
|
||||
|
@ -1,5 +1,6 @@
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
import './restyle.css'
|
||||
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
|
@ -1,7 +1,11 @@
|
||||
:root {
|
||||
/* 主题色 */
|
||||
:root,
|
||||
:root.dark {
|
||||
/* --el-font-size-base: 14px;
|
||||
--el-font-size-small: 16px;
|
||||
--el-font-size-large: 18px; */
|
||||
|
||||
--el-color-primary: rgba(var(--color-primary), 1);
|
||||
--el-color-primary-dark-2: rgba(var(--color-primary-60), 1);
|
||||
--el-color-primary-dark-2: rgba(var(--color-primary-600), 1);
|
||||
--el-color-primary-light-1: rgba(var(--color-primary-400), 1);
|
||||
--el-color-primary-light-2: rgba(var(--color-primary-400), 1);
|
||||
--el-color-primary-light-3: rgba(var(--color-primary-300), 1);
|
||||
@ -11,11 +15,10 @@
|
||||
--el-color-primary-light-7: rgba(var(--color-primary-100), 1);
|
||||
--el-color-primary-light-8: rgba(var(--color-primary-100), 1);
|
||||
--el-color-primary-light-9: rgba(var(--color-primary-50), 1);
|
||||
}
|
||||
|
||||
/* 字体大小 */
|
||||
/* --el-font-size-base: 14px;
|
||||
--el-font-size-small: 16px;
|
||||
--el-font-size-large: 18px; */
|
||||
:root.dark.dark {
|
||||
--el-color-primary: rgba(var(--color-primary-400), 1);
|
||||
}
|
||||
|
||||
.el-tabs-flex {
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { createPinia } from 'pinia'
|
||||
import { useDeviceStore } from './device/index.js'
|
||||
import { usePreferenceStore } from './preference/index.js'
|
||||
import { useThemeStore } from './theme/index.js'
|
||||
|
||||
export { useDeviceStore, usePreferenceStore }
|
||||
export { useDeviceStore, usePreferenceStore, useThemeStore }
|
||||
|
||||
export default {
|
||||
install(app) {
|
||||
@ -13,6 +14,7 @@ export default {
|
||||
app.config.globalProperties.$store = {
|
||||
device: useDeviceStore(),
|
||||
preference: usePreferenceStore(),
|
||||
theme: useThemeStore(),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -1,19 +1,20 @@
|
||||
import { cloneDeep, keyBy, mergeWith, uniq } from 'lodash-es'
|
||||
import model from '../model/index.js'
|
||||
|
||||
export function getModelFields(data = model) {
|
||||
export function getTopFields(data = model) {
|
||||
return uniq(Object.values(data).map(item => item.field))
|
||||
}
|
||||
|
||||
const modelFields = getModelFields()
|
||||
const topFields = getTopFields()
|
||||
|
||||
export function getModelMap(data = model) {
|
||||
const value = Object.entries(data).reduce((obj, [key, item]) => {
|
||||
const value = Object.entries(data).reduce((obj, [parentId, parentItem]) => {
|
||||
const children
|
||||
= item?.children()?.map(item_1 => ({
|
||||
...item_1,
|
||||
parentField: item.field,
|
||||
parentId: key,
|
||||
= Object.entries(parentItem?.children || {})?.map(([id, item]) => ({
|
||||
...item,
|
||||
parentField: parentItem.field,
|
||||
parentId,
|
||||
id,
|
||||
})) || []
|
||||
|
||||
const subData = keyBy(children, 'field')
|
||||
@ -26,7 +27,7 @@ export function getModelMap(data = model) {
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
// console.raw('getModelMap.value', value)
|
||||
// console.log('getModelMap.value', value)
|
||||
|
||||
return value
|
||||
}
|
||||
@ -41,7 +42,7 @@ export function getDefaultData(parentId) {
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
// console.raw('getDefaultData.value', value)
|
||||
// console.log('getDefaultData.value', value)
|
||||
|
||||
return value
|
||||
}
|
||||
@ -49,7 +50,7 @@ export function getDefaultData(parentId) {
|
||||
export const getStoreData = (scope) => {
|
||||
const value = {}
|
||||
|
||||
modelFields.forEach((key) => {
|
||||
topFields.forEach((key) => {
|
||||
const storeValue = window.appStore.get(key) || {}
|
||||
if (key === 'scrcpy') {
|
||||
Object.assign(value, storeValue[scope || 'global'])
|
||||
@ -59,7 +60,7 @@ export const getStoreData = (scope) => {
|
||||
Object.assign(value, storeValue)
|
||||
})
|
||||
|
||||
// console.raw('getStoreData.value', value)
|
||||
// console.log('getStoreData.value', value)
|
||||
|
||||
return value
|
||||
}
|
||||
@ -67,7 +68,7 @@ export const getStoreData = (scope) => {
|
||||
export function setStoreData(data, scope) {
|
||||
const modelMap = getModelMap()
|
||||
|
||||
const fieldModel = modelFields.reduce((obj, key) => {
|
||||
const storeModel = topFields.reduce((obj, key) => {
|
||||
obj[key] = {}
|
||||
return obj
|
||||
}, {})
|
||||
@ -75,10 +76,10 @@ export function setStoreData(data, scope) {
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
const { parentField } = modelMap[key]
|
||||
|
||||
fieldModel[parentField][key] = value
|
||||
storeModel[parentField][key] = value
|
||||
})
|
||||
|
||||
const fieldList = Object.entries(fieldModel).reduce((arr, [field, value]) => {
|
||||
const storeList = Object.entries(storeModel).reduce((arr, [field, value]) => {
|
||||
arr.push({
|
||||
field: field === 'scrcpy' ? `scrcpy.${scope}` : field,
|
||||
value,
|
||||
@ -86,9 +87,9 @@ export function setStoreData(data, scope) {
|
||||
return arr
|
||||
}, [])
|
||||
|
||||
// console.raw('setStoreData.fieldList', fieldList)
|
||||
// console.log('setStoreData.storeList', storeList)
|
||||
|
||||
fieldList.forEach((item) => {
|
||||
storeList.forEach((item) => {
|
||||
window.appStore.set(item.field, item.value)
|
||||
})
|
||||
}
|
||||
@ -105,7 +106,9 @@ export function mergeConfig(object, sources, { debug = false } = {}) {
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
console.raw(key, value)
|
||||
console.log(`srcValue.${key}`, srcValue)
|
||||
console.log(`objValue.${key}`, objValue)
|
||||
console.log(key, value)
|
||||
}
|
||||
|
||||
return value
|
||||
@ -118,9 +121,9 @@ export function mergeConfig(object, sources, { debug = false } = {}) {
|
||||
|
||||
export const getOtherFields = (excludeKey = '') => {
|
||||
const modelMap = getModelMap()
|
||||
const value = Object.entries(modelMap).reduce((arr, [key, data]) => {
|
||||
if (data.parentField !== excludeKey) {
|
||||
arr.push(data.field)
|
||||
const value = Object.values(modelMap).reduce((arr, item) => {
|
||||
if (item.parentField !== excludeKey) {
|
||||
arr.push(item.field)
|
||||
}
|
||||
return arr
|
||||
}, [])
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { cloneDeep, get, set } from 'lodash-es'
|
||||
import model from './model/index.js'
|
||||
|
||||
import {
|
||||
getDefaultData,
|
||||
getModelFields,
|
||||
getOtherFields,
|
||||
getStoreData,
|
||||
getTopFields,
|
||||
mergeConfig,
|
||||
setStoreData,
|
||||
} from './helpers/index.js'
|
||||
@ -26,15 +26,20 @@ export const usePreferenceStore = defineStore({
|
||||
scrcpyExcludeKeys: ['--record-format', ...getOtherFields('scrcpy')],
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
modelList() {
|
||||
return Object.values(this.model)
|
||||
},
|
||||
},
|
||||
getters: {},
|
||||
actions: {
|
||||
getDefaultData,
|
||||
init(scope = this.deviceScope) {
|
||||
const data = mergeConfig(getDefaultData(), getStoreData(scope))
|
||||
const globalData = mergeConfig(getDefaultData(), getStoreData())
|
||||
|
||||
let data = {}
|
||||
|
||||
if (scope === 'global') {
|
||||
data = globalData
|
||||
}
|
||||
else {
|
||||
data = mergeConfig(globalData, getStoreData(replaceIP(scope)))
|
||||
}
|
||||
|
||||
this.data = data
|
||||
|
||||
@ -59,7 +64,7 @@ export const usePreferenceStore = defineStore({
|
||||
delete cloneData.scrcpyPath
|
||||
}
|
||||
|
||||
setStoreData(cloneData, scope)
|
||||
setStoreData(cloneData, replaceIP(scope))
|
||||
|
||||
this.init(scope)
|
||||
},
|
||||
@ -68,7 +73,7 @@ export const usePreferenceStore = defineStore({
|
||||
window.appStore.reset()
|
||||
}
|
||||
else {
|
||||
const fields = getModelFields()
|
||||
const fields = getTopFields()
|
||||
|
||||
fields.forEach((key) => {
|
||||
if (key === 'scrcpy') {
|
||||
@ -97,11 +102,12 @@ export const usePreferenceStore = defineStore({
|
||||
}
|
||||
this.init()
|
||||
},
|
||||
getData(scope = this.scope) {
|
||||
getData(scope = this.deviceScope) {
|
||||
const value = this.init(scope)
|
||||
return value
|
||||
},
|
||||
getScrcpyData(scope = this.scope) {
|
||||
|
||||
getScrcpyData(scope = this.deviceScope) {
|
||||
const data = this.getData(scope)
|
||||
|
||||
if (!data) {
|
||||
@ -133,45 +139,24 @@ export const usePreferenceStore = defineStore({
|
||||
|
||||
return value
|
||||
},
|
||||
getModel(key, params) {
|
||||
const handler = this.model[key]
|
||||
const value = handler(params)
|
||||
// console.log('setModel.value', value)
|
||||
getModel(path) {
|
||||
const value = get(this.model, path)
|
||||
// console.log('getModel.value', value)
|
||||
|
||||
return value
|
||||
},
|
||||
setModelParams(key, ...args) {
|
||||
const handler = this.model[key]?.children
|
||||
|
||||
if (!handler) {
|
||||
return false
|
||||
}
|
||||
|
||||
const value = handler(...args)
|
||||
|
||||
console.raw('setModelParams.value', value)
|
||||
|
||||
this.model[key].children = () => value
|
||||
setModel(path, value) {
|
||||
set(this.model, path, value)
|
||||
|
||||
return this.model
|
||||
},
|
||||
resetModel(key) {
|
||||
const keys = []
|
||||
if (key) {
|
||||
keys.push(key)
|
||||
}
|
||||
else {
|
||||
keys.push(...Object.keys(model))
|
||||
resetModel(path) {
|
||||
if (!path) {
|
||||
this.model = cloneDeep(model)
|
||||
return true
|
||||
}
|
||||
|
||||
keys.forEach((value) => {
|
||||
if (!this.model?.[value]?.children) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.model[value].children = (...args) =>
|
||||
model[value].children(...args)
|
||||
})
|
||||
set(this.model, path, cloneDeep(get(model, path)))
|
||||
|
||||
return true
|
||||
},
|
||||
|
@ -1,22 +1,18 @@
|
||||
import { t } from '@/locales/index.js'
|
||||
|
||||
export default {
|
||||
label: t('preferences.audio.name'),
|
||||
label: 'preferences.audio.name',
|
||||
field: 'scrcpy',
|
||||
|
||||
children: () => {
|
||||
// "[server] INFO: List of audio encoders:"
|
||||
// "--audio-codec=opus --audio-encoder='c2.android.opus.encoder'"
|
||||
// "--audio-codec=aac --audio-encoder='c2.android.aac.encoder'"
|
||||
// "--audio-codec=aac --audio-encoder='OMX.google.aac.encoder'"
|
||||
return [
|
||||
{
|
||||
label: t('preferences.audio.disable.name'),
|
||||
field: '--no-audio',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t('preferences.audio.disable.placeholder'),
|
||||
},
|
||||
]
|
||||
children: {
|
||||
noAudio: {
|
||||
// "[server] INFO: List of audio encoders:"
|
||||
// "--audio-codec=opus --audio-encoder='c2.android.opus.encoder'"
|
||||
// "--audio-codec=aac --audio-encoder='c2.android.aac.encoder'"
|
||||
// "--audio-codec=aac --audio-encoder='OMX.google.aac.encoder'"
|
||||
label: 'preferences.audio.disable.name',
|
||||
field: '--no-audio',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.audio.disable.placeholder',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,47 +1,88 @@
|
||||
import { t } from '@/locales/index.js'
|
||||
const { adbPath, scrcpyPath, desktopPath } = window?.electron?.configs || {}
|
||||
|
||||
const defaultLanguage = window.electron?.process?.env?.LOCALE
|
||||
|
||||
export default {
|
||||
label: t('preferences.common.name'),
|
||||
label: 'preferences.common.name',
|
||||
field: 'common',
|
||||
|
||||
children: () => {
|
||||
const { adbPath, scrcpyPath, desktopPath }
|
||||
= window?.electron?.configs || {}
|
||||
|
||||
return [
|
||||
{
|
||||
label: t('preferences.common.file.name'),
|
||||
field: 'savePath',
|
||||
type: 'Input.path',
|
||||
value: desktopPath,
|
||||
placeholder: t('preferences.common.file.placeholder'),
|
||||
tips: t('preferences.common.file.tips'),
|
||||
properties: ['openDirectory'],
|
||||
},
|
||||
{
|
||||
label: t('preferences.common.adb.name'),
|
||||
field: 'adbPath',
|
||||
value: adbPath,
|
||||
type: 'Input.path',
|
||||
placeholder: t('preferences.common.adb.placeholder'),
|
||||
tips: t('preferences.common.adb.tips'),
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: t('preferences.common.adb.name'), extensions: ['*'] },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('preferences.common.scrcpy.name'),
|
||||
field: 'scrcpyPath',
|
||||
value: scrcpyPath,
|
||||
type: 'Input.path',
|
||||
placeholder: t('preferences.common.scrcpy.placeholder'),
|
||||
tips: t('preferences.common.scrcpy.tips'),
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: t('preferences.common.scrcpy.name'), extensions: ['*'] },
|
||||
],
|
||||
},
|
||||
]
|
||||
children: {
|
||||
theme: {
|
||||
label: 'preferences.common.theme.name',
|
||||
field: 'theme',
|
||||
type: 'Select',
|
||||
value: 'system',
|
||||
placeholder: 'preferences.common.theme.placeholder',
|
||||
tips: '',
|
||||
options: [
|
||||
{
|
||||
label: 'preferences.common.theme.options[0]',
|
||||
value: 'light',
|
||||
},
|
||||
{
|
||||
label: 'preferences.common.theme.options[1]',
|
||||
value: 'dark',
|
||||
},
|
||||
{
|
||||
label: 'preferences.common.theme.options[2]',
|
||||
value: 'system',
|
||||
},
|
||||
],
|
||||
},
|
||||
language: {
|
||||
label: 'preferences.common.language.name',
|
||||
field: 'language',
|
||||
type: 'LanguageSelect',
|
||||
value: defaultLanguage,
|
||||
placeholder: 'preferences.common.language.placeholder',
|
||||
tips: '',
|
||||
options: [
|
||||
{
|
||||
label: 'preferences.common.language.chinese',
|
||||
value: 'zh_CN',
|
||||
},
|
||||
{
|
||||
label: 'preferences.common.language.english',
|
||||
value: 'en_US',
|
||||
},
|
||||
],
|
||||
},
|
||||
savePath: {
|
||||
label: 'preferences.common.file.name',
|
||||
field: 'savePath',
|
||||
type: 'Input.path',
|
||||
value: desktopPath,
|
||||
placeholder: 'preferences.common.file.placeholder',
|
||||
tips: 'preferences.common.file.tips',
|
||||
properties: ['openDirectory'],
|
||||
},
|
||||
adbPath: {
|
||||
label: 'preferences.common.adb.name',
|
||||
field: 'adbPath',
|
||||
value: adbPath,
|
||||
type: 'Input.path',
|
||||
placeholder: 'preferences.common.adb.placeholder',
|
||||
tips: 'preferences.common.adb.tips',
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'preferences.common.adb.name', extensions: ['*'] }],
|
||||
},
|
||||
scrcpyPath: {
|
||||
label: 'preferences.common.scrcpy.name',
|
||||
field: 'scrcpyPath',
|
||||
value: scrcpyPath,
|
||||
type: 'Input.path',
|
||||
placeholder: 'preferences.common.scrcpy.placeholder',
|
||||
tips: 'preferences.common.scrcpy.tips',
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'preferences.common.scrcpy.name', extensions: ['*'] }],
|
||||
},
|
||||
debug: {
|
||||
label: 'preferences.common.debug.name',
|
||||
field: 'debug',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: 'preferences.common.debug.placeholder',
|
||||
tips: 'preferences.common.debug.tips',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,52 +1,44 @@
|
||||
import { t } from '@/locales/index.js'
|
||||
|
||||
export default {
|
||||
label: t('preferences.device.name'),
|
||||
label: 'preferences.device.name',
|
||||
field: 'scrcpy',
|
||||
children: () => {
|
||||
return [
|
||||
{
|
||||
label: t('preferences.device.show-touch.name'),
|
||||
field: '--show-touches',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t('preferences.device.show-touch.placeholder'),
|
||||
tips: t('preferences.device.show-touch.tips'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.device.stay-awake.name'),
|
||||
field: '--stay-awake',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t('preferences.device.stay-awake.placeholder'),
|
||||
tips: t('preferences.device.stay-awake.tips'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.device.control-in-close-screen.name'),
|
||||
field: '--turn-screen-off',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t(
|
||||
'preferences.device.control-in-close-screen.placeholder',
|
||||
),
|
||||
},
|
||||
{
|
||||
label: t('preferences.device.control-end-video.name'),
|
||||
field: '--power-off-on-close',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t('preferences.device.control-end-video.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.device.control-in-stop-charging.name'),
|
||||
field: '--no-power-on',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t(
|
||||
'preferences.device.control-in-stop-charging.placeholder',
|
||||
),
|
||||
tips: t('preferences.device.control-in-stop-charging.tips'),
|
||||
},
|
||||
]
|
||||
children: {
|
||||
showTouches: {
|
||||
label: 'preferences.device.show-touch.name',
|
||||
field: '--show-touches',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.device.show-touch.placeholder',
|
||||
tips: 'preferences.device.show-touch.tips',
|
||||
},
|
||||
stayAwake: {
|
||||
label: 'preferences.device.stay-awake.name',
|
||||
field: '--stay-awake',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.device.stay-awake.placeholder',
|
||||
tips: 'preferences.device.stay-awake.tips',
|
||||
},
|
||||
turnScreenOff: {
|
||||
label: 'preferences.device.control-in-close-screen.name',
|
||||
field: '--turn-screen-off',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.device.control-in-close-screen.placeholder',
|
||||
},
|
||||
powerOffOnClose: {
|
||||
label: 'preferences.device.control-end-video.name',
|
||||
field: '--power-off-on-close',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.device.control-end-video.placeholder',
|
||||
},
|
||||
noPowerOn: {
|
||||
label: 'preferences.device.control-in-stop-charging.name',
|
||||
field: '--no-power-on',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.device.control-in-stop-charging.placeholder',
|
||||
tips: 'preferences.device.control-in-stop-charging.tips',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,27 +1,23 @@
|
||||
import { t } from '@/locales/index.js'
|
||||
|
||||
export default {
|
||||
label: t('preferences.record.name'),
|
||||
label: 'preferences.record.name',
|
||||
field: 'scrcpy',
|
||||
children: () => {
|
||||
return [
|
||||
{
|
||||
label: t('preferences.record.format.name'),
|
||||
field: '--record-format',
|
||||
type: 'Select',
|
||||
value: 'mp4',
|
||||
placeholder: t('preferences.record.format.placeholder'),
|
||||
options: [
|
||||
{
|
||||
label: 'mp4',
|
||||
value: 'mp4',
|
||||
},
|
||||
{
|
||||
label: 'mkv',
|
||||
value: 'mkv',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
children: {
|
||||
recordFormat: {
|
||||
label: 'preferences.record.format.name',
|
||||
field: '--record-format',
|
||||
type: 'Select',
|
||||
value: 'mp4',
|
||||
placeholder: 'preferences.record.format.placeholder',
|
||||
options: [
|
||||
{
|
||||
label: 'mp4',
|
||||
value: 'mp4',
|
||||
},
|
||||
{
|
||||
label: 'mkv',
|
||||
value: 'mkv',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,159 +1,148 @@
|
||||
import { t } from '@/locales/index.js'
|
||||
|
||||
const getDisplayOptions = (display = []) =>
|
||||
display?.map(value => ({ label: value, value })) || []
|
||||
|
||||
export default {
|
||||
label: t('preferences.video.name'),
|
||||
label: 'preferences.video.name',
|
||||
field: 'scrcpy',
|
||||
|
||||
children: ({ display } = {}) => {
|
||||
const displayOptions = display?.length
|
||||
? getDisplayOptions(display)
|
||||
: [
|
||||
{ label: '0', value: '0' },
|
||||
{ label: '1', value: '1' },
|
||||
{ label: '2', value: '2' },
|
||||
]
|
||||
|
||||
return [
|
||||
{
|
||||
label: t('preferences.video.resolution.name'),
|
||||
field: '--max-size',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.resolution.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.bit.name'),
|
||||
field: '--video-bit-rate',
|
||||
type: 'Input',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.bit.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.refresh-rate.name'),
|
||||
field: '--max-fps',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.refresh-rate.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.decoder.name'),
|
||||
field: '--video-codec',
|
||||
type: 'Select',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.decoder.placeholder'),
|
||||
options: [
|
||||
{
|
||||
label: 'h264',
|
||||
value: 'h264',
|
||||
},
|
||||
{
|
||||
label: 'h265',
|
||||
value: 'h265',
|
||||
},
|
||||
{
|
||||
label: 'av1',
|
||||
value: 'av1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.encoder.name'),
|
||||
field: '--video-encoder',
|
||||
type: 'Select',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.encoder.placeholder'),
|
||||
// "[server] INFO: List of video encoders:"
|
||||
// "--video-codec=h264 --video-encoder='OMX.qcom.video.encoder.avc'"
|
||||
// "--video-codec=h264 --video-encoder='c2.android.avc.encoder'"
|
||||
// "--video-codec=h264 --video-encoder='OMX.google.h264.encoder'"
|
||||
// "--video-codec=h265 --video-encoder='OMX.qcom.video.encoder.hevc'"
|
||||
// "--video-codec=h265 --video-encoder='c2.android.hevc.encoder'"
|
||||
options: [
|
||||
{
|
||||
label: 'Android HEVC(H.265) ',
|
||||
value: 'OMX.qcom.video.encoder.avc',
|
||||
},
|
||||
{
|
||||
label: 'Qualcomm HEVC(H.265) ',
|
||||
value: 'c2.android.avc.encoder',
|
||||
},
|
||||
{
|
||||
label: 'Google H.264(AVC)',
|
||||
value: 'OMX.google.h264.encoder',
|
||||
},
|
||||
{
|
||||
label: 'Android AVC(H.264) ',
|
||||
value: 'OMX.qcom.video.encoder.hevc',
|
||||
},
|
||||
{
|
||||
label: 'Qualcomm AVC(H.264)',
|
||||
value: 'c2.android.hevc.encoder',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.screen-rotation.name'),
|
||||
field: '--rotation',
|
||||
type: 'Select',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.screen-rotation.placeholder'),
|
||||
options: [
|
||||
{ label: '0°', value: '0' },
|
||||
{ label: '-90°', value: '1' },
|
||||
{ label: '180°', value: '2' },
|
||||
{ label: '90°', value: '3' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.screen-cropping.name'),
|
||||
field: '--crop',
|
||||
type: 'Input',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.screen-cropping.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.multi-display.name'),
|
||||
field: '--display',
|
||||
type: 'Select',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.multi-display.placeholder'),
|
||||
options: displayOptions,
|
||||
props: {
|
||||
filterable: true,
|
||||
allowCreate: true,
|
||||
children: {
|
||||
maxSize: {
|
||||
label: 'preferences.video.resolution.name',
|
||||
field: '--max-size',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.resolution.placeholder',
|
||||
},
|
||||
videoBitRate: {
|
||||
label: 'preferences.video.bit.name',
|
||||
field: '--video-bit-rate',
|
||||
type: 'Input',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.bit.placeholder',
|
||||
},
|
||||
maxFps: {
|
||||
label: 'preferences.video.refresh-rate.name',
|
||||
field: '--max-fps',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.refresh-rate.placeholder',
|
||||
},
|
||||
videoCodec: {
|
||||
label: 'preferences.video.decoder.name',
|
||||
field: '--video-codec',
|
||||
type: 'Select',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.decoder.placeholder',
|
||||
options: [
|
||||
{
|
||||
label: 'h264',
|
||||
value: 'h264',
|
||||
},
|
||||
{
|
||||
label: 'h265',
|
||||
value: 'h265',
|
||||
},
|
||||
{
|
||||
label: 'av1',
|
||||
value: 'av1',
|
||||
},
|
||||
],
|
||||
},
|
||||
videoEncoder: {
|
||||
label: 'preferences.video.encoder.name',
|
||||
field: '--video-encoder',
|
||||
type: 'Select',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.encoder.placeholder',
|
||||
// "[server] INFO: List of video encoders:"
|
||||
// "--video-codec=h264 --video-encoder='OMX.qcom.video.encoder.avc'"
|
||||
// "--video-codec=h264 --video-encoder='c2.android.avc.encoder'"
|
||||
// "--video-codec=h264 --video-encoder='OMX.google.h264.encoder'"
|
||||
// "--video-codec=h265 --video-encoder='OMX.qcom.video.encoder.hevc'"
|
||||
// "--video-codec=h265 --video-encoder='c2.android.hevc.encoder'"
|
||||
options: [
|
||||
{
|
||||
label: 'Android HEVC(H.265) ',
|
||||
value: 'OMX.qcom.video.encoder.avc',
|
||||
},
|
||||
{
|
||||
label: 'Qualcomm HEVC(H.265) ',
|
||||
value: 'c2.android.avc.encoder',
|
||||
},
|
||||
{
|
||||
label: 'Google H.264(AVC)',
|
||||
value: 'OMX.google.h264.encoder',
|
||||
},
|
||||
{
|
||||
label: 'Android AVC(H.264) ',
|
||||
value: 'OMX.qcom.video.encoder.hevc',
|
||||
},
|
||||
{
|
||||
label: 'Qualcomm AVC(H.264)',
|
||||
value: 'c2.android.hevc.encoder',
|
||||
},
|
||||
],
|
||||
},
|
||||
rotation: {
|
||||
label: 'preferences.video.screen-rotation.name',
|
||||
field: '--rotation',
|
||||
type: 'Select',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.screen-rotation.placeholder',
|
||||
options: [
|
||||
{ label: '0°', value: '0' },
|
||||
{ label: '-90°', value: '1' },
|
||||
{ label: '180°', value: '2' },
|
||||
{ label: '90°', value: '3' },
|
||||
],
|
||||
},
|
||||
crop: {
|
||||
label: 'preferences.video.screen-cropping.name',
|
||||
field: '--crop',
|
||||
type: 'Input',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.screen-cropping.placeholder',
|
||||
},
|
||||
display: {
|
||||
label: 'preferences.video.multi-display.name',
|
||||
field: '--display',
|
||||
type: 'Select',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.multi-display.placeholder',
|
||||
options: [
|
||||
{ label: '0', value: '0' },
|
||||
{ label: '1', value: '1' },
|
||||
{ label: '2', value: '2' },
|
||||
],
|
||||
props: {
|
||||
filterable: true,
|
||||
allowCreate: true,
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.video-buffering.name'),
|
||||
field: '--display-buffer',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.video-buffering.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.audio-buffering.name'),
|
||||
field: '--audio-buffer',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.video-buffering.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.receiver-buffering.name'),
|
||||
field: '--v4l2-buffer',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: t('preferences.video.video-buffering.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.video.disable.name'),
|
||||
field: '--no-video',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t('preferences.video.disable.placeholder'),
|
||||
},
|
||||
]
|
||||
},
|
||||
displayBuffer: {
|
||||
label: 'preferences.video.video-buffering.name',
|
||||
field: '--display-buffer',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.video-buffering.placeholder',
|
||||
},
|
||||
audioBuffer: {
|
||||
label: 'preferences.video.audio-buffering.name',
|
||||
field: '--audio-buffer',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.audio-buffering.placeholder',
|
||||
},
|
||||
v4l2Buffer: {
|
||||
label: 'preferences.video.receiver-buffering.name',
|
||||
field: '--v4l2-buffer',
|
||||
type: 'Input.number',
|
||||
value: '',
|
||||
placeholder: 'preferences.video.receiver-buffering.placeholder',
|
||||
},
|
||||
noVideo: {
|
||||
label: 'preferences.video.disable.name',
|
||||
field: '--no-video',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.video.disable.placeholder',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,38 +1,35 @@
|
||||
import { t } from '@/locales/index.js'
|
||||
|
||||
export default {
|
||||
label: t('preferences.window.name'),
|
||||
label: 'preferences.window.name',
|
||||
field: 'scrcpy',
|
||||
children: () => {
|
||||
return [
|
||||
{
|
||||
label: t('preferences.window.borderless.name'),
|
||||
field: '--window-borderless',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t('preferences.window.borderless.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.window.full-screen.name'),
|
||||
field: '--fullscreen',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t('preferences.window.full-screen.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.window.always-top.name'),
|
||||
field: '--always-on-top',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t('preferences.window.always-top.placeholder'),
|
||||
},
|
||||
{
|
||||
label: t('preferences.window.disable-screen-saver.name'),
|
||||
field: '--disable-screensaver',
|
||||
type: 'Switch',
|
||||
value: false,
|
||||
placeholder: t('preferences.window.disable-screen-saver.placeholder'),
|
||||
},
|
||||
]
|
||||
|
||||
children: {
|
||||
windowBorderless: {
|
||||
label: 'preferences.window.borderless.name',
|
||||
field: '--window-borderless',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.window.borderless.placeholder',
|
||||
},
|
||||
fullscreen: {
|
||||
label: 'preferences.window.full-screen.name',
|
||||
field: '--fullscreen',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.window.full-screen.placeholder',
|
||||
},
|
||||
alwaysOnTop: {
|
||||
label: 'preferences.window.always-top.name',
|
||||
field: '--always-on-top',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.window.always-top.placeholder',
|
||||
},
|
||||
disableScreensaver: {
|
||||
label: 'preferences.window.disable-screen-saver.name',
|
||||
field: '--disable-screensaver',
|
||||
type: 'Switch',
|
||||
value: null,
|
||||
placeholder: 'preferences.window.disable-screen-saver.placeholder',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
63
src/store/theme/index.js
Normal file
63
src/store/theme/index.js
Normal file
@ -0,0 +1,63 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
const systemTheme = (key, value) => {
|
||||
if (key === 'change') {
|
||||
window.electron.ipcRenderer.on('app-theme-change', (_, ...args) =>
|
||||
value(...args),
|
||||
)
|
||||
return
|
||||
}
|
||||
window.electron.ipcRenderer.invoke(`app-theme-${key}`, value)
|
||||
}
|
||||
|
||||
export const useThemeStore = defineStore({
|
||||
id: 'app-theme',
|
||||
state() {
|
||||
return {
|
||||
value: window.appStore.get('common.theme') || 'system',
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
system: systemTheme,
|
||||
|
||||
init() {
|
||||
this.update(this.value)
|
||||
},
|
||||
|
||||
update(value) {
|
||||
this.value = value
|
||||
systemTheme('update', value)
|
||||
this.updateHtml(value)
|
||||
return true
|
||||
},
|
||||
|
||||
async updateHtml(value) {
|
||||
const updateClass = (theme) => {
|
||||
const htmlEl = document.querySelector('html')
|
||||
if (theme === 'dark') {
|
||||
htmlEl.classList.add('dark')
|
||||
return
|
||||
}
|
||||
|
||||
htmlEl.classList.remove('dark')
|
||||
}
|
||||
|
||||
if (value === 'system') {
|
||||
const isDark = await systemTheme('isDark')
|
||||
updateClass(isDark ? 'dark' : 'light')
|
||||
return
|
||||
}
|
||||
|
||||
updateClass(value)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
/** 监听系统主题色变化 */
|
||||
systemTheme('change', ({ value }) => {
|
||||
// console.log('systemTheme.change.value', value)
|
||||
const themeStore = useThemeStore()
|
||||
if (value !== themeStore.value) {
|
||||
themeStore.update(value)
|
||||
}
|
||||
})
|
@ -8,14 +8,15 @@ html {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: theme('colors.gray.100');
|
||||
background-color: theme("colors.gray.100");
|
||||
@apply bg-gray-100 dark:bg-gray-800;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: theme('colors.gray.300');
|
||||
border-radius: 9999px;
|
||||
@apply bg-gray-300 dark:bg-gray-600;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: theme('colors.gray.500');
|
||||
@apply bg-gray-500 dark:bg-gray-300;
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
import { createProxy } from './index.js'
|
||||
|
||||
Object.assign(console, {
|
||||
...createProxy(window.appLog.functions, window.appLog.levels),
|
||||
raw: console.log,
|
||||
})
|
||||
const debug = window.appStore.get('common.debug') || false
|
||||
|
||||
if (debug) {
|
||||
Object.assign(console, {
|
||||
...createProxy(window.appLog.functions, window.appLog.levels),
|
||||
raw: console.log,
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user