202 lines
5.5 KiB
Vue
202 lines
5.5 KiB
Vue
<script setup lang="ts">
|
|
import { useGameStore } from '@/stores/gameStore'
|
|
import { useI18n } from '@/composables/useI18n'
|
|
|
|
const store = useGameStore()
|
|
const { t, currentLang, setLang } = useI18n()
|
|
|
|
const emit = defineEmits<{
|
|
close: []
|
|
}>()
|
|
|
|
const fontSizeOptions = [20, 24, 28, 32]
|
|
const bgAlphaOptions = [0, 0.3, 0.5, 0.7, 0.9]
|
|
|
|
const langLabels: Record<string, string> = {
|
|
zh: '中文',
|
|
en: 'English',
|
|
ja: '日本語',
|
|
ko: '한국어',
|
|
fr: 'Français',
|
|
de: 'Deutsch',
|
|
es: 'Español',
|
|
pt: 'Português',
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="settings-overlay" @click.self="emit('close')" @keydown.escape="emit('close')">
|
|
<div class="settings-panel">
|
|
<h2 class="settings-title">{{ t('ui.settings') }}</h2>
|
|
|
|
<div class="settings-body">
|
|
<div class="setting-row" v-if="store.storyLocales.languages.length > 1">
|
|
<span class="setting-label">{{ t('ui.language') }}</span>
|
|
<select :value="currentLang" @change="setLang(($event.target as HTMLSelectElement).value)">
|
|
<option v-for="lang in store.storyLocales.languages" :key="lang" :value="lang">
|
|
{{ langLabels[lang] || lang.toUpperCase() }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<span class="setting-label">{{ t('ui.subtitleSize') }}</span>
|
|
<select :value="store.subFontSize" @change="store.setSubFontSize(+($event.target as HTMLSelectElement).value)">
|
|
<option v-for="s in fontSizeOptions" :key="s" :value="s">{{ s }}px</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<span class="setting-label">{{ t('ui.subtitleBg') }}</span>
|
|
<select :value="store.subBgAlpha" @change="store.setSubBgAlpha(+($event.target as HTMLSelectElement).value)">
|
|
<option :value="0">{{ t('ui.none') }}</option>
|
|
<option v-for="a in bgAlphaOptions.filter(v => v > 0)" :key="a" :value="a">{{ (a * 100) + '%' }}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<span class="setting-label">{{ t('ui.qteTimeRelax') }}</span>
|
|
<label class="toggle">
|
|
<input type="checkbox" :checked="store.qteTimeRelax" @change="store.setQteTimeRelax(($event.target as HTMLInputElement).checked)" />
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<span class="setting-label">{{ t('ui.qteSingleKey') }}</span>
|
|
<label class="toggle">
|
|
<input type="checkbox" :checked="store.qteSingleKey" @change="store.setQteSingleKey(($event.target as HTMLInputElement).checked)" />
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<span class="setting-label">{{ t('ui.antiMistap') }}</span>
|
|
<label class="toggle">
|
|
<input type="checkbox" :checked="store.antiMistap" @change="store.setAntiMistap(($event.target as HTMLInputElement).checked)" />
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<span class="setting-label">{{ t('ui.pauseEnabled') }}</span>
|
|
<label class="toggle">
|
|
<input type="checkbox" :checked="store.pauseEnabled" @change="store.setPauseEnabled(($event.target as HTMLInputElement).checked)" />
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="settings-close" @click="emit('close')">{{ t('ui.close') }}</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.settings-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.85);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 200;
|
|
}
|
|
|
|
.settings-panel {
|
|
background: #1a1a2e;
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: 10px;
|
|
padding: 36px 40px;
|
|
min-width: 400px;
|
|
max-width: 480px;
|
|
}
|
|
|
|
.settings-title {
|
|
text-align: center;
|
|
font-size: 22px;
|
|
font-weight: 400;
|
|
color: #ddd;
|
|
letter-spacing: 3px;
|
|
margin-bottom: 28px;
|
|
}
|
|
|
|
.settings-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
}
|
|
|
|
.setting-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 8px 12px;
|
|
background: rgba(255, 255, 255, 0.03);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.setting-label {
|
|
font-size: 13px;
|
|
color: #bbb;
|
|
}
|
|
|
|
select {
|
|
padding: 4px 10px;
|
|
font-size: 13px;
|
|
background: rgba(255, 255, 255, 0.08);
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
border-radius: 3px;
|
|
color: #ddd;
|
|
outline: none;
|
|
}
|
|
|
|
.toggle {
|
|
position: relative;
|
|
display: inline-block;
|
|
width: 40px;
|
|
height: 22px;
|
|
}
|
|
|
|
.toggle input { display: none; }
|
|
|
|
.toggle-slider {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: rgba(255, 255, 255, 0.15);
|
|
border-radius: 11px;
|
|
cursor: pointer;
|
|
transition: background 0.2s;
|
|
}
|
|
|
|
.toggle-slider::after {
|
|
content: '';
|
|
position: absolute;
|
|
width: 16px;
|
|
height: 16px;
|
|
left: 3px;
|
|
top: 3px;
|
|
background: #fff;
|
|
border-radius: 50%;
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.toggle input:checked + .toggle-slider { background: #4caf50; }
|
|
.toggle input:checked + .toggle-slider::after { transform: translateX(18px); }
|
|
|
|
.settings-close {
|
|
display: block;
|
|
margin: 24px auto 0;
|
|
padding: 10px 36px;
|
|
font-size: 14px;
|
|
color: #888;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
transition: background 0.15s, color 0.15s;
|
|
}
|
|
|
|
.settings-close:hover { background: rgba(255, 255, 255, 0.1); color: #ccc; }
|
|
</style>
|