From e9befea886b4a9152605aafeb7691d0ba3d0982a Mon Sep 17 00:00:00 2001 From: viarotel <viarotel@qq.com> Date: Fri, 3 Nov 2023 10:38:02 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=F0=9F=8E=A8=20Update=20preferences=20s?= =?UTF-8?q?tyle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Preference/index.bak.vue | 376 ++++++++++++++++++++++++ src/components/Preference/index.vue | 252 ++++++++-------- src/styles/css/helpers.css | 4 + src/styles/css/index.js | 1 + 4 files changed, 514 insertions(+), 119 deletions(-) create mode 100644 src/components/Preference/index.bak.vue create mode 100644 src/styles/css/helpers.css diff --git a/src/components/Preference/index.bak.vue b/src/components/Preference/index.bak.vue new file mode 100644 index 0000000..bfee5ab --- /dev/null +++ b/src/components/Preference/index.bak.vue @@ -0,0 +1,376 @@ +<template> + <div class=""> + <div class="pb-4 pr-2 flex items-center justify-between"> + <div class=""> + <el-select + v-model="deviceScope" + value-key="" + :placeholder="$t('preferences.scope.placeholder')" + filterable + :no-data-text="$t('preferences.scope.no-data')" + class="!w-90" + @change="onScopeChange" + > + <template #prefix> + <el-tooltip class="" effect="dark" placement="bottom-start"> + <el-icon class="text-primary-300 hover:text-primary-500"> + <QuestionFilled /> + </el-icon> + <template #content> + <div class="space-y-1"> + <div class="pb-1"> + {{ $t("preferences.scope.details[0]") }} + </div> + <div class=""> + {{ $t("preferences.scope.details[1]") }} + </div> + <div class=""> + {{ $t("preferences.scope.details[2]") }} + </div> + </div> + </template> + </el-tooltip> + </template> + <el-option + v-for="item in scopeList" + :key="item.id" + :label="item.label" + :value="item.value" + > + </el-option> + </el-select> + </div> + <div class=""> + <el-button type="" icon="Upload" plain @click="handleImport"> + {{ $t("preferences.config.import.name") }} + </el-button> + <el-button type="" icon="Download" plain @click="handleExport"> + {{ $t("preferences.config.export.name") }} + </el-button> + <el-button type="" icon="Edit" plain @click="handleEdit"> + {{ $t("preferences.config.edit.name") }} + </el-button> + <el-button type="" icon="RefreshRight" plain @click="handleResetAll"> + {{ $t("preferences.config.reset.name") }} + </el-button> + </div> + </div> + <div class="grid gap-6 pr-2"> + <el-card + v-for="(item, parentId) of preferenceModel" + :key="parentId" + shadow="hover" + class="" + > + <template #header> + <div class="flex items-center"> + <div class="flex-1 w-0 truncate pl-2 text-base"> + {{ $t(item.label) }} + </div> + <div class="flex-none pl-4"> + <el-button type="primary" text @click="handleReset(parentId)"> + {{ $t("preferences.reset") }} + </el-button> + </div> + </div> + </template> + <div class=""> + <el-form + ref="elForm" + :model="preferenceData" + label-width="200px" + class="pr-8 pt-4" + > + <el-row :gutter="20"> + <el-col + v-for="(item_1, index_1) of subModel(item)" + :key="index_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" + class="" + 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" + > + </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-card> + </div> + </div> +</template> + +<script> +import { debounce } from 'lodash-es' +import { ref } from 'vue' +import { useOtg } from './__composables__/otg/index.js' +import LanguageSelect from './LanguageSelect/index.vue' +import PathInput from './PathInput/index.vue' +import VideoCodecSelect from './VideoCodecSelect/index.vue' +import AudioCodecSelect from './AudioCodecSelect/index.vue' +import DisplaySelect from './DisplaySelect/index.vue' +import LoadingIcon from '@/components/Device/ControlBar/LoadingIcon/index.vue' +import { usePreferenceStore } from '@/store/index.js' + +export default { + components: { + LanguageSelect, + PathInput, + VideoCodecSelect, + AudioCodecSelect, + DisplaySelect, + }, + setup() { + const preferenceStore = usePreferenceStore() + + const preferenceData = ref(preferenceStore.data) + const deviceScope = ref(preferenceStore.deviceScope) + + useOtg(preferenceData) + + return { + preferenceData, + deviceScope, + } + }, + computed: { + preferenceModel() { + return this.$store.preference.model || {} + }, + scopeList() { + const value = this.$store.device.list.map(item => ({ + ...item, + label: `${item.id}(${item.$name}${ + item.$remark ? `,${item.$remark}` : '' + })`, + value: item.id, + })) + + value.unshift({ + label: `Global(${this.$t('preferences.scope.global')})`, + value: 'global', + }) + + return value + }, + }, + watch: { + 'preferenceData': { + handler() { + this.handleSave() + }, + deep: true, + }, + 'preferenceData.theme': { + handler(value) { + this.$store.theme.update(value) + }, + }, + // 列表设备发生变化后如果没有匹配到则默认选中 global + 'scopeList': { + handler(value) { + const someValue = value.some( + item => this.$replaceIP(item.value) === this.deviceScope, + ) + + if (someValue) { + return + } + + this.deviceScope = 'global' + this.$store.preference.setScope(this.deviceScope) + this.preferenceData = this.$store.preference.data + }, + immediate: true, + }, + }, + created() { + this.handleSave = debounce(this.handleSave, 1000, { + leading: false, + trailing: true, + }) + }, + methods: { + subModel(item) { + const children = item?.children || {} + const value = {} + Object.entries(children).forEach(([key, data]) => { + if (!data.hidden) { + value[key] = data + } + }) + return value + }, + handleResetAll() { + this.$store.preference.reset(this.deviceScope) + this.preferenceData = this.$store.preference.data + }, + + onScopeChange(value) { + this.$store.preference.setScope(value) + this.preferenceData = this.$store.preference.data + }, + + async handleImport() { + try { + await this.$electron.ipcRenderer.invoke('show-open-dialog', { + preset: 'replaceFile', + filePath: this.$appStore.path, + filters: [ + { + name: this.$t('preferences.config.import.placeholder'), + extensions: ['json'], + }, + ], + }) + + this.$message.success(this.$t('preferences.config.import.success')) + + this.preferenceData = this.$store.preference.init() + } + catch (error) { + if (error.message) { + const message = error.message?.match(/Error: (.*)/)?.[1] + this.$message.warning(message || error.message) + } + } + }, + + handleEdit() { + this.$appStore.openInEditor() + }, + + async handleExport() { + const messageEl = this.$message({ + message: this.$t('preferences.config.export.message'), + icon: LoadingIcon, + duration: 0, + }) + + try { + await this.$electron.ipcRenderer.invoke('show-save-dialog', { + defaultPath: 'escrcpy-configs.json', + filePath: this.$appStore.path, + filters: [ + { + name: this.$t('preferences.config.export.placeholder'), + extensions: ['json'], + }, + ], + }) + this.$message.success(this.$t('preferences.config.export.success')) + } + catch (error) { + if (error.message) { + const message = error.message?.match(/Error: (.*)/)?.[1] + this.$message.warning(message || error.message) + } + } + + messageEl.close() + }, + + handleSave() { + this.$store.preference.setData(this.preferenceData) + this.$message.success(this.$t('preferences.config.save.placeholder')) + }, + + handleReset(type) { + this.preferenceData = { + ...this.preferenceData, + ...this.$store.preference.getDefaultData(type), + } + this.$store.preference.setData(this.preferenceData) + }, + }, +} +</script> + +<style scoped lang="postcss"> +:deep(.el-card) { + --el-card-padding: 8px; +} +</style> diff --git a/src/components/Preference/index.vue b/src/components/Preference/index.vue index bfee5ab..47e25f0 100644 --- a/src/components/Preference/index.vue +++ b/src/components/Preference/index.vue @@ -56,129 +56,136 @@ </div> </div> <div class="grid gap-6 pr-2"> - <el-card - v-for="(item, parentId) of preferenceModel" - :key="parentId" - shadow="hover" - class="" - > - <template #header> - <div class="flex items-center"> - <div class="flex-1 w-0 truncate pl-2 text-base"> - {{ $t(item.label) }} + <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"> + <el-button type="primary" text @click="handleReset(name)"> + {{ $t("preferences.reset") }} + </el-button> + </div> </div> - <div class="flex-none pl-4"> - <el-button type="primary" text @click="handleReset(parentId)"> - {{ $t("preferences.reset") }} - </el-button> - </div> - </div> - </template> - <div class=""> - <el-form - ref="elForm" - :model="preferenceData" - label-width="200px" - class="pr-8 pt-4" - > - <el-row :gutter="20"> - <el-col - v-for="(item_1, index_1) of subModel(item)" - :key="index_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" - class="" - effect="dark" - :content="$t(item_1.tips)" - placement="bottom" - > - <el-link - class="mr-1 !text-base" - icon="InfoFilled" - type="warning" - :underline="false" + </template> + <div class=""> + <el-form + ref="elForm" + :model="preferenceData" + label-width="200px" + 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" + class="" + effect="dark" + :content="$t(item_1.tips)" + placement="bottom" > - </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 }} + <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> - <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" + <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 > - </el-option> - </el-select> + <template v-if="item_1.append" #append> + {{ item_1.append }} + </template> + </el-input> - <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-card> + <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" + > + </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> </template> @@ -209,11 +216,14 @@ 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: { @@ -370,7 +380,11 @@ export default { </script> <style scoped lang="postcss"> -:deep(.el-card) { - --el-card-padding: 8px; +:deep(.el-collapse-item__header) { + @apply h-13 leading-13; +} + +:deep(.el-collapse-item__arrow) { + @apply w-2em; } </style> diff --git a/src/styles/css/helpers.css b/src/styles/css/helpers.css new file mode 100644 index 0000000..1d75c86 --- /dev/null +++ b/src/styles/css/helpers.css @@ -0,0 +1,4 @@ +.borderless, +.borderless * { + @apply border-width-0; +} \ No newline at end of file diff --git a/src/styles/css/index.js b/src/styles/css/index.js index f09f3fb..e4199bc 100644 --- a/src/styles/css/index.js +++ b/src/styles/css/index.js @@ -1,2 +1,3 @@ import '@viarotel-org/design/styles/resets' import './desktop.css' +import './helpers.css'