feat: pinyin input mode (letters->candidates->pick word); built-in dict + two-syllable combos

This commit is contained in:
2026-09-01 23:25:39 +08:00
parent f1f6efda2d
commit eea6ff6cfe
+116 -10
View File
@@ -147,6 +147,12 @@
<div class="kb-input">
<text class="kb-text">{{inputText}}</text>
</div>
<div class="kb-cands" if="{{pinyinMode && candidates.length}}">
<text class="kb-cand" onclick="pickCand0">{{candidates[0]}}</text>
<text class="kb-cand" onclick="pickCand1">{{candidates[1]}}</text>
<text class="kb-cand" onclick="pickCand2">{{candidates[2]}}</text>
<text class="kb-cand" onclick="pickCand3">{{candidates[3]}}</text>
</div>
<div class="kb-keys">
<!-- 英文模式 -->
<div class="kb-row" if="{{kbMode == 'en'}}">
@@ -169,7 +175,7 @@
<text class="kb-key kb-char" onclick="kbChar('这')">这</text><text class="kb-key kb-char" onclick="kbChar('中')">中</text><text class="kb-key kb-char" onclick="kbChar('大')">大</text><text class="kb-key kb-char" onclick="kbChar('为')">为</text><text class="kb-key kb-char" onclick="kbChar('上')">上</text><text class="kb-key kb-char" onclick="kbChar('国')">国</text><text class="kb-key kb-char" onclick="kbChar('个')">个</text><text class="kb-key kb-char" onclick="kbChar('到')">到</text><text class="kb-key kb-char" onclick="kbChar('说')">说</text><text class="kb-key kb-char" onclick="kbChar('们')">们</text>
</div>
<div class="kb-row">
<text class="kb-key kb-fn" onclick="kbToggleZh">{{kbMode == 'en' ? '中' : 'Abc'}}</text>
<text class="kb-key kb-fn" onclick="kbTogglePy">{{pinyinMode ? 'Abc' : '拼'}}</text>
<text class="kb-key kb-fn" onclick="kbSpace">空格</text>
<text class="kb-key kb-fn" onclick="kbBack">退格</text>
<text class="kb-key kb-fn" onclick="kbClose">收起</text>
@@ -313,6 +319,56 @@ export default {
showFontPicker: false,
showKeyboard: false,
kbMode: 'en',
pinyinMode: false,
pinyinBuf: '',
candidates: [],
pinDict: {
'ni': ['你','呢','尼','泥'],
'hao': ['好','号','豪','耗'],
'wo': ['我','握','沃','卧'],
'shi': ['是','时','事','十','使','式'],
'de': ['的','得','地','德'],
'zai': ['在','再','载','仔'],
'bu': ['不','部','布','步'],
'le': ['了','乐','勒','累'],
'you': ['有','又','由','油','游'],
'he': ['和','合','河','喝','盒'],
'ta': ['他','她','它','塔'],
'men': ['们','门','闷','焖'],
'zhong': ['中','种','重','钟','衷'],
'guo': ['国','过','果','裹'],
'ge': ['个','各','歌','格','隔'],
'zhang': ['长','张','章','涨','账'],
'xiang': ['想','向','相','项','像'],
'kan': ['看','砍','坎','侃'],
'xian': ['先','现','线','县','显'],
'huo': ['或','活','火','伙','货'],
'qing': ['请','情','清','轻','青'],
'qu': ['去','取','区','曲','趣'],
'shuo': ['说','硕','烁','朔'],
'wen': ['问','文','闻','温','稳'],
'shen': ['什','深','身','神','甚'],
'me': ['么','吗','么','摩'],
'zhi': ['之','只','知','直','制'],
'dao': ['到','道','倒','岛','导'],
'jin': ['今','金','进','近','仅'],
'tian': ['天','田','添','填','甜'],
'xing': ['行','星','型','形','姓'],
'neng': ['能','宁','凝'],
'can': ['参','残','餐','灿'],
'yi': ['一','以','已','意','义','易'],
'er': ['二','而','儿','耳','尔'],
'san': ['三','散','伞','伞'],
'ri': ['日','入','肉'],
'yue': ['月','越','乐','阅'],
'zhe': ['这','着','哲','折','者'],
'xia': ['下','夏','吓','虾'],
'shang': ['上','尚','商','伤'],
'liao': ['了','聊','料','廖'],
'wenxin': ['问心','文心'],
'youxiu': ['优秀','优修'],
'nihao': ['你好','尼豪']
},
showOptionPicker: false,
pickerTitle: '',
pickerOptions: [],
@@ -425,21 +481,50 @@ export default {
},
kbKey(ch) {
this.inputText = (this.inputText + ch).slice(0, 80)
},
kbPhrase(ph) {
this.inputText = (this.inputText + ph).slice(0, 80)
},
kbChar(ch) {
this.inputText = (this.inputText + ch).slice(0, 80)
if (this.pinyinMode) {
this.pinyinBuf = (this.pinyinBuf + ch).slice(0, 12)
this.computeCandidates()
} else {
this.inputText = (this.inputText + ch).slice(0, 80)
}
},
kbToggleZh() {
this.kbMode = this.kbMode == 'en' ? 'zh' : 'en'
},
kbTogglePy() {
this.pinyinMode = !this.pinyinMode
this.pinyinBuf = ''
this.candidates = []
},
computeCandidates() {
const buf = this.pinyinBuf
if (!buf) { this.candidates = []; return }
const out = []
if (this.pinDict[buf]) out.push(...this.pinDict[buf])
// 双音节组合词(如 ni+hao -> 你好)
for (let i = 1; i < buf.length; i++) {
const a = this.pinDict[buf.slice(0, i)]
const b = this.pinDict[buf.slice(i)]
if (a && b) out.push(a[0] + b[0])
}
this.candidates = out.slice(0, 4)
},
pickCand0() { this.candPick(0) },
pickCand1() { this.candPick(1) },
pickCand2() { this.candPick(2) },
pickCand3() { this.candPick(3) },
candPick(i) {
const c = this.candidates[i]
if (!c) return
this.inputText = (this.inputText + c).slice(0, 80)
this.pinyinBuf = ''
this.candidates = []
},
kbSpace() {
this.inputText = (this.inputText + ' ').slice(0, 80)
},
@@ -1198,4 +1283,25 @@ export default {
background-color: rgba(51,180,255,0.22);
border: 1px solid #33b4ff;
}
.kb-cands {
width: 100%;
flex-direction: row;
justify-content: center;
margin-bottom: 8px;
}
.kb-cand {
width: 56px;
height: 38px;
margin: 0 3px;
justify-content: center;
align-items: center;
border-radius: 9px;
background-color: rgba(51,180,255,0.16);
border: 1px solid rgba(120,200,255,0.28);
color: #e6f0ff;
font-size: 18px;
text-align: center;
}
</style>