feat: 汉字页输入和选择体验优化
This commit is contained in:
@@ -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] : [],
|
||||
});
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 组件的方法列表
|
||||
|
||||
Reference in New Issue
Block a user