Skip to content

Commit b617c8a

Browse files
committed
settings.
1 parent 5d0273b commit b617c8a

3 files changed

Lines changed: 222 additions & 5 deletions

File tree

src/App.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import CharBox from './components/CharBox.vue';
44
import CreateQuizModal from './components/CreateQuizModal.vue';
55
import ProgressNav from './components/ProgressNav.vue';
66
import RulesModal from './components/RulesModal.vue';
7+
import SettingsModal from './components/SettingsModal.vue';
78
import { idioms } from './assets/idioms';
89
import {
910
type GameState,
@@ -33,6 +34,7 @@ const startTime = ref(0);
3334
const showCongrats = ref(false);
3435
const showCreateQuiz = ref(false);
3536
const showRules = ref(false);
37+
const showSettings = ref(false);
3638
3739
const MAX_ATTEMPTS = 10;
3840
@@ -429,6 +431,14 @@ const closeRules = () => {
429431
showRules.value = false;
430432
};
431433
434+
const openSettings = () => {
435+
showSettings.value = true;
436+
};
437+
438+
const closeSettings = () => {
439+
showSettings.value = false;
440+
};
441+
432442
const isAllCompleted = computed(() => {
433443
if (!customMode.isActive.value) return false;
434444
return customMode.results.value.every(r => r.completed);
@@ -452,6 +462,7 @@ const showCompletionDialog = () => {
452462
<div class="header">
453463
<div class="header-buttons">
454464
<button @click="openRules" class="share-btn" title="游戏规则">ℹ️</button>
465+
<button @click="openSettings" class="share-btn" title="设置">⚙️</button>
455466
</div>
456467
<h1>猜成语</h1>
457468
<div class="header-buttons">
@@ -483,6 +494,7 @@ const showCompletionDialog = () => {
483494

484495
<CreateQuizModal :show="showCreateQuiz" :encryptFn="encryptIdiom" @close="closeCreateQuiz" />
485496
<RulesModal :show="showRules" @close="closeRules" />
497+
<SettingsModal :show="showSettings" @close="closeSettings" />
486498

487499
<div v-if="showCongrats" class="congrats-modal">
488500
<div class="congrats-content">

src/components/CharBox.vue

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
type PinyinParts,
44
type CharMatch,
55
} from '../idiom'
6+
import { ref, onMounted, onUnmounted } from 'vue';
67
78
interface Props {
89
char: string;
@@ -19,7 +20,36 @@ const toneSymbols: Record<string, string> = {
1920
'4': '\u02CB',
2021
};
2122
22-
const getToneSymbol = (tone: string) => toneSymbols[tone] || '';
23+
const toneDisplay = ref<'symbol' | 'number'>('symbol');
24+
25+
const getToneDisplay = (tone: string) => {
26+
if (toneDisplay.value === 'number') {
27+
return tone;
28+
}
29+
return toneSymbols[tone] || '';
30+
};
31+
32+
const handleToneDisplayChange = (event: Event) => {
33+
const customEvent = event as CustomEvent;
34+
toneDisplay.value = customEvent.detail;
35+
};
36+
37+
onMounted(() => {
38+
const settings = localStorage.getItem('settings');
39+
if (settings) {
40+
try {
41+
const parsed = JSON.parse(settings);
42+
toneDisplay.value = parsed.toneDisplay || 'symbol';
43+
} catch {
44+
toneDisplay.value = 'symbol';
45+
}
46+
}
47+
window.addEventListener('toneDisplayChange', handleToneDisplayChange);
48+
});
49+
50+
onUnmounted(() => {
51+
window.removeEventListener('toneDisplayChange', handleToneDisplayChange);
52+
});
2353
</script>
2454

2555
<template>
@@ -31,8 +61,9 @@ const getToneSymbol = (tone: string) => toneSymbols[tone] || '';
3161
<span class="final" :class="{ correct: match.pinyin.final === 2, present: match.pinyin.final === 1 }">
3262
{{ pinyin.final }}
3363
</span>
34-
<span class="tone" :class="{ correct: match.pinyin.tone === 2, present: match.pinyin.tone === 1 }">
35-
{{ getToneSymbol(pinyin.tone) }}
64+
<span class="tone"
65+
:class="{ correct: match.pinyin.tone === 2, present: match.pinyin.tone === 1, 'tone-symbol': toneDisplay === 'symbol' }">
66+
{{ getToneDisplay(pinyin.tone) }}
3667
</span>
3768
</div>
3869
<div class="char">{{ char }}</div>
@@ -84,7 +115,7 @@ const getToneSymbol = (tone: string) => toneSymbols[tone] || '';
84115
.pinyin .final.present,
85116
.pinyin .tone.present {
86117
color: #ff9800;
87-
font-weight: bold;
118+
/* font-weight: bold; */
88119
}
89120
90121
.char-box.correct .pinyin .initial.correct,
@@ -102,10 +133,19 @@ const getToneSymbol = (tone: string) => toneSymbols[tone] || '';
102133
}
103134
104135
.pinyin {
105-
font-size: 14px;
136+
font-size: 16px;
106137
color: #666;
107138
}
108139
140+
.pinyin .tone {
141+
margin-left: 2px;
142+
}
143+
144+
.pinyin .tone.tone-symbol {
145+
transform: scale(1.2, 1);
146+
display: inline-block;
147+
}
148+
109149
.char {
110150
font-size: 28px;
111151
font-weight: normal;

src/components/SettingsModal.vue

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<script setup lang="ts">
2+
import { ref, onMounted } from 'vue';
3+
4+
defineProps<{
5+
show: boolean
6+
}>();
7+
8+
const emit = defineEmits<{
9+
close: []
10+
}>();
11+
12+
interface Settings {
13+
toneDisplay: 'symbol' | 'number';
14+
}
15+
16+
const toneDisplay = ref<'symbol' | 'number'>('symbol');
17+
18+
const loadSettings = (): Settings => {
19+
const saved = localStorage.getItem('settings');
20+
if (saved) {
21+
try {
22+
return JSON.parse(saved);
23+
} catch {
24+
return { toneDisplay: 'symbol' };
25+
}
26+
}
27+
return { toneDisplay: 'symbol' };
28+
};
29+
30+
const saveSettings = (settings: Settings) => {
31+
localStorage.setItem('settings', JSON.stringify(settings));
32+
};
33+
34+
onMounted(() => {
35+
const settings = loadSettings();
36+
toneDisplay.value = settings.toneDisplay;
37+
});
38+
39+
const handleToneDisplayChange = (value: 'symbol' | 'number') => {
40+
toneDisplay.value = value;
41+
const settings: Settings = { toneDisplay: value };
42+
saveSettings(settings);
43+
window.dispatchEvent(new CustomEvent('toneDisplayChange', { detail: value }));
44+
};
45+
</script>
46+
47+
<template>
48+
<div v-if="show" class="modal-overlay" @click="emit('close')">
49+
<div class="modal-content" @click.stop>
50+
<button class="close-x" @click="emit('close')">×</button>
51+
<h2>设置</h2>
52+
<div class="settings">
53+
<div class="setting-item">
54+
<label>声调显示方式</label>
55+
<div class="button-group">
56+
<button :class="['option-btn', { active: toneDisplay === 'symbol' }]"
57+
@click="handleToneDisplayChange('symbol')">
58+
符号声调
59+
</button>
60+
<button :class="['option-btn', { active: toneDisplay === 'number' }]"
61+
@click="handleToneDisplayChange('number')">
62+
数字声调
63+
</button>
64+
</div>
65+
</div>
66+
</div>
67+
</div>
68+
</div>
69+
</template>
70+
71+
<style scoped>
72+
.modal-overlay {
73+
position: fixed;
74+
top: 0;
75+
left: 0;
76+
right: 0;
77+
bottom: 0;
78+
background: rgba(0, 0, 0, 0.5);
79+
display: flex;
80+
align-items: center;
81+
justify-content: center;
82+
z-index: 1000;
83+
}
84+
85+
.modal-content {
86+
background: white;
87+
padding: 30px;
88+
border-radius: 8px;
89+
max-width: 400px;
90+
width: 90%;
91+
position: relative;
92+
}
93+
94+
.close-x {
95+
position: absolute;
96+
top: 15px;
97+
right: 15px;
98+
background: transparent;
99+
border: none;
100+
font-size: 28px;
101+
color: #999;
102+
cursor: pointer;
103+
padding: 0;
104+
width: 30px;
105+
height: 30px;
106+
display: flex;
107+
align-items: center;
108+
justify-content: center;
109+
line-height: 1;
110+
}
111+
112+
.close-x:hover {
113+
color: #666;
114+
}
115+
116+
h2 {
117+
color: #333;
118+
margin: 0 0 20px 0;
119+
text-align: center;
120+
font-size: 20px;
121+
}
122+
123+
.settings {
124+
text-align: left;
125+
}
126+
127+
.setting-item {
128+
margin-bottom: 20px;
129+
}
130+
131+
.setting-item>label {
132+
display: block;
133+
font-weight: bold;
134+
color: #333;
135+
margin-bottom: 12px;
136+
font-size: 15px;
137+
}
138+
139+
.button-group {
140+
display: flex;
141+
gap: 10px;
142+
}
143+
144+
.option-btn {
145+
flex: 1;
146+
padding: 12px;
147+
background: white;
148+
color: #666;
149+
border: 1px solid #ddd;
150+
border-radius: 4px;
151+
font-size: 14px;
152+
cursor: pointer;
153+
transition: all 0.2s;
154+
}
155+
156+
.option-btn:hover {
157+
border-color: #00bcd4;
158+
}
159+
160+
.option-btn.active {
161+
background: #00bcd4;
162+
color: white;
163+
border-color: #00bcd4;
164+
}
165+
</style>

0 commit comments

Comments
 (0)