feat(PreferencePanel, usePreferences): add PreferencePanel component and integrate preference settings
This commit is contained in:
		
							parent
							
								
									e245afd709
								
							
						
					
					
						commit
						885a7dabab
					
				
							
								
								
									
										14
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/App.vue
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,13 +1,23 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { useRoute, useRouter } from 'vue-router'
 | 
			
		||||
import Player from './components/Player.vue'
 | 
			
		||||
import PreferencePanel from './components/PreferencePanel.vue'
 | 
			
		||||
import { ref } from 'vue'
 | 
			
		||||
 | 
			
		||||
import LeftArrowIcon from './assets/icons/leftarrow.vue'
 | 
			
		||||
// import SearchIcon from './assets/icons/search.vue'
 | 
			
		||||
import CorgIcon from './assets/icons/corg.vue'
 | 
			
		||||
import { watch } from 'vue'
 | 
			
		||||
 | 
			
		||||
const presentPreferencePanel = ref(true)
 | 
			
		||||
 | 
			
		||||
const route = useRoute()
 | 
			
		||||
const router = useRouter()
 | 
			
		||||
 | 
			
		||||
watch(() => presentPreferencePanel, (value) => {
 | 
			
		||||
	console.log(value)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +64,8 @@ const router = useRouter()
 | 
			
		|||
						</button> -->
 | 
			
		||||
 | 
			
		||||
						<button
 | 
			
		||||
							class="text-white w-9 h-9 bg-neutral-800/80 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex justify-center items-center">
 | 
			
		||||
							class="text-white w-9 h-9 bg-neutral-800/80 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex justify-center items-center"
 | 
			
		||||
							@click="presentPreferencePanel = true">
 | 
			
		||||
							<CorgIcon :size="4" />
 | 
			
		||||
						</button>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -64,5 +75,6 @@ const router = useRouter()
 | 
			
		|||
			</div>
 | 
			
		||||
			<RouterView />
 | 
			
		||||
		</div>
 | 
			
		||||
		<PreferencePanel :present="presentPreferencePanel" @dismiss="presentPreferencePanel = false" />
 | 
			
		||||
	</div>
 | 
			
		||||
</template>
 | 
			
		||||
							
								
								
									
										144
									
								
								src/components/PreferencePanel.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/components/PreferencePanel.vue
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,144 @@
 | 
			
		|||
<script lang="ts" setup>
 | 
			
		||||
import { watch } from 'vue';
 | 
			
		||||
import XIcon from '../assets/icons/x.vue'
 | 
			
		||||
import { usePreferences } from '../stores/usePreferences'
 | 
			
		||||
 | 
			
		||||
const preferences = usePreferences()
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	present: boolean
 | 
			
		||||
}>()
 | 
			
		||||
 | 
			
		||||
defineEmits<{
 | 
			
		||||
	(e: 'dismiss'): void
 | 
			
		||||
}>()
 | 
			
		||||
 | 
			
		||||
watch(() => props.present, (value) => console.log(value))
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
	<Transition name="modal">
 | 
			
		||||
		<div v-if="present"
 | 
			
		||||
			class="bg-black/30 w-screen h-screen absolute top-0 left-0 z-30 flex justify-center items-center select-none"
 | 
			
		||||
			@click="$emit('dismiss')">
 | 
			
		||||
			<div
 | 
			
		||||
				class="bg-neutral-900/80 shadow-[0_0_16px_0_rgba(0,0,0,0.5)] backdrop-blur-2xl border border-[#ffffff39] rounded-lg w-[60rem] h-3/4 relative overflow-scroll modal-content"
 | 
			
		||||
				@click.stop>
 | 
			
		||||
				<div
 | 
			
		||||
					class="flex justify-between items-center p-8 sticky top-0 bg-gradient-to-b from-neutral-900 to-neutral-900/0 z-10">
 | 
			
		||||
					<div class="text-white text-2xl font-semibold">偏好设置</div>
 | 
			
		||||
					<button
 | 
			
		||||
						class="text-white w-9 h-9 bg-neutral-800/80 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex justify-center items-center transition-all duration-200 hover:bg-neutral-700/80"
 | 
			
		||||
						@click="$emit('dismiss')">
 | 
			
		||||
						<XIcon :size="4" />
 | 
			
		||||
					</button>
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
				<div class="flex flex-col gap-4">
 | 
			
		||||
					<div>
 | 
			
		||||
						<div class="px-8">
 | 
			
		||||
							<div class="text-white/50 text-sm ml-6">播放间</div>
 | 
			
		||||
							<ul class="border border-[#ffffff39] rounded-lg backdrop-blur-lg mt-2 overflow-hidden">
 | 
			
		||||
								<li class="odd:bg-neutral-300/5">
 | 
			
		||||
									<button
 | 
			
		||||
										class="flex justify-between items-center px-6 py-4 w-full text-left hover:bg-neutral-300/10 transition-all"
 | 
			
		||||
										@click="preferences.displayTimeLeft = !preferences.displayTimeLeft">
 | 
			
		||||
										<div class="flex flex-col">
 | 
			
		||||
											<div class="text-base text-white">播放进度条右侧显示剩余时间</div>
 | 
			
		||||
											<div class="text-sm text-white/80">而非歌曲总时长</div>
 | 
			
		||||
										</div>
 | 
			
		||||
 | 
			
		||||
										<div>
 | 
			
		||||
											<div class="text-sky-500 text-lg" v-if="preferences.displayTimeLeft">开启</div>
 | 
			
		||||
											<div class="text-neutral-500 text-lg" v-else>关闭</div>
 | 
			
		||||
										</div>
 | 
			
		||||
									</button>
 | 
			
		||||
								</li>
 | 
			
		||||
								<li class="odd:bg-neutral-300/5">
 | 
			
		||||
									<button
 | 
			
		||||
										class="flex justify-between items-center px-6 py-4 w-full text-left hover:bg-neutral-300/10 transition-all"
 | 
			
		||||
										@click="preferences.presentLyrics = !preferences.presentLyrics">
 | 
			
		||||
										<div class="flex flex-col">
 | 
			
		||||
											<div class="text-base text-white">显示滚动歌词文本</div>
 | 
			
		||||
											<div class="text-sm text-white/80">当歌词文本可用时</div>
 | 
			
		||||
										</div>
 | 
			
		||||
 | 
			
		||||
										<div>
 | 
			
		||||
											<div class="text-sky-500 text-lg" v-if="preferences.presentLyrics">开启</div>
 | 
			
		||||
											<div class="text-neutral-500 text-lg" v-else>关闭</div>
 | 
			
		||||
										</div>
 | 
			
		||||
									</button>
 | 
			
		||||
								</li>
 | 
			
		||||
 | 
			
		||||
							</ul>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
 | 
			
		||||
					<div>
 | 
			
		||||
						<div class="px-8">
 | 
			
		||||
							<div class="text-white/50 text-sm ml-6">行为</div>
 | 
			
		||||
							<ul class="border border-[#ffffff39] rounded-lg backdrop-blur-lg mt-2 overflow-hidden">
 | 
			
		||||
								<li class="odd:bg-neutral-300/5">
 | 
			
		||||
									<button
 | 
			
		||||
										class="flex justify-between items-center px-6 py-4 w-full text-left hover:bg-neutral-300/10 transition-all"
 | 
			
		||||
										@click="preferences.autoRedirect = !preferences.autoRedirect">
 | 
			
		||||
										<div class="flex flex-col">
 | 
			
		||||
											<div class="text-base text-white">自动重定向</div>
 | 
			
		||||
											<div class="text-sm text-white/80">当尝试访问塞壬唱片官网时,重定向到 MSR Mod</div>
 | 
			
		||||
										</div>
 | 
			
		||||
 | 
			
		||||
										<div>
 | 
			
		||||
											<div class="text-sky-500 text-lg" v-if="preferences.autoRedirect">开启</div>
 | 
			
		||||
											<div class="text-neutral-500 text-lg" v-else>关闭</div>
 | 
			
		||||
										</div>
 | 
			
		||||
									</button>
 | 
			
		||||
								</li>
 | 
			
		||||
							</ul>
 | 
			
		||||
							<div class="text-white/50 text-sm ml-6 mt-2">即使此项目关闭,随时都可以通过点按 MSR Mod 扩展图标启动 MSR Mod。</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
 | 
			
		||||
					<div>
 | 
			
		||||
						<div class="px-8">
 | 
			
		||||
							<div class="text-white/50 text-sm ml-6">关于</div>
 | 
			
		||||
							<ul class="border border-[#ffffff39] rounded-lg backdrop-blur-lg mt-2 overflow-hidden">
 | 
			
		||||
								<li class="odd:bg-neutral-300/5">
 | 
			
		||||
									<button
 | 
			
		||||
										class="flex justify-between items-center px-6 py-4 w-full text-left hover:bg-neutral-300/10 transition-all">
 | 
			
		||||
										<div class="flex flex-col">
 | 
			
		||||
											<div class="text-base text-white">MSR Mod</div>
 | 
			
		||||
											<div class="text-sm text-white/80">版本号 0.0.1</div>
 | 
			
		||||
										</div>
 | 
			
		||||
									</button>
 | 
			
		||||
								</li>
 | 
			
		||||
							</ul>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</Transition>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.modal-enter-active,
 | 
			
		||||
.modal-leave-active {
 | 
			
		||||
	transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.modal-enter-from,
 | 
			
		||||
.modal-leave-to {
 | 
			
		||||
	opacity: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.modal-enter-active .modal-content,
 | 
			
		||||
.modal-leave-active .modal-content {
 | 
			
		||||
	transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.modal-enter-from .modal-content,
 | 
			
		||||
.modal-leave-to .modal-content {
 | 
			
		||||
	opacity: 0;
 | 
			
		||||
	transform: scale(0.95) translateY(1rem);
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ declare global {
 | 
			
		|||
export const usePreferences = defineStore('preferences', () => {
 | 
			
		||||
	const displayTimeLeft = ref<boolean>(false)
 | 
			
		||||
	const presentLyrics = ref<boolean>(false)
 | 
			
		||||
	const autoRedirect = ref<boolean>(true)
 | 
			
		||||
 | 
			
		||||
	const isLoaded = ref(false)
 | 
			
		||||
	const storageType = ref<'chrome' | 'localStorage' | 'memory'>('chrome')
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +19,8 @@ export const usePreferences = defineStore('preferences', () => {
 | 
			
		|||
	// 默认偏好设置
 | 
			
		||||
	const defaultPreferences = {
 | 
			
		||||
		displayTimeLeft: false,
 | 
			
		||||
		presentLyrics: false
 | 
			
		||||
		presentLyrics: false,
 | 
			
		||||
		autoRedirect: true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检测可用的 API
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +142,8 @@ export const usePreferences = defineStore('preferences', () => {
 | 
			
		|||
	const savePreferences = async () => {
 | 
			
		||||
		const preferences = {
 | 
			
		||||
			displayTimeLeft: displayTimeLeft.value,
 | 
			
		||||
			presentLyrics: presentLyrics.value
 | 
			
		||||
			presentLyrics: presentLyrics.value,
 | 
			
		||||
			autoRedirect: autoRedirect.value
 | 
			
		||||
		}
 | 
			
		||||
		await setStoredValue('preferences', preferences)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -151,16 +154,18 @@ export const usePreferences = defineStore('preferences', () => {
 | 
			
		|||
			const preferences = await getPreferences()
 | 
			
		||||
			displayTimeLeft.value = preferences.displayTimeLeft
 | 
			
		||||
			presentLyrics.value = preferences.presentLyrics
 | 
			
		||||
			autoRedirect.value = preferences.autoRedirect
 | 
			
		||||
			isLoaded.value = true
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			displayTimeLeft.value = false
 | 
			
		||||
			presentLyrics.value = false
 | 
			
		||||
			autoRedirect.value = true
 | 
			
		||||
			isLoaded.value = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 监听变化并保存
 | 
			
		||||
	watch([displayTimeLeft, presentLyrics], async () => {
 | 
			
		||||
	watch([displayTimeLeft, presentLyrics, autoRedirect], async () => {
 | 
			
		||||
		if (isLoaded.value) {
 | 
			
		||||
			try {
 | 
			
		||||
				await savePreferences()
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +181,7 @@ export const usePreferences = defineStore('preferences', () => {
 | 
			
		|||
	return {
 | 
			
		||||
		displayTimeLeft,
 | 
			
		||||
		presentLyrics,
 | 
			
		||||
		autoRedirect,
 | 
			
		||||
		isLoaded,
 | 
			
		||||
		storageType,
 | 
			
		||||
		initializePreferences,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user