perf: Support for custom startup mirroring

This commit is contained in:
viarotel 2024-07-12 01:53:09 +08:00
parent db9e3e791e
commit 677f30cdc2
32 changed files with 602 additions and 210 deletions

View File

@ -30,7 +30,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"i18n-ally.localesPaths": ["src/locales/index.js", "src/locales/languages"], "i18n-ally.localesPaths": ["src/locales/index.js", "src/locales/languages"],
"i18n-ally.sourceLanguage": "en", "i18n-ally.sourceLanguage": "zh-CN",
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",
"i18n-ally.extract.ignored": [ "i18n-ally.extract.ignored": [
"Switch", "Switch",

View File

@ -84,6 +84,7 @@ Windows 及 Linux 端内部集成了 Gnirehtet 用于提供 PC 到安卓设
- 录制 - 录制
- OTG - OTG
- 摄像 - 摄像
- 灵活启动
### 设备交互栏 ### 设备交互栏
@ -204,11 +205,12 @@ Windows 及 Linux 端内部集成了 Gnirehtet 用于提供 PC 到安卓设
16. 支持使用内置终端执行自定义命令 ✅ 16. 支持使用内置终端执行自定义命令 ✅
17. 支持设备自动执行镜像 ✅ 17. 支持设备自动执行镜像 ✅
18. 支持常用批量功能 ✅ 18. 支持常用批量功能 ✅
19. 支持更多批量处理功能 🚧 19. 支持灵活启动镜像 ✅
20. 支持对设备进行分组 🚧 20. 支持更多批量处理功能 🚧
21. 添加文件传输助手功能 🚧 21. 支持对设备进行分组 🚧
22. 支持通过界面从设备下载选中的文件 🚧 22. 添加文件传输助手功能 🚧
23. 添加对游戏的增强功能,如游戏键位映射 🚧 23. 支持通过界面从设备下载选中的文件 🚧
24. 添加对游戏的增强功能,如游戏键位映射 🚧
## 常见问题 ## 常见问题

View File

@ -82,6 +82,7 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master
- Recording - Recording
- OTG - OTG
- Camera - Camera
- Custom
### Device Interaction Bar ### Device Interaction Bar
@ -202,11 +203,12 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master
16. Support to use built-in terminals to execute custom commands ✅ 16. Support to use built-in terminals to execute custom commands ✅
17. Supports automatic execution of mirror on devices ✅ 17. Supports automatic execution of mirror on devices ✅
18. Support common batch processing function ✅ 18. Support common batch processing function ✅
19. Support more batch processing functions 🚧 19. Support for custom startup mirroring ✅
20. Support the device to group 🚧 20. Support more batch processing functions 🚧
21. Add file transmission assistant function 🚧 21. Support the device to group 🚧
22. Support GUI-based selective file downloads from devices 🚧 22. Add file transmission assistant function 🚧
23. Add game enhancement features such as game keyboard mapping 🚧 23. Support GUI-based selective file downloads from devices 🚧
24. Add game enhancement features such as game keyboard mapping 🚧
## FAQ ## FAQ

View File

@ -45,6 +45,8 @@ export default {
}, },
)}` )}`
console.log('args', args)
try { try {
const mirroring = this.$scrcpy.mirror(row.id, { const mirroring = this.$scrcpy.mirror(row.id, {
title: this.$store.device.getLabel(row), title: this.$store.device.getLabel(row),

View File

@ -0,0 +1,94 @@
<template>
<TemplatePromise v-slot="{ resolve, reject }">
<el-dialog
v-model="visible"
:title="$t('device.actions.more.custom.name')"
class="w-11/12 el-dialog-flex el-dialog-beautify"
append-to-body
destroy-on-close
@close="close(reject)"
>
<div class="h-full overflow-auto -mx-2 pr-2">
<PreferenceForm
ref="preferenceFormRef"
v-model="preferenceData"
tag="el-collapse-item"
v-bind="{
collapseProps: { accordion: true },
excludes: ['common'],
}"
/>
</div>
<template #footer>
<el-button @click="close(reject)">
取消
</el-button>
<el-button type="primary" @click="submit(resolve)">
确定
</el-button>
</template>
</el-dialog>
</TemplatePromise>
</template>
<script setup>
import { createTemplatePromise } from '@vueuse/core'
import { nextTick } from 'vue'
import { usePreferenceStore } from '$/store/index.js'
import PreferenceForm from '$/components/Preference/components/PreferenceForm/index.vue'
const TemplatePromise = createTemplatePromise()
const preferenceStore = usePreferenceStore()
const visible = ref(false)
const preferenceFormRef = ref(null)
const preferenceData = ref({
...getDefaultData(),
})
const collapseValue = ref([])
const device = ref(null)
async function open(row) {
device.value = row
visible.value = true
return TemplatePromise.start()
}
async function submit(resolve) {
const data = await preferenceFormRef.value.generateCommand()
visible.value = false
resolve(data)
}
async function close(reject) {
visible.value = false
await nextTick()
preferenceData.value = { ...getDefaultData() }
reject(new Error('User cancel operation'))
}
function getDefaultData() {
return preferenceStore.getDefaultData('global', () => void 0)
}
defineExpose({
open,
close,
})
</script>
<style></style>

View File

@ -0,0 +1,84 @@
<template>
<slot :loading="loading" :trigger="handleClick" />
<DeployDialog ref="deployDialogRef" />
</template>
<script>
import DeployDialog from './components/DeployDialog/index.vue'
import { sleep } from '$/utils'
export default {
components: {
DeployDialog,
},
props: {
row: {
type: Object,
default: () => ({}),
},
toggleRowExpansion: {
type: Function,
default: () => () => false,
},
},
data() {
return {
loading: false,
}
},
methods: {
async handleClick() {
const row = this.row
this.loading = true
let args = ''
try {
args = await this.$refs.deployDialogRef.open(row)
}
catch (error) {
this.loading = false
this.$message.warning(error.message)
return false
}
/** TODO */
const isCamera = ['--camera-facing'].some(key => args.includes(key))
if (isCamera) {
args += ' --video-source=camera'
}
this.toggleRowExpansion(row, true)
try {
const mirroring = this.$scrcpy.mirror(row.id, {
title: this.$store.device.getLabel(row),
args,
stdout: this.onStdout,
stderr: this.onStderr,
})
await sleep(1 * 1000)
this.loading = false
await mirroring
}
catch (error) {
console.error('mirror.args', args)
console.error('mirror.error', error)
if (error.message) {
this.$message.warning(error.message)
}
}
},
onStdout() {},
onStderr() {},
},
}
</script>
<style></style>

View File

@ -41,17 +41,20 @@
import Record from './components/Record/index.vue' import Record from './components/Record/index.vue'
import Otg from './components/Otg/index.vue' import Otg from './components/Otg/index.vue'
import Camera from './components/Camera/index.vue' import Camera from './components/Camera/index.vue'
import Custom from './components/Custom/index.vue'
export default { export default {
components: { components: {
Record, Record,
Otg, Otg,
Camera, Camera,
Custom,
}, },
props: { props: {
...Record.props, ...Record.props,
...Otg.props, ...Otg.props,
...Camera.props, ...Camera.props,
...Custom.props,
}, },
data() { data() {
return { return {
@ -68,6 +71,10 @@ export default {
label: 'device.actions.more.camera.name', label: 'device.actions.more.camera.name',
component: 'Camera', component: 'Camera',
}, },
{
label: 'device.actions.more.custom.name',
component: 'Custom',
},
], ],
} }
}, },

View File

@ -0,0 +1,26 @@
<template>
<el-input
class="!w-full"
v-bind="{
clearable: true,
...(data.props || {}),
}"
>
<template v-if="data.append" #append>
{{ data.append }}
</template>
</el-input>
</template>
<script>
export default {
props: {
data: {
type: Object,
default: () => ({}),
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,27 @@
<template>
<el-input
class="!w-full"
v-bind="{
type: 'number',
clearable: true,
...(data.props || {}),
}"
>
<template v-if="data.append" #append>
{{ data.append }}
</template>
</el-input>
</template>
<script>
export default {
props: {
data: {
type: Object,
default: () => ({}),
},
},
}
</script>
<style></style>

View File

@ -1,11 +1,11 @@
<template> <template>
<el-input <el-input
v-bind="data.props || {}" v-bind="{
clearable: true,
...(data.props || {}),
}"
v-model="pathValue" v-model="pathValue"
clearable
class="!w-full" class="!w-full"
:title="$t(data.placeholder)"
:placeholder="$t(data.placeholder)"
> >
<template #append> <template #append>
<el-button <el-button

View File

@ -0,0 +1,31 @@
<template>
<el-select
v-bind="{
clearable: true,
...(data.props || {}),
}"
class="!w-full"
>
<el-option
v-for="(item, index) in data.options"
:key="index"
:label="$t(item.label)"
:value="item.value"
:title="$t(item.placeholder || item.label)"
>
</el-option>
</el-select>
</template>
<script>
export default {
props: {
data: {
type: Object,
default: () => ({}),
},
},
}
</script>
<style></style>

View File

@ -1,10 +1,8 @@
<template> <template>
<el-select <el-select
v-bind="data.props || {}" v-bind="{ ...(data.props || {}) }"
v-model="selectValue" v-model="selectValue"
class="!w-full" class="!w-full"
:title="$t(data.placeholder)"
:placeholder="$t(data.placeholder)"
> >
<el-option <el-option
v-for="(item, index) in options" v-for="(item, index) in options"

View File

@ -1,10 +1,10 @@
<template> <template>
<el-select <el-select
v-bind="data.props || {}" v-bind="{
...(data.props || {}),
}"
v-model="selectValue" v-model="selectValue"
class="!w-full" class="!w-full"
:title="$t(data.placeholder)"
:placeholder="$t(data.placeholder)"
> >
<el-option <el-option
v-for="(item, index) in options" v-for="(item, index) in options"

View File

@ -1,11 +1,8 @@
<template> <template>
<el-select <el-select
v-bind="data.props || {}" v-bind="{ clearable: true, ...(data.props || {}) }"
v-model="selectValue" v-model="selectValue"
class="!w-full" class="!w-full"
clearable
:title="$t(data.placeholder)"
:placeholder="$t(data.placeholder)"
> >
<el-option <el-option
v-for="(item, index) in options" v-for="(item, index) in options"

View File

@ -1,10 +1,8 @@
<template> <template>
<el-select <el-select
v-bind="data.props || {}" v-bind="{ ...(data.props || {}) }"
v-model="inputValue" v-model="inputValue"
class="!w-full" class="!w-full"
:title="$t(data.placeholder)"
:placeholder="$t(data.placeholder)"
> >
<el-option <el-option
v-for="(item, index) in data.options" v-for="(item, index) in data.options"

View File

@ -1,10 +1,8 @@
<template> <template>
<el-select <el-select
v-bind="data.props || {}" v-bind="{ ...(data.props || {}) }"
v-model="selectValue" v-model="selectValue"
class="!w-full" class="!w-full"
:title="$t(data.placeholder)"
:placeholder="$t(data.placeholder)"
> >
<el-option <el-option
v-for="(item, index) in options" v-for="(item, index) in options"

View File

@ -0,0 +1,16 @@
<template>
<el-switch class="!w-full" v-bind="{ ...(data.props || {}) }"></el-switch>
</template>
<script>
export default {
props: {
data: {
type: Object,
default: () => ({}),
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,30 @@
import Input from './Input/index.vue'
import InputNumber from './InputNumber/index.vue'
import InputPath from './InputPath/index.vue'
import Select from './Select/index.vue'
import SelectAudioCodec from './SelectAudioCodec/index.vue'
import SelectDisplay from './SelectDisplay/index.vue'
import SelectKeyboardInject from './SelectKeyboardInject/index.vue'
import SelectLanguage from './SelectLanguage/index.vue'
import SelectVideoCodec from './SelectVideoCodec/index.vue'
import Switch from './Switch/index.vue'
export const inputModel = {
PathInput: InputPath,
AudioCodecSelect: SelectAudioCodec,
VideoCodecSelect: SelectVideoCodec,
DisplaySelect: SelectDisplay,
KeyboardInjectSelect: SelectKeyboardInject,
LanguageSelect: SelectLanguage,
Input,
InputNumber,
InputPath,
Select,
SelectAudioCodec,
SelectDisplay,
SelectKeyboardInject,
SelectLanguage,
SelectVideoCodec,
Switch,
}

View File

@ -0,0 +1,195 @@
<template>
<el-form ref="elForm" :model="preferenceData" label-width="225px" class="">
<el-collapse
v-model="collapseValue"
v-bind="{
accordion: false,
...collapseProps,
}"
class="space-y-4 borderless"
>
<el-collapse-item
v-for="(item, name) of preferenceModel"
:key="name"
:name="name"
class="!border dark:border-gray-700 rounded-[5px] overflow-hidden shadow-el-lighter"
>
<template #title>
<div
class="flex items-center w-full text-left -mr-10 overflow-hidden dark:border-gray-700"
:class="{
'!border-b': collapseValue.includes(name),
}"
>
<div class="flex-1 w-0 truncate pl-4 text-base">
{{ $t(item.label) }}
</div>
<div class="flex-none pl-4 pr-12" @click.stop>
<el-button type="primary" text @click="handleReset(name)">
{{ $t('preferences.reset') }}
</el-button>
</div>
</div>
</template>
<div class="pt-4">
<el-form
ref="elForm"
:model="preferenceData"
label-width="225px"
class="pr-8 pt-4"
>
<el-row :gutter="20">
<el-col
v-for="(item_1, name_1) of subModel(item)"
:key="name_1"
:span="item_1.span || 12"
:offset="item_1.offset || 0"
>
<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"
popper-class="max-w-96"
effect="dark"
:content="$t(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="$t(item_1.placeholder)">{{
$t(item_1.label)
}}</span>
</div>
</template>
<component
:is="inputModel[item_1.type]"
v-model="preferenceData[item_1.field]"
v-bind="{
preferenceData,
deviceScope,
title: $t(item_1.placeholder),
placeholder: $t(item_1.placeholder),
data: item_1,
}"
></component>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</el-collapse-item>
</el-collapse>
</el-form>
</template>
<script setup>
import { omit } from 'lodash-es'
import { inputModel } from './components/index.js'
import { usePreferenceStore } from '$/store/index.js'
const props = defineProps({
deviceScope: {
type: String,
default: '',
},
collapseProps: {
type: Object,
default: () => ({}),
},
excludes: {
type: Array,
default: () => [],
},
})
const preferenceData = defineModel('modelValue', {
type: Object,
default: () => ({}),
})
const preferenceStore = usePreferenceStore()
const collapseValue = ref([])
const preferenceModel = computed(() =>
omit(preferenceStore.model, props.excludes),
)
const preferenceModelKeys = Object.keys(preferenceModel.value ?? {})
if (preferenceModelKeys.length) {
if (props.collapseProps.accordion) {
collapseValue.value = preferenceModelKeys[0]
}
else {
collapseValue.value = preferenceModelKeys
}
}
watch(
() => props.deviceScope,
(value) => {
if (!value) {
return false
}
preferenceData.value = preferenceStore.getData(value)
},
{ immediate: true },
)
function subModel(item) {
const children = item?.children || {}
const value = {}
Object.entries(children).forEach(([key, data]) => {
if (!data.hidden) {
value[key] = data
}
})
return value
}
function handleReset(type) {
preferenceData.value = {
...preferenceData.value,
...preferenceStore.getDefaultData(type),
}
}
async function generateCommand() {
const value = await preferenceStore.getScrcpyArgs(preferenceData.value, {
isRecord: true,
isCamera: true,
isOtg: true,
})
return value
}
defineExpose({
generateCommand,
})
</script>
<style scoped lang="postcss">
:deep(.el-collapse-item__header) {
@apply h-13 leading-13;
}
:deep(.el-collapse-item__arrow) {
@apply w-2em;
}
</style>

View File

@ -58,165 +58,28 @@
</div> </div>
</div> </div>
<div class="grid gap-6 pr-2 pt-4 flex-1 h-0 overflow-auto"> <div class="pr-2 pt-4 flex-1 h-0 overflow-auto">
<el-collapse v-model="collapseValues" class="space-y-4 borderless"> <PreferenceForm
<el-collapse-item v-model="preferenceData"
v-for="(item, name) of preferenceModel" v-bind="{
:key="name" deviceScope,
:name="name" }"
class="!border dark:border-gray-700 rounded-[5px] overflow-hidden shadow-el-lighter" >
> </PreferenceForm>
<template #title>
<div
class="flex items-center w-full text-left -mr-10 overflow-hidden dark:border-gray-700"
:class="{
'!border-b': collapseValues.includes(name),
}"
>
<div class="flex-1 w-0 truncate pl-4 text-base">
{{ $t(item.label) }}
</div>
<div class="flex-none pl-4 pr-12" @click.stop>
<el-button type="primary" text @click="handleReset(name)">
{{ $t('preferences.reset') }}
</el-button>
</div>
</div>
</template>
<div class="pt-4">
<el-form
ref="elForm"
:model="preferenceData"
label-width="225px"
class="pr-8 pt-4"
>
<el-row :gutter="20">
<el-col
v-for="(item_1, name_1) of subModel(item)"
:key="name_1"
:span="item_1.span || 12"
:offset="item_1.offset || 0"
>
<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"
popper-class="max-w-96"
effect="dark"
:content="$t(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="$t(item_1.placeholder)">{{
$t(item_1.label)
}}</span>
</div>
</template>
<el-input
v-if="item_1.type === 'Input'"
v-bind="item_1.props || {}"
v-model="preferenceData[item_1.field]"
class="!w-full"
:title="$t(item_1.placeholder)"
:placeholder="$t(item_1.placeholder)"
clearable
>
<template v-if="item_1.append" #append>
{{ item_1.append }}
</template>
</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="$t(item_1.placeholder)"
:placeholder="$t(item_1.placeholder)"
clearable
>
<template v-if="item_1.append" #append>
{{ item_1.append }}
</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"
: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]"
class="!w-full"
: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="$t(item_2.label)"
:value="item_2.value"
:title="$t(item_2.placeholder || item_2.label)"
>
</el-option>
</el-select>
<component
:is="item_1.type"
v-else
v-model="preferenceData[item_1.field]"
:data="item_1"
:device-scope="deviceScope"
:preference-data="preferenceData"
></component>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</el-collapse-item>
</el-collapse>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { debounce } from 'lodash-es' import { debounce } from 'lodash-es'
import { ref } from 'vue'
import { useOtg } from './composables/otg/index.js' import PreferenceForm from './components/PreferenceForm/index.vue'
import LanguageSelect from './components/LanguageSelect/index.vue'
import PathInput from './components/PathInput/index.vue'
import VideoCodecSelect from './components/VideoCodecSelect/index.vue'
import AudioCodecSelect from './components/AudioCodecSelect/index.vue'
import DisplaySelect from './components/DisplaySelect/index.vue'
import KeyboardInjectSelect from './components/KeyboardInjectSelect/index.vue'
import { usePreferenceStore } from '$/store/index.js' import { usePreferenceStore } from '$/store/index.js'
export default { export default {
components: { components: {
LanguageSelect, PreferenceForm,
PathInput,
VideoCodecSelect,
AudioCodecSelect,
DisplaySelect,
KeyboardInjectSelect,
}, },
setup() { setup() {
const preferenceStore = usePreferenceStore() const preferenceStore = usePreferenceStore()
@ -224,17 +87,15 @@ export default {
const preferenceData = ref(preferenceStore.data) const preferenceData = ref(preferenceStore.data)
const deviceScope = ref(preferenceStore.deviceScope) const deviceScope = ref(preferenceStore.deviceScope)
const collapseValues = ref(Object.keys(preferenceStore.model))
useOtg(preferenceData)
return { return {
preferenceData, preferenceData,
deviceScope, deviceScope,
collapseValues,
} }
}, },
computed: { computed: {
commonPreferenceModel() {
return this.$store.preference.model?.common || {}
},
preferenceModel() { preferenceModel() {
return this.$store.preference.model || {} return this.$store.preference.model || {}
}, },
@ -348,7 +209,9 @@ export default {
}, },
async handleExport() { async handleExport() {
const messageEl = this.$message.loading(this.$t('preferences.config.export.message')) const messageEl = this.$message.loading(
this.$t('preferences.config.export.message'),
)
try { try {
await this.$electron.ipcRenderer.invoke('show-save-dialog', { await this.$electron.ipcRenderer.invoke('show-save-dialog', {

View File

@ -86,6 +86,7 @@
"device.actions.more.record.name": "Start Recording", "device.actions.more.record.name": "Start Recording",
"device.actions.more.otg.name": "Startup OTG", "device.actions.more.otg.name": "Startup OTG",
"device.actions.more.camera.name": "Startup Camera", "device.actions.more.camera.name": "Startup Camera",
"device.actions.more.custom.name": "Custom Startup",
"device.control.name": "Control", "device.control.name": "Control",
"device.control.more": "More Controls", "device.control.more": "More Controls",

View File

@ -86,6 +86,7 @@
"device.actions.more.record.name": "开始录制", "device.actions.more.record.name": "开始录制",
"device.actions.more.otg.name": "启动OTG", "device.actions.more.otg.name": "启动OTG",
"device.actions.more.camera.name": "启动摄像", "device.actions.more.camera.name": "启动摄像",
"device.actions.more.custom.name": "灵活启动",
"device.control.name": "操作", "device.control.name": "操作",
"device.control.more": "设备交互", "device.control.more": "设备交互",

View File

@ -86,6 +86,7 @@
"device.actions.more.record.name": "開始錄製", "device.actions.more.record.name": "開始錄製",
"device.actions.more.otg.name": "啟動 OTG", "device.actions.more.otg.name": "啟動 OTG",
"device.actions.more.camera.name": "啟動鏡頭", "device.actions.more.camera.name": "啟動鏡頭",
"device.actions.more.custom.name": "靈活啟動",
"device.control.name": "操作", "device.control.name": "操作",
"device.control.more": "裝置互動", "device.control.more": "裝置互動",

View File

@ -59,3 +59,16 @@
@apply !p-0; @apply !p-0;
} }
} }
.el-dialog-beautify {
@apply !rounded-lg;
.el-dialog__title {
@apply relative;
&::before {
content: '';
@apply absolute inset-x-0 bottom-0 h-2 bg-primary-500/30;
}
}
}

View File

@ -30,12 +30,14 @@ export function getModelMap(data = model) {
return value return value
} }
export function getDefaultData(parentId) { export function getDefaultData(parentId, iteratee) {
const modelMap = getModelMap() const modelMap = getModelMap()
iteratee = iteratee ?? (value => value)
const value = Object.entries(modelMap).reduce((obj, [key, data]) => { const value = Object.entries(modelMap).reduce((obj, [key, data]) => {
if (!parentId || data.parentId === parentId) { if (!parentId || data.parentId === parentId) {
obj[key] = data.value obj[key] = iteratee(data.value)
} }
return obj return obj
}, {}) }, {})

View File

@ -54,15 +54,9 @@ export const usePreferenceStore = defineStore({
getters: {}, getters: {},
actions: { actions: {
getDefaultData, getDefaultData,
init(scope = this.deviceScope) { init(scope = this.deviceScope) {
let data = mergeConfig(getDefaultData(), getStoreData()) this.data = this.getData(scope)
if (scope !== 'global') {
data = mergeConfig(data, getStoreData(replaceIP(scope)))
}
this.data = data
return this.data return this.data
}, },
setScope(value) { setScope(value) {
@ -127,7 +121,12 @@ export const usePreferenceStore = defineStore({
this.init() this.init()
}, },
getData(scope = this.deviceScope) { getData(scope = this.deviceScope) {
const value = this.init(scope) let value = mergeConfig(getDefaultData(), getStoreData())
if (scope !== 'global') {
value = mergeConfig(value, getStoreData(replaceIP(scope)))
}
return value return value
}, },
@ -135,7 +134,7 @@ export const usePreferenceStore = defineStore({
scope = this.deviceScope, scope = this.deviceScope,
{ isRecord = false, isCamera = false, isOtg = false, excludes = [] } = {}, { isRecord = false, isCamera = false, isOtg = false, excludes = [] } = {},
) { ) {
const data = this.getData(scope) const data = typeof scope === 'object' ? scope : this.getData(scope)
if (!data) { if (!data) {
return '' return ''
@ -182,12 +181,14 @@ export const usePreferenceStore = defineStore({
return arr return arr
}, []) }, [])
if (this.data.scrcpyAppend) { if (data.scrcpyAppend) {
valueList.push(...this.data.scrcpyAppend.split(' ')) valueList.push(...data.scrcpyAppend.split(' '))
} }
const value = valueList.join(' ') const value = valueList.join(' ')
// console.log('value', value)
return value return value
}, },
getModel(path) { getModel(path) {

View File

@ -66,7 +66,7 @@ export default {
audioBuffer: { audioBuffer: {
label: 'preferences.audio.audio-buffer.name', label: 'preferences.audio.audio-buffer.name',
field: '--audio-buffer', field: '--audio-buffer',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.audio.audio-buffer.placeholder', placeholder: 'preferences.audio.audio-buffer.placeholder',
append: 'ms', append: 'ms',
@ -74,7 +74,7 @@ export default {
audioOutputBuffer: { audioOutputBuffer: {
label: 'preferences.audio.audio-output-buffer.name', label: 'preferences.audio.audio-output-buffer.name',
field: '--audio-output-buffer', field: '--audio-output-buffer',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.audio.audio-output-buffer.placeholder', placeholder: 'preferences.audio.audio-output-buffer.placeholder',
append: 'ms', append: 'ms',

View File

@ -34,7 +34,7 @@ export default {
cameraFps: { cameraFps: {
label: 'preferences.camera.camera-fps.name', label: 'preferences.camera.camera-fps.name',
field: '--camera-fps', field: '--camera-fps',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.camera.camera-fps.placeholder', placeholder: 'preferences.camera.camera-fps.placeholder',
append: 'fps', append: 'fps',

View File

@ -28,6 +28,9 @@ export default {
value: 'system', value: 'system',
}, },
], ],
props: {
clearable: false,
},
}, },
language: { language: {
label: 'common.language.name', label: 'common.language.name',

View File

@ -35,7 +35,7 @@ export default {
timeLimit: { timeLimit: {
label: 'preferences.record.time-limit.name', label: 'preferences.record.time-limit.name',
field: '--time-limit', field: '--time-limit',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.record.time-limit.placeholder', placeholder: 'preferences.record.time-limit.placeholder',
append: 's', append: 's',

View File

@ -31,7 +31,7 @@ export default {
maxSize: { maxSize: {
label: 'preferences.video.resolution.name', label: 'preferences.video.resolution.name',
field: '--max-size', field: '--max-size',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.video.resolution.placeholder', placeholder: 'preferences.video.resolution.placeholder',
}, },
@ -46,7 +46,7 @@ export default {
maxFps: { maxFps: {
label: 'preferences.video.refresh-rate.name', label: 'preferences.video.refresh-rate.name',
field: '--max-fps', field: '--max-fps',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.video.refresh-rate.placeholder', placeholder: 'preferences.video.refresh-rate.placeholder',
append: 'fps', append: 'fps',
@ -133,7 +133,7 @@ export default {
displayBuffer: { displayBuffer: {
label: 'preferences.video.video-buffer.name', label: 'preferences.video.video-buffer.name',
field: '--display-buffer', field: '--display-buffer',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.video.video-buffer.placeholder', placeholder: 'preferences.video.video-buffer.placeholder',
append: 'ms', append: 'ms',
@ -141,7 +141,7 @@ export default {
v4l2Buffer: { v4l2Buffer: {
label: 'preferences.video.receiver-buffer.name', label: 'preferences.video.receiver-buffer.name',
field: '--v4l2-buffer', field: '--v4l2-buffer',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.video.receiver-buffer.placeholder', placeholder: 'preferences.video.receiver-buffer.placeholder',
append: 'ms', append: 'ms',

View File

@ -6,7 +6,7 @@ export default {
windowWidth: { windowWidth: {
label: 'preferences.window.size.width', label: 'preferences.window.size.width',
field: '--window-width', field: '--window-width',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.window.size.width.placeholder', placeholder: 'preferences.window.size.width.placeholder',
tips: 'preferences.window.size.width.tips', tips: 'preferences.window.size.width.tips',
@ -14,7 +14,7 @@ export default {
windowHeight: { windowHeight: {
label: 'preferences.window.size.height', label: 'preferences.window.size.height',
field: '--window-height', field: '--window-height',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.window.size.height.placeholder', placeholder: 'preferences.window.size.height.placeholder',
tips: 'preferences.window.size.height.tips', tips: 'preferences.window.size.height.tips',
@ -22,14 +22,14 @@ export default {
windowX: { windowX: {
label: 'preferences.window.position.x', label: 'preferences.window.position.x',
field: '--window-x', field: '--window-x',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.window.position.x.placeholder', placeholder: 'preferences.window.position.x.placeholder',
}, },
windowY: { windowY: {
label: 'preferences.window.position.y', label: 'preferences.window.position.y',
field: '--window-y', field: '--window-y',
type: 'Input.number', type: 'InputNumber',
value: undefined, value: undefined,
placeholder: 'preferences.window.position.y.placeholder', placeholder: 'preferences.window.position.y.placeholder',
}, },