feat: 汉字页输入和选择体验优化

This commit is contained in:
R524809
2026-05-25 14:18:28 +08:00
parent 2e8a03de57
commit 4379358a41
3 changed files with 48 additions and 98 deletions
@@ -70,9 +70,8 @@ createPage(
currentMode: HANDWRITING_SHEET_WORKSHEET_ID as HandwritingSheetMode,
modeOptions: HANDWRITING_SHEET_MODE_OPTIONS,
hasContent: false,
/** 练习字表(唯一数据源);inputValue 与弹窗均为其编辑入口 */
words: [] as string[],
inputWords: [] as string[],
selectedWords: [] as string[],
inputValue: '',
showSelectWordPopup: false,
pickerCurrentTab: 0,
@@ -256,34 +255,35 @@ createPage(
: CUSTOM_MAX_WORDS;
},
/** 以 words 为准,同步输入框镜像并刷新预览 */
applyWords(nextWords: string[], options?: { closePicker?: boolean }) {
const inputValue = nextWords.join('');
const patch: Record<string, unknown> = {
words: nextWords,
inputValue,
};
if (options?.closePicker) {
patch.showSelectWordPopup = false;
}
this.setData(patch, () =>
this.renderPracticeContent().catch(console.error),
);
},
syncWordsFromInput(value: string, showLimitToast: boolean) {
const max = this.getMaxWordsForMode();
const selectedWords = this.data.selectedWords as string[];
const maxInputCount = Math.max(0, max - selectedWords.length);
const nextInputWords = this.splitToSingleChars(value).slice(
0,
maxInputCount,
);
const nextWords = [...nextInputWords, ...selectedWords];
const parsed = this.splitToSingleChars(value);
const nextWords = parsed.slice(0, max);
if (
showLimitToast &&
nextInputWords.length < this.splitToSingleChars(value).length
) {
if (showLimitToast && parsed.length > max) {
wx.showToast({
title: `最多输入 ${maxInputCount} 个字`,
title: `最多输入 ${max} 个字`,
icon: 'none',
});
}
this.setData(
{
inputValue: nextInputWords.join(''),
inputWords: nextInputWords,
words: nextWords,
},
() => this.renderPracticeContent().catch(console.error),
);
this.applyWords(nextWords);
},
openSelectWordPopup() {
@@ -304,10 +304,9 @@ createPage(
onChangeWord(e: WechatMiniprogram.CustomEvent) {
const max = this.getMaxWordsForMode();
const nextSelectedWords = e.detail.selectedWords as string[];
const inputWords = this.data.inputWords as string[];
const nextWords = e.detail.selectedWords as string[];
if (inputWords.length + nextSelectedWords.length > max) {
if (nextWords.length > max) {
wx.showToast({
title: `最多只能添加 ${max} 个字`,
icon: 'none',
@@ -315,61 +314,22 @@ createPage(
return;
}
this.setData(
{
selectedWords: nextSelectedWords,
words: [...inputWords, ...nextSelectedWords],
showSelectWordPopup: false,
},
() => this.renderPracticeContent().catch(console.error),
);
this.applyWords(nextWords, { closePicker: true });
},
deleteWordChip(e: WechatMiniprogram.TouchEvent) {
const index = Number(e.currentTarget.dataset.index);
if (Number.isNaN(index)) return;
const inputWords = [...(this.data.inputWords as string[])];
const selectedWords = [...(this.data.selectedWords as string[])];
const words = [...(this.data.words as string[])];
if (index < 0 || index >= words.length) return;
if (index < inputWords.length) {
inputWords.splice(index, 1);
this.setData(
{
inputWords,
inputValue: inputWords.join(''),
words: [...inputWords, ...selectedWords],
},
() => this.renderPracticeContent().catch(console.error),
);
return;
}
const selectedIndex = index - inputWords.length;
if (selectedIndex < 0 || selectedIndex >= selectedWords.length)
return;
selectedWords.splice(selectedIndex, 1);
this.setData(
{
selectedWords,
words: [...inputWords, ...selectedWords],
},
() => this.renderPracticeContent().catch(console.error),
);
words.splice(index, 1);
this.applyWords(words);
},
clearWords() {
this.setData(
{
words: [],
inputWords: [],
selectedWords: [],
inputValue: '',
showSelectWordPopup: false,
},
() => this.renderPracticeContent().catch(console.error),
);
this.applyWords([], { closePicker: true });
},
initDefaultWords() {
@@ -383,15 +343,7 @@ createPage(
return;
}
this.setData(
{
words: defaults,
inputWords: defaults,
selectedWords: [],
inputValue: defaults.join(''),
},
() => this.renderPracticeContent().catch(console.error),
);
this.applyWords(defaults);
return;
}
@@ -404,15 +356,7 @@ createPage(
return;
}
this.setData(
{
words: defaults,
inputWords: defaults,
selectedWords: [],
inputValue: defaults.join(''),
},
() => this.renderPracticeContent().catch(console.error),
);
this.applyWords(defaults);
},
refreshWords() {
@@ -439,15 +383,7 @@ createPage(
return;
}
this.setData(
{
words: supported,
inputWords: supported,
selectedWords: [],
inputValue: supported.join(''),
},
() => this.renderPracticeContent().catch(console.error),
);
this.applyWords(supported);
},
splitToSingleChars(text: string): string[] {
@@ -148,7 +148,7 @@
<word-picker
show="{{showSelectWordPopup}}"
selectedWords="{{selectedWords}}"
selectedWords="{{words}}"
currentTab="{{pickerCurrentTab}}"
max="{{pickerMax}}"
bind:onClose="closeSelectWordPopup"
@@ -45,9 +45,23 @@ Component({
},
observers: {
cardList(newVal: CardList) {
if (!Array.isArray(newVal)) return;
// 练字贴等页面只传 selectedWords、cardList 恒为 [],勿用空列表覆盖已选字
if (newVal.length === 0) {
const fromProp = this.properties.selectedWords as string[];
if (Array.isArray(fromProp) && fromProp.length > 0) return;
}
const selectedWords = newVal.map((item) => item.word);
this.setData({ selectedWords });
},
/** 仅在弹窗打开时从父组件同步一次,避免监听 selectedWords 与 setData 形成死循环 */
show(show: boolean) {
if (!show) return;
const incoming = this.properties.selectedWords as string[];
this.setData({
selectedWords: Array.isArray(incoming) ? [...incoming] : [],
});
},
},
/**
* 组件的方法列表