Skip to content

Commit c98fa20

Browse files
committed
✨ feat: 优化设置页面 拆分组件
1 parent 16d6ff3 commit c98fa20

File tree

6 files changed

+438
-242
lines changed

6 files changed

+438
-242
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<template>
2+
<n-modal
3+
v-model:show="visible"
4+
preset="dialog"
5+
:title="t('settings.system.cache')"
6+
:positive-text="t('common.confirm')"
7+
:negative-text="t('common.cancel')"
8+
@positive-click="handleConfirm"
9+
@negative-click="handleCancel"
10+
>
11+
<n-space vertical>
12+
<p>{{ t('settings.system.cacheClearTitle') }}</p>
13+
<n-checkbox-group v-model:value="selectedTypes">
14+
<n-space vertical>
15+
<n-checkbox
16+
v-for="option in clearCacheOptions"
17+
:key="option.key"
18+
:value="option.key"
19+
:label="option.label"
20+
>
21+
<template #default>
22+
<div>
23+
<div>{{ t(`settings.system.cacheTypes.${option.key}.label`) }}</div>
24+
<div class="text-gray-400 text-sm">
25+
{{ t(`settings.system.cacheTypes.${option.key}.description`) }}
26+
</div>
27+
</div>
28+
</template>
29+
</n-checkbox>
30+
</n-space>
31+
</n-checkbox-group>
32+
</n-space>
33+
</n-modal>
34+
</template>
35+
36+
<script setup lang="ts">
37+
import { ref, watch, defineProps, defineEmits } from 'vue';
38+
import { useI18n } from 'vue-i18n';
39+
40+
const props = defineProps({
41+
show: {
42+
type: Boolean,
43+
default: false
44+
}
45+
});
46+
47+
const emit = defineEmits(['update:show', 'confirm']);
48+
49+
const { t } = useI18n();
50+
const visible = ref(props.show);
51+
const selectedTypes = ref<string[]>([]);
52+
53+
const clearCacheOptions = ref([
54+
{
55+
label: t('settings.system.cacheTypes.history.label'),
56+
key: 'history',
57+
description: t('settings.system.cacheTypes.history.description')
58+
},
59+
{
60+
label: t('settings.system.cacheTypes.favorite.label'),
61+
key: 'favorite',
62+
description: t('settings.system.cacheTypes.favorite.description')
63+
},
64+
{
65+
label: t('settings.system.cacheTypes.user.label'),
66+
key: 'user',
67+
description: t('settings.system.cacheTypes.user.description')
68+
},
69+
{
70+
label: t('settings.system.cacheTypes.settings.label'),
71+
key: 'settings',
72+
description: t('settings.system.cacheTypes.settings.description')
73+
},
74+
{
75+
label: t('settings.system.cacheTypes.downloads.label'),
76+
key: 'downloads',
77+
description: t('settings.system.cacheTypes.downloads.description')
78+
},
79+
{
80+
label: t('settings.system.cacheTypes.resources.label'),
81+
key: 'resources',
82+
description: t('settings.system.cacheTypes.resources.description')
83+
},
84+
{
85+
label: t('settings.system.cacheTypes.lyrics.label'),
86+
key: 'lyrics',
87+
description: t('settings.system.cacheTypes.lyrics.description')
88+
}
89+
]);
90+
91+
// 同步外部show属性变化
92+
watch(
93+
() => props.show,
94+
(newVal) => {
95+
visible.value = newVal;
96+
}
97+
);
98+
99+
// 同步内部visible变化
100+
watch(
101+
() => visible.value,
102+
(newVal) => {
103+
emit('update:show', newVal);
104+
}
105+
);
106+
107+
const handleConfirm = () => {
108+
emit('confirm', selectedTypes.value);
109+
selectedTypes.value = [];
110+
};
111+
112+
const handleCancel = () => {
113+
selectedTypes.value = [];
114+
visible.value = false;
115+
};
116+
</script>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<template>
2+
<n-modal
3+
v-model:show="visible"
4+
preset="dialog"
5+
:title="t('settings.playback.musicSources')"
6+
:positive-text="t('common.confirm')"
7+
:negative-text="t('common.cancel')"
8+
@positive-click="handleConfirm"
9+
@negative-click="handleCancel"
10+
>
11+
<n-space vertical>
12+
<p>{{ t('settings.playback.musicSourcesDesc') }}</p>
13+
<n-checkbox-group v-model:value="selectedSources">
14+
<n-grid :cols="2" :x-gap="12" :y-gap="8">
15+
<n-grid-item v-for="source in musicSourceOptions" :key="source.value">
16+
<n-checkbox :value="source.value">
17+
{{ source.label }}
18+
<template v-if="source.value === 'gdmusic'">
19+
<n-tooltip>
20+
<template #trigger>
21+
<n-icon size="16" class="ml-1 text-blue-500 cursor-help">
22+
<i class="ri-information-line"></i>
23+
</n-icon>
24+
</template>
25+
{{ t('settings.playback.gdmusicInfo') }}
26+
</n-tooltip>
27+
</template>
28+
</n-checkbox>
29+
</n-grid-item>
30+
</n-grid>
31+
</n-checkbox-group>
32+
<div v-if="selectedSources.length === 0" class="text-red-500 text-sm">
33+
{{ t('settings.playback.musicSourcesWarning') }}
34+
</div>
35+
36+
<!-- GD音乐台设置 -->
37+
<div v-if="selectedSources.includes('gdmusic')" class="mt-4 border-t pt-4 border-gray-200 dark:border-gray-700">
38+
<h3 class="text-base font-medium mb-2">GD音乐台(music.gdstudio.xyz)设置</h3>
39+
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">
40+
GD音乐台将自动尝试多个音乐平台进行解析,无需额外配置。优先级高于其他解析方式,但是请求可能较慢。感谢(music.gdstudio.xyz)
41+
</p>
42+
</div>
43+
</n-space>
44+
</n-modal>
45+
</template>
46+
47+
<script setup lang="ts">
48+
import { ref, watch, defineProps, defineEmits } from 'vue';
49+
import { useI18n } from 'vue-i18n';
50+
import { type Platform } from '@/types/music';
51+
52+
const props = defineProps({
53+
show: {
54+
type: Boolean,
55+
default: false
56+
},
57+
sources: {
58+
type: Array as () => Platform[],
59+
default: () => ['migu', 'kugou', 'pyncmd', 'bilibili', 'youtube']
60+
}
61+
});
62+
63+
const emit = defineEmits(['update:show', 'update:sources']);
64+
65+
const { t } = useI18n();
66+
const visible = ref(props.show);
67+
const selectedSources = ref<Platform[]>(props.sources);
68+
69+
const musicSourceOptions = ref([
70+
{ label: 'MiGu音乐', value: 'migu' },
71+
{ label: '酷狗音乐', value: 'kugou' },
72+
{ label: 'pyncmd', value: 'pyncmd' },
73+
{ label: '酷我音乐', value: 'kuwo' },
74+
{ label: 'Bilibili音乐', value: 'bilibili' },
75+
{ label: 'YouTube', value: 'youtube' },
76+
{ label: 'GD音乐台', value: 'gdmusic' }
77+
]);
78+
79+
// 同步外部show属性变化
80+
watch(
81+
() => props.show,
82+
(newVal) => {
83+
visible.value = newVal;
84+
}
85+
);
86+
87+
// 同步内部visible变化
88+
watch(
89+
() => visible.value,
90+
(newVal) => {
91+
emit('update:show', newVal);
92+
}
93+
);
94+
95+
// 同步外部sources属性变化
96+
watch(
97+
() => props.sources,
98+
(newVal) => {
99+
selectedSources.value = [...newVal];
100+
},
101+
{ deep: true }
102+
);
103+
104+
const handleConfirm = () => {
105+
// 确保至少选择一个音源
106+
const defaultPlatforms = ['migu', 'kugou', 'pyncmd', 'bilibili', 'youtube'];
107+
const valuesToEmit = selectedSources.value.length > 0
108+
? [...new Set(selectedSources.value)]
109+
: defaultPlatforms;
110+
111+
emit('update:sources', valuesToEmit);
112+
visible.value = false;
113+
};
114+
115+
const handleCancel = () => {
116+
// 取消时还原为props传入的初始值
117+
selectedSources.value = [...props.sources];
118+
visible.value = false;
119+
};
120+
</script>
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
<template>
2+
<n-modal
3+
v-model:show="visible"
4+
preset="dialog"
5+
:title="t('settings.network.proxy')"
6+
:positive-text="t('common.confirm')"
7+
:negative-text="t('common.cancel')"
8+
:show-icon="false"
9+
@positive-click="handleProxyConfirm"
10+
@negative-click="handleCancel"
11+
>
12+
<n-form
13+
ref="formRef"
14+
:model="proxyForm"
15+
:rules="proxyRules"
16+
label-placement="left"
17+
label-width="80"
18+
require-mark-placement="right-hanging"
19+
>
20+
<n-form-item :label="t('settings.network.proxy')" path="protocol">
21+
<n-select
22+
v-model:value="proxyForm.protocol"
23+
:options="[
24+
{ label: 'HTTP', value: 'http' },
25+
{ label: 'HTTPS', value: 'https' },
26+
{ label: 'SOCKS5', value: 'socks5' }
27+
]"
28+
/>
29+
</n-form-item>
30+
<n-form-item :label="t('settings.network.proxyHost')" path="host">
31+
<n-input
32+
v-model:value="proxyForm.host"
33+
:placeholder="t('settings.network.proxyHostPlaceholder')"
34+
/>
35+
</n-form-item>
36+
<n-form-item :label="t('settings.network.proxyPort')" path="port">
37+
<n-input-number
38+
v-model:value="proxyForm.port"
39+
:placeholder="t('settings.network.proxyPortPlaceholder')"
40+
:min="1"
41+
:max="65535"
42+
/>
43+
</n-form-item>
44+
</n-form>
45+
</n-modal>
46+
</template>
47+
48+
<script setup lang="ts">
49+
import { ref, watch, defineProps, defineEmits } from 'vue';
50+
import { useI18n } from 'vue-i18n';
51+
import { useMessage } from 'naive-ui';
52+
import type { FormRules } from 'naive-ui';
53+
54+
const props = defineProps({
55+
show: {
56+
type: Boolean,
57+
default: false
58+
},
59+
config: {
60+
type: Object,
61+
default: () => ({
62+
protocol: 'http',
63+
host: '127.0.0.1',
64+
port: 7890
65+
})
66+
}
67+
});
68+
69+
const emit = defineEmits(['update:show', 'confirm']);
70+
71+
const { t } = useI18n();
72+
const message = useMessage();
73+
const formRef = ref();
74+
75+
const visible = ref(props.show);
76+
const proxyForm = ref({
77+
protocol: props.config.protocol || 'http',
78+
host: props.config.host || '127.0.0.1',
79+
port: props.config.port || 7890
80+
});
81+
82+
const proxyRules: FormRules = {
83+
protocol: {
84+
required: true,
85+
message: t('settings.validation.selectProxyProtocol'),
86+
trigger: ['blur', 'change']
87+
},
88+
host: {
89+
required: true,
90+
message: t('settings.validation.proxyHost'),
91+
trigger: ['blur', 'change'],
92+
validator: (_rule, value) => {
93+
if (!value) return false;
94+
// 简单的IP或域名验证
95+
const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$|^localhost$|^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
96+
return ipRegex.test(value);
97+
}
98+
},
99+
port: {
100+
required: true,
101+
message: t('settings.validation.portNumber'),
102+
trigger: ['blur', 'change'],
103+
validator: (_rule, value) => {
104+
return value >= 1 && value <= 65535;
105+
}
106+
}
107+
};
108+
109+
// 同步外部show属性变化
110+
watch(
111+
() => props.show,
112+
(newVal) => {
113+
visible.value = newVal;
114+
}
115+
);
116+
117+
// 同步内部visible变化
118+
watch(
119+
() => visible.value,
120+
(newVal) => {
121+
emit('update:show', newVal);
122+
}
123+
);
124+
125+
// 同步外部config变化
126+
watch(
127+
() => props.config,
128+
(newVal) => {
129+
proxyForm.value = {
130+
protocol: newVal.protocol || 'http',
131+
host: newVal.host || '127.0.0.1',
132+
port: newVal.port || 7890
133+
};
134+
},
135+
{ deep: true }
136+
);
137+
138+
const handleProxyConfirm = async () => {
139+
try {
140+
await formRef.value?.validate();
141+
emit('confirm', { ...proxyForm.value });
142+
visible.value = false;
143+
message.success(t('settings.network.messages.proxySuccess'));
144+
} catch (err) {
145+
message.error(t('settings.network.messages.proxyError'));
146+
}
147+
};
148+
149+
const handleCancel = () => {
150+
visible.value = false;
151+
};
152+
</script>

0 commit comments

Comments
 (0)