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'