Skip to content

Commit 260a237

Browse files
许君山许君山
authored andcommitted
feat: add real-time autocomplete suggestions for common japanese verbs
1 parent 8ec8180 commit 260a237

2 files changed

Lines changed: 193 additions & 2 deletions

File tree

frontend/src/App.vue

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,31 @@
1111
<div class="card">
1212
<h2>动词活用工具</h2>
1313

14-
<div class="form-group">
14+
<div class="form-group position-relative">
1515
<label for="verb">动词原形</label>
1616
<input
1717
id="verb"
1818
v-model="form.verb"
1919
type="text"
2020
placeholder="例如:飲む、食べる、nomu、taberu"
2121
@keyup.enter="conjugate"
22+
@focus="showSuggestions = true"
23+
@blur="hideSuggestionsWithDelay"
24+
autocomplete="off"
2225
>
26+
<!-- 自动补全下拉框 -->
27+
<ul v-if="showSuggestions && suggestions.length > 0" class="suggestions-list">
28+
<li
29+
v-for="(item, index) in suggestions"
30+
:key="index"
31+
@mousedown.prevent="selectSuggestion(item)"
32+
>
33+
<span class="suggestion-kanji">{{ item.kanji }}</span>
34+
<span class="suggestion-kana">{{ item.kana }}</span>
35+
<span class="suggestion-romaji">{{ item.romaji }}</span>
36+
<span class="suggestion-meaning">{{ item.meaning }}</span>
37+
</li>
38+
</ul>
2339
</div>
2440

2541
<button @click="conjugate" class="btn-primary">
@@ -163,8 +179,9 @@
163179
</template>
164180

165181
<script setup>
166-
import { ref } from 'vue';
182+
import { ref, computed } from 'vue';
167183
import axios from 'axios';
184+
import commonVerbs from './common-verbs.json';
168185
169186
const form = ref({
170187
verb: ''
@@ -173,6 +190,7 @@ const form = ref({
173190
const result = ref(null);
174191
const loading = ref(false);
175192
const error = ref('');
193+
const showSuggestions = ref(false);
176194
177195
const verbTypeMap = {
178196
GODAN: '五段动词',
@@ -181,6 +199,32 @@ const verbTypeMap = {
181199
KURU: 'カ变动词'
182200
};
183201
202+
// 联想补全计算属性
203+
const suggestions = computed(() => {
204+
const query = form.value.verb.toLowerCase().trim();
205+
if (!query) return [];
206+
207+
// 匹配 kanji, kana 或 romaji 中包含用户输入的动词
208+
return commonVerbs.filter(verb => {
209+
return verb.kanji.includes(query) ||
210+
verb.kana.includes(query) ||
211+
verb.romaji.includes(query) ||
212+
verb.meaning.includes(query);
213+
}).slice(0, 8); // 最多显示8个建议
214+
});
215+
216+
const selectSuggestion = (item) => {
217+
form.value.verb = item.kanji;
218+
showSuggestions.value = false;
219+
conjugate();
220+
};
221+
222+
const hideSuggestionsWithDelay = () => {
223+
setTimeout(() => {
224+
showSuggestions.value = false;
225+
}, 200);
226+
};
227+
184228
const conjugate = async () => {
185229
error.value = '';
186230
result.value = null;
@@ -264,6 +308,71 @@ const conjugate = async () => {
264308
margin-bottom: 20px;
265309
}
266310
311+
.position-relative {
312+
position: relative;
313+
}
314+
315+
.suggestions-list {
316+
position: absolute;
317+
top: 100%;
318+
left: 0;
319+
right: 0;
320+
background: white;
321+
border: 1px solid #e0e0e0;
322+
border-radius: 8px;
323+
margin-top: 4px;
324+
padding: 0;
325+
list-style: none;
326+
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
327+
z-index: 10;
328+
max-height: 250px;
329+
overflow-y: auto;
330+
}
331+
332+
.suggestions-list li {
333+
padding: 10px 15px;
334+
cursor: pointer;
335+
display: flex;
336+
align-items: center;
337+
border-bottom: 1px solid #f5f5f5;
338+
transition: background-color 0.2s;
339+
}
340+
341+
.suggestions-list li:last-child {
342+
border-bottom: none;
343+
}
344+
345+
.suggestions-list li:hover {
346+
background-color: #f8f9fa;
347+
}
348+
349+
.suggestion-kanji {
350+
font-weight: 600;
351+
font-size: 1.1em;
352+
color: #333;
353+
min-width: 60px;
354+
}
355+
356+
.suggestion-kana {
357+
color: #667eea;
358+
font-size: 0.9em;
359+
margin-left: 10px;
360+
min-width: 80px;
361+
}
362+
363+
.suggestion-romaji {
364+
color: #999;
365+
font-size: 0.85em;
366+
margin-left: 10px;
367+
font-family: monospace;
368+
}
369+
370+
.suggestion-meaning {
371+
color: #666;
372+
font-size: 0.85em;
373+
margin-left: auto;
374+
}
375+
267376
.form-group label {
268377
display: block;
269378
margin-bottom: 8px;

frontend/src/common-verbs.json

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
[
2+
{"kanji": "する", "kana": "する", "romaji": "suru", "meaning": "做 (do)"},
3+
{"kanji": "来る", "kana": "くる", "romaji": "kuru", "meaning": "来 (come)"},
4+
{"kanji": "行く", "kana": "いく", "romaji": "iku", "meaning": "去 (go)"},
5+
{"kanji": "食べる", "kana": "たべる", "romaji": "taberu", "meaning": "吃 (eat)"},
6+
{"kanji": "飲む", "kana": "のむ", "romaji": "nomu", "meaning": "喝 (drink)"},
7+
{"kanji": "見る", "kana": "みる", "romaji": "miru", "meaning": "看 (see)"},
8+
{"kanji": "寝る", "kana": "ねる", "romaji": "neru", "meaning": "睡觉 (sleep)"},
9+
{"kanji": "起きる", "kana": "おきる", "romaji": "okiru", "meaning": "起床 (wake up)"},
10+
{"kanji": "書く", "kana": "かく", "romaji": "kaku", "meaning": "写 (write)"},
11+
{"kanji": "読む", "kana": "よむ", "romaji": "yomu", "meaning": "读 (read)"},
12+
{"kanji": "話す", "kana": "はなす", "romaji": "hanasu", "meaning": "说 (speak)"},
13+
{"kanji": "聞く", "kana": "きく", "romaji": "kiku", "meaning": "听 (listen)"},
14+
{"kanji": "会う", "kana": "あう", "romaji": "au", "meaning": "见面 (meet)"},
15+
{"kanji": "買う", "kana": "かう", "romaji": "kau", "meaning": "买 (buy)"},
16+
{"kanji": "待つ", "kana": "まつ", "romaji": "matsu", "meaning": "等 (wait)"},
17+
{"kanji": "持つ", "kana": "もつ", "romaji": "motsu", "meaning": "拿 (hold)"},
18+
{"kanji": "帰る", "kana": "かえる", "romaji": "kaeru", "meaning": "回家 (return)"},
19+
{"kanji": "分かる", "kana": "わかる", "romaji": "wakaru", "meaning": "懂 (understand)"},
20+
{"kanji": "走る", "kana": "はしる", "romaji": "hashiru", "meaning": "跑 (run)"},
21+
{"kanji": "歩く", "kana": "あるく", "romaji": "aruku", "meaning": "走 (walk)"},
22+
{"kanji": "泳ぐ", "kana": "およぐ", "romaji": "oyogu", "meaning": "游泳 (swim)"},
23+
{"kanji": "遊ぶ", "kana": "あそぶ", "romaji": "asobu", "meaning": "玩 (play)"},
24+
{"kanji": "休む", "kana": "やすむ", "romaji": "yasumu", "meaning": "休息 (rest)"},
25+
{"kanji": "働く", "kana": "はたらく", "romaji": "hataraku", "meaning": "工作 (work)"},
26+
{"kanji": "勉強する", "kana": "べんきょうする", "romaji": "benkyousuru", "meaning": "学习 (study)"},
27+
{"kanji": "終わる", "kana": "おわる", "romaji": "owaru", "meaning": "结束 (finish)"},
28+
{"kanji": "始まる", "kana": "はじまる", "romaji": "hajimaru", "meaning": "开始 (begin)"},
29+
{"kanji": "知る", "kana": "しる", "romaji": "shiru", "meaning": "知道 (know)"},
30+
{"kanji": "使う", "kana": "つかう", "romaji": "tsukau", "meaning": "使用 (use)"},
31+
{"kanji": "作る", "kana": "つくる", "romaji": "tsukuru", "meaning": "制作 (make)"},
32+
{"kanji": "思う", "kana": "おもう", "romaji": "omou", "meaning": "想 (think)"},
33+
{"kanji": "言う", "kana": "いう", "romaji": "iu", "meaning": "说 (say)"},
34+
{"kanji": "立つ", "kana": "たつ", "romaji": "tatsu", "meaning": "站立 (stand)"},
35+
{"kanji": "座る", "kana": "すわる", "romaji": "suwaru", "meaning": "坐 (sit)"},
36+
{"kanji": "死ぬ", "kana": "しぬ", "romaji": "shinu", "meaning": "死 (die)"},
37+
{"kanji": "飛ぶ", "kana": "とぶ", "romaji": "tobu", "meaning": "飞 (fly)"},
38+
{"kanji": "呼ぶ", "kana": "よぶ", "romaji": "yobu", "meaning": "叫 (call)"},
39+
{"kanji": "選ぶ", "kana": "えらぶ", "romaji": "erabu", "meaning": "选择 (choose)"},
40+
{"kanji": "頼む", "kana": "たのむ", "romaji": "tanomu", "meaning": "请求 (request)"},
41+
{"kanji": "探す", "kana": "さがす", "romaji": "sagasu", "meaning": "寻找 (search)"},
42+
{"kanji": "返す", "kana": "かえす", "romaji": "kaesu", "meaning": "归还 (return)"},
43+
{"kanji": "直す", "kana": "なおす", "romaji": "naosu", "meaning": "修理 (fix)"},
44+
{"kanji": "貸す", "kana": "かす", "romaji": "kasu", "meaning": "借出 (lend)"},
45+
{"kanji": "落とす", "kana": "おとす", "romaji": "otosu", "meaning": "掉落 (drop)"},
46+
{"kanji": "勝つ", "kana": "かつ", "romaji": "katsu", "meaning": "赢 (win)"},
47+
{"kanji": "洗う", "kana": "あらう", "romaji": "arau", "meaning": "洗 (wash)"},
48+
{"kanji": "歌う", "kana": "うたう", "romaji": "utau", "meaning": "唱 (sing)"},
49+
{"kanji": "払う", "kana": "はらう", "romaji": "harau", "meaning": "支付 (pay)"},
50+
{"kanji": "もらう", "kana": "もらう", "romaji": "morau", "meaning": "收到 (receive)"},
51+
{"kanji": "泣く", "kana": "なく", "romaji": "naku", "meaning": "哭 (cry)"},
52+
{"kanji": "笑う", "kana": "わらう", "romaji": "warau", "meaning": "笑 (laugh)"},
53+
{"kanji": "怒る", "kana": "おこる", "romaji": "okoru", "meaning": "生气 (get angry)"},
54+
{"kanji": "開ける", "kana": "あける", "romaji": "akeru", "meaning": "打开 (open)"},
55+
{"kanji": "閉める", "kana": "しめる", "romaji": "shimeru", "meaning": "关闭 (close)"},
56+
{"kanji": "乗る", "kana": "のる", "romaji": "noru", "meaning": "乘坐 (ride)"},
57+
{"kanji": "降りる", "kana": "おりる", "romaji": "oriru", "meaning": "下车 (get off)"},
58+
{"kanji": "着る", "kana": "きる", "romaji": "kiru", "meaning": "穿 (wear)"},
59+
{"kanji": "脱ぐ", "kana": "ぬぐ", "romaji": "nugu", "meaning": "脱 (take off)"},
60+
{"kanji": "忘れる", "kana": "わすれる", "romaji": "wasureru", "meaning": "忘记 (forget)"},
61+
{"kanji": "覚える", "kana": "おぼえる", "romaji": "oboeru", "meaning": "记住 (remember)"},
62+
{"kanji": "教える", "kana": "おしえる", "romaji": "oshieru", "meaning": "教 (teach)"},
63+
{"kanji": "習う", "kana": "ならう", "romaji": "narau", "meaning": "学习 (learn)"},
64+
{"kanji": "見せる", "kana": "みせる", "romaji": "miseru", "meaning": "展示 (show)"},
65+
{"kanji": "答える", "kana": "こたえる", "romaji": "kotaeru", "meaning": "回答 (answer)"},
66+
{"kanji": "質問する", "kana": "しつもんする", "romaji": "shitsumonsuru", "meaning": "提问 (ask)"},
67+
{"kanji": "信じる", "kana": "しんじる", "romaji": "shinjiru", "meaning": "相信 (believe)"},
68+
{"kanji": "売る", "kana": "うる", "romaji": "uru", "meaning": "卖 (sell)"},
69+
{"kanji": "切る", "kana": "きる", "romaji": "kiru", "meaning": "切 (cut)"},
70+
{"kanji": "出す", "kana": "だす", "romaji": "dasu", "meaning": "拿出 (take out)"},
71+
{"kanji": "入れる", "kana": "いれる", "romaji": "ireru", "meaning": "放入 (put in)"},
72+
{"kanji": "置く", "kana": "おく", "romaji": "oku", "meaning": "放置 (put)"},
73+
{"kanji": "住む", "kana": "すむ", "romaji": "sumu", "meaning": "居住 (live)"},
74+
{"kanji": "働く", "kana": "はたらく", "romaji": "hataraku", "meaning": "工作 (work)"},
75+
{"kanji": "手伝う", "kana": "てつだう", "romaji": "tetsudau", "meaning": "帮忙 (help)"},
76+
{"kanji": "頑張る", "kana": "がんばる", "romaji": "ganbaru", "meaning": "加油 (do one's best)"},
77+
{"kanji": "疲れる", "kana": "つかれる", "romaji": "tsukareru", "meaning": "累 (get tired)"},
78+
{"kanji": "急ぐ", "kana": "いそぐ", "romaji": "isogu", "meaning": "着急 (hurry)"},
79+
{"kanji": "遅れる", "kana": "おくれる", "romaji": "okureru", "meaning": "迟到 (be late)"},
80+
{"kanji": "間に合う", "kana": "まにあう", "romaji": "maniau", "meaning": "赶上 (be in time)"},
81+
{"kanji": "助ける", "kana": "たすける", "romaji": "tasukeru", "meaning": "救 (save)"}
82+
]

0 commit comments

Comments
 (0)