mirror of
https://github.com/viarotel-org/escrcpy.git
synced 2024-11-27 17:00:53 +01:00
perf: ✨ Support for custom startup mirroring
This commit is contained in:
parent
db9e3e791e
commit
677f30cdc2
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -30,7 +30,7 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"i18n-ally.localesPaths": ["src/locales/index.js", "src/locales/languages"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.sourceLanguage": "zh-CN",
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.extract.ignored": [
|
||||
"Switch",
|
||||
|
12
README-CN.md
12
README-CN.md
@ -84,6 +84,7 @@ Windows 及 Linux 端内部集成了 Gnirehtet, 用于提供 PC 到安卓设
|
||||
- 录制
|
||||
- OTG
|
||||
- 摄像
|
||||
- 灵活启动
|
||||
|
||||
### 设备交互栏
|
||||
|
||||
@ -204,11 +205,12 @@ Windows 及 Linux 端内部集成了 Gnirehtet, 用于提供 PC 到安卓设
|
||||
16. 支持使用内置终端执行自定义命令 ✅
|
||||
17. 支持设备自动执行镜像 ✅
|
||||
18. 支持常用批量功能 ✅
|
||||
19. 支持更多批量处理功能 🚧
|
||||
20. 支持对设备进行分组 🚧
|
||||
21. 添加文件传输助手功能 🚧
|
||||
22. 支持通过界面从设备下载选中的文件 🚧
|
||||
23. 添加对游戏的增强功能,如游戏键位映射 🚧
|
||||
19. 支持灵活启动镜像 ✅
|
||||
20. 支持更多批量处理功能 🚧
|
||||
21. 支持对设备进行分组 🚧
|
||||
22. 添加文件传输助手功能 🚧
|
||||
23. 支持通过界面从设备下载选中的文件 🚧
|
||||
24. 添加对游戏的增强功能,如游戏键位映射 🚧
|
||||
|
||||
## 常见问题
|
||||
|
||||
|
12
README.md
12
README.md
@ -82,6 +82,7 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master
|
||||
- Recording
|
||||
- OTG
|
||||
- Camera
|
||||
- Custom
|
||||
|
||||
### 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 ✅
|
||||
17. Supports automatic execution of mirror on devices ✅
|
||||
18. Support common batch processing function ✅
|
||||
19. Support more batch processing functions 🚧
|
||||
20. Support the device to group 🚧
|
||||
21. Add file transmission assistant function 🚧
|
||||
22. Support GUI-based selective file downloads from devices 🚧
|
||||
23. Add game enhancement features such as game keyboard mapping 🚧
|
||||
19. Support for custom startup mirroring ✅
|
||||
20. Support more batch processing functions 🚧
|
||||
21. Support the device to group 🚧
|
||||
22. Add file transmission assistant function 🚧
|
||||
23. Support GUI-based selective file downloads from devices 🚧
|
||||
24. Add game enhancement features such as game keyboard mapping 🚧
|
||||
|
||||
## FAQ
|
||||
|
||||
|
@ -45,6 +45,8 @@ export default {
|
||||
},
|
||||
)}`
|
||||
|
||||
console.log('args', args)
|
||||
|
||||
try {
|
||||
const mirroring = this.$scrcpy.mirror(row.id, {
|
||||
title: this.$store.device.getLabel(row),
|
||||
|
@ -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>
|
@ -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>
|
@ -41,17 +41,20 @@
|
||||
import Record from './components/Record/index.vue'
|
||||
import Otg from './components/Otg/index.vue'
|
||||
import Camera from './components/Camera/index.vue'
|
||||
import Custom from './components/Custom/index.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Record,
|
||||
Otg,
|
||||
Camera,
|
||||
Custom,
|
||||
},
|
||||
props: {
|
||||
...Record.props,
|
||||
...Otg.props,
|
||||
...Camera.props,
|
||||
...Custom.props,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -68,6 +71,10 @@ export default {
|
||||
label: 'device.actions.more.camera.name',
|
||||
component: 'Camera',
|
||||
},
|
||||
{
|
||||
label: 'device.actions.more.custom.name',
|
||||
component: 'Custom',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
|
@ -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>
|
@ -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>
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<el-input
|
||||
v-bind="data.props || {}"
|
||||
v-bind="{
|
||||
clearable: true,
|
||||
...(data.props || {}),
|
||||
}"
|
||||
v-model="pathValue"
|
||||
clearable
|
||||
class="!w-full"
|
||||
:title="$t(data.placeholder)"
|
||||
:placeholder="$t(data.placeholder)"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
@ -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>
|
@ -1,10 +1,8 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-bind="data.props || {}"
|
||||
v-bind="{ ...(data.props || {}) }"
|
||||
v-model="selectValue"
|
||||
class="!w-full"
|
||||
:title="$t(data.placeholder)"
|
||||
:placeholder="$t(data.placeholder)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in options"
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-bind="data.props || {}"
|
||||
v-bind="{
|
||||
...(data.props || {}),
|
||||
}"
|
||||
v-model="selectValue"
|
||||
class="!w-full"
|
||||
:title="$t(data.placeholder)"
|
||||
:placeholder="$t(data.placeholder)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in options"
|
@ -1,11 +1,8 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-bind="data.props || {}"
|
||||
v-bind="{ clearable: true, ...(data.props || {}) }"
|
||||
v-model="selectValue"
|
||||
class="!w-full"
|
||||
clearable
|
||||
:title="$t(data.placeholder)"
|
||||
:placeholder="$t(data.placeholder)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in options"
|
@ -1,10 +1,8 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-bind="data.props || {}"
|
||||
v-bind="{ ...(data.props || {}) }"
|
||||
v-model="inputValue"
|
||||
class="!w-full"
|
||||
:title="$t(data.placeholder)"
|
||||
:placeholder="$t(data.placeholder)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in data.options"
|
@ -1,10 +1,8 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-bind="data.props || {}"
|
||||
v-bind="{ ...(data.props || {}) }"
|
||||
v-model="selectValue"
|
||||
class="!w-full"
|
||||
:title="$t(data.placeholder)"
|
||||
:placeholder="$t(data.placeholder)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in options"
|
@ -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>
|
@ -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,
|
||||
}
|
195
src/components/Preference/components/PreferenceForm/index.vue
Normal file
195
src/components/Preference/components/PreferenceForm/index.vue
Normal 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>
|
@ -58,165 +58,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 pr-2 pt-4 flex-1 h-0 overflow-auto">
|
||||
<el-collapse v-model="collapseValues" 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': 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 class="pr-2 pt-4 flex-1 h-0 overflow-auto">
|
||||
<PreferenceForm
|
||||
v-model="preferenceData"
|
||||
v-bind="{
|
||||
deviceScope,
|
||||
}"
|
||||
>
|
||||
</PreferenceForm>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { debounce } from 'lodash-es'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { useOtg } from './composables/otg/index.js'
|
||||
|
||||
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 PreferenceForm from './components/PreferenceForm/index.vue'
|
||||
|
||||
import { usePreferenceStore } from '$/store/index.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LanguageSelect,
|
||||
PathInput,
|
||||
VideoCodecSelect,
|
||||
AudioCodecSelect,
|
||||
DisplaySelect,
|
||||
KeyboardInjectSelect,
|
||||
PreferenceForm,
|
||||
},
|
||||
setup() {
|
||||
const preferenceStore = usePreferenceStore()
|
||||
@ -224,17 +87,15 @@ export default {
|
||||
const preferenceData = ref(preferenceStore.data)
|
||||
const deviceScope = ref(preferenceStore.deviceScope)
|
||||
|
||||
const collapseValues = ref(Object.keys(preferenceStore.model))
|
||||
|
||||
useOtg(preferenceData)
|
||||
|
||||
return {
|
||||
preferenceData,
|
||||
deviceScope,
|
||||
collapseValues,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
commonPreferenceModel() {
|
||||
return this.$store.preference.model?.common || {}
|
||||
},
|
||||
preferenceModel() {
|
||||
return this.$store.preference.model || {}
|
||||
},
|
||||
@ -348,7 +209,9 @@ export default {
|
||||
},
|
||||
|
||||
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 {
|
||||
await this.$electron.ipcRenderer.invoke('show-save-dialog', {
|
||||
|
@ -86,6 +86,7 @@
|
||||
"device.actions.more.record.name": "Start Recording",
|
||||
"device.actions.more.otg.name": "Startup OTG",
|
||||
"device.actions.more.camera.name": "Startup Camera",
|
||||
"device.actions.more.custom.name": "Custom Startup",
|
||||
|
||||
"device.control.name": "Control",
|
||||
"device.control.more": "More Controls",
|
||||
|
@ -86,6 +86,7 @@
|
||||
"device.actions.more.record.name": "开始录制",
|
||||
"device.actions.more.otg.name": "启动OTG",
|
||||
"device.actions.more.camera.name": "启动摄像",
|
||||
"device.actions.more.custom.name": "灵活启动",
|
||||
|
||||
"device.control.name": "操作",
|
||||
"device.control.more": "设备交互",
|
||||
|
@ -86,6 +86,7 @@
|
||||
"device.actions.more.record.name": "開始錄製",
|
||||
"device.actions.more.otg.name": "啟動 OTG",
|
||||
"device.actions.more.camera.name": "啟動鏡頭",
|
||||
"device.actions.more.custom.name": "靈活啟動",
|
||||
|
||||
"device.control.name": "操作",
|
||||
"device.control.more": "裝置互動",
|
||||
|
@ -59,3 +59,16 @@
|
||||
@apply !p-0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog-beautify {
|
||||
@apply !rounded-lg;
|
||||
|
||||
.el-dialog__title {
|
||||
@apply relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
@apply absolute inset-x-0 bottom-0 h-2 bg-primary-500/30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,12 +30,14 @@ export function getModelMap(data = model) {
|
||||
return value
|
||||
}
|
||||
|
||||
export function getDefaultData(parentId) {
|
||||
export function getDefaultData(parentId, iteratee) {
|
||||
const modelMap = getModelMap()
|
||||
|
||||
iteratee = iteratee ?? (value => value)
|
||||
|
||||
const value = Object.entries(modelMap).reduce((obj, [key, data]) => {
|
||||
if (!parentId || data.parentId === parentId) {
|
||||
obj[key] = data.value
|
||||
obj[key] = iteratee(data.value)
|
||||
}
|
||||
return obj
|
||||
}, {})
|
||||
|
@ -54,15 +54,9 @@ export const usePreferenceStore = defineStore({
|
||||
getters: {},
|
||||
actions: {
|
||||
getDefaultData,
|
||||
|
||||
init(scope = this.deviceScope) {
|
||||
let data = mergeConfig(getDefaultData(), getStoreData())
|
||||
|
||||
if (scope !== 'global') {
|
||||
data = mergeConfig(data, getStoreData(replaceIP(scope)))
|
||||
}
|
||||
|
||||
this.data = data
|
||||
|
||||
this.data = this.getData(scope)
|
||||
return this.data
|
||||
},
|
||||
setScope(value) {
|
||||
@ -127,7 +121,12 @@ export const usePreferenceStore = defineStore({
|
||||
this.init()
|
||||
},
|
||||
getData(scope = this.deviceScope) {
|
||||
const value = this.init(scope)
|
||||
let value = mergeConfig(getDefaultData(), getStoreData())
|
||||
|
||||
if (scope !== 'global') {
|
||||
value = mergeConfig(value, getStoreData(replaceIP(scope)))
|
||||
}
|
||||
|
||||
return value
|
||||
},
|
||||
|
||||
@ -135,7 +134,7 @@ export const usePreferenceStore = defineStore({
|
||||
scope = this.deviceScope,
|
||||
{ isRecord = false, isCamera = false, isOtg = false, excludes = [] } = {},
|
||||
) {
|
||||
const data = this.getData(scope)
|
||||
const data = typeof scope === 'object' ? scope : this.getData(scope)
|
||||
|
||||
if (!data) {
|
||||
return ''
|
||||
@ -182,12 +181,14 @@ export const usePreferenceStore = defineStore({
|
||||
return arr
|
||||
}, [])
|
||||
|
||||
if (this.data.scrcpyAppend) {
|
||||
valueList.push(...this.data.scrcpyAppend.split(' '))
|
||||
if (data.scrcpyAppend) {
|
||||
valueList.push(...data.scrcpyAppend.split(' '))
|
||||
}
|
||||
|
||||
const value = valueList.join(' ')
|
||||
|
||||
// console.log('value', value)
|
||||
|
||||
return value
|
||||
},
|
||||
getModel(path) {
|
||||
|
@ -66,7 +66,7 @@ export default {
|
||||
audioBuffer: {
|
||||
label: 'preferences.audio.audio-buffer.name',
|
||||
field: '--audio-buffer',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.audio.audio-buffer.placeholder',
|
||||
append: 'ms',
|
||||
@ -74,7 +74,7 @@ export default {
|
||||
audioOutputBuffer: {
|
||||
label: 'preferences.audio.audio-output-buffer.name',
|
||||
field: '--audio-output-buffer',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.audio.audio-output-buffer.placeholder',
|
||||
append: 'ms',
|
||||
|
@ -34,7 +34,7 @@ export default {
|
||||
cameraFps: {
|
||||
label: 'preferences.camera.camera-fps.name',
|
||||
field: '--camera-fps',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.camera.camera-fps.placeholder',
|
||||
append: 'fps',
|
||||
|
@ -28,6 +28,9 @@ export default {
|
||||
value: 'system',
|
||||
},
|
||||
],
|
||||
props: {
|
||||
clearable: false,
|
||||
},
|
||||
},
|
||||
language: {
|
||||
label: 'common.language.name',
|
||||
|
@ -35,7 +35,7 @@ export default {
|
||||
timeLimit: {
|
||||
label: 'preferences.record.time-limit.name',
|
||||
field: '--time-limit',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.record.time-limit.placeholder',
|
||||
append: 's',
|
||||
|
@ -31,7 +31,7 @@ export default {
|
||||
maxSize: {
|
||||
label: 'preferences.video.resolution.name',
|
||||
field: '--max-size',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.video.resolution.placeholder',
|
||||
},
|
||||
@ -46,7 +46,7 @@ export default {
|
||||
maxFps: {
|
||||
label: 'preferences.video.refresh-rate.name',
|
||||
field: '--max-fps',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.video.refresh-rate.placeholder',
|
||||
append: 'fps',
|
||||
@ -133,7 +133,7 @@ export default {
|
||||
displayBuffer: {
|
||||
label: 'preferences.video.video-buffer.name',
|
||||
field: '--display-buffer',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.video.video-buffer.placeholder',
|
||||
append: 'ms',
|
||||
@ -141,7 +141,7 @@ export default {
|
||||
v4l2Buffer: {
|
||||
label: 'preferences.video.receiver-buffer.name',
|
||||
field: '--v4l2-buffer',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.video.receiver-buffer.placeholder',
|
||||
append: 'ms',
|
||||
|
@ -6,7 +6,7 @@ export default {
|
||||
windowWidth: {
|
||||
label: 'preferences.window.size.width',
|
||||
field: '--window-width',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.window.size.width.placeholder',
|
||||
tips: 'preferences.window.size.width.tips',
|
||||
@ -14,7 +14,7 @@ export default {
|
||||
windowHeight: {
|
||||
label: 'preferences.window.size.height',
|
||||
field: '--window-height',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.window.size.height.placeholder',
|
||||
tips: 'preferences.window.size.height.tips',
|
||||
@ -22,14 +22,14 @@ export default {
|
||||
windowX: {
|
||||
label: 'preferences.window.position.x',
|
||||
field: '--window-x',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.window.position.x.placeholder',
|
||||
},
|
||||
windowY: {
|
||||
label: 'preferences.window.position.y',
|
||||
field: '--window-y',
|
||||
type: 'Input.number',
|
||||
type: 'InputNumber',
|
||||
value: undefined,
|
||||
placeholder: 'preferences.window.position.y.placeholder',
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user