feat: 汉字页输入和选择体验优化
This commit is contained in:
@@ -70,9 +70,8 @@ createPage(
|
|||||||
currentMode: HANDWRITING_SHEET_WORKSHEET_ID as HandwritingSheetMode,
|
currentMode: HANDWRITING_SHEET_WORKSHEET_ID as HandwritingSheetMode,
|
||||||
modeOptions: HANDWRITING_SHEET_MODE_OPTIONS,
|
modeOptions: HANDWRITING_SHEET_MODE_OPTIONS,
|
||||||
hasContent: false,
|
hasContent: false,
|
||||||
|
/** 练习字表(唯一数据源);inputValue 与弹窗均为其编辑入口 */
|
||||||
words: [] as string[],
|
words: [] as string[],
|
||||||
inputWords: [] as string[],
|
|
||||||
selectedWords: [] as string[],
|
|
||||||
inputValue: '',
|
inputValue: '',
|
||||||
showSelectWordPopup: false,
|
showSelectWordPopup: false,
|
||||||
pickerCurrentTab: 0,
|
pickerCurrentTab: 0,
|
||||||
@@ -256,34 +255,35 @@ createPage(
|
|||||||
: CUSTOM_MAX_WORDS;
|
: 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) {
|
syncWordsFromInput(value: string, showLimitToast: boolean) {
|
||||||
const max = this.getMaxWordsForMode();
|
const max = this.getMaxWordsForMode();
|
||||||
const selectedWords = this.data.selectedWords as string[];
|
const parsed = this.splitToSingleChars(value);
|
||||||
const maxInputCount = Math.max(0, max - selectedWords.length);
|
const nextWords = parsed.slice(0, max);
|
||||||
const nextInputWords = this.splitToSingleChars(value).slice(
|
|
||||||
0,
|
|
||||||
maxInputCount,
|
|
||||||
);
|
|
||||||
const nextWords = [...nextInputWords, ...selectedWords];
|
|
||||||
|
|
||||||
if (
|
if (showLimitToast && parsed.length > max) {
|
||||||
showLimitToast &&
|
|
||||||
nextInputWords.length < this.splitToSingleChars(value).length
|
|
||||||
) {
|
|
||||||
wx.showToast({
|
wx.showToast({
|
||||||
title: `最多输入 ${maxInputCount} 个字`,
|
title: `最多输入 ${max} 个字`,
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData(
|
this.applyWords(nextWords);
|
||||||
{
|
|
||||||
inputValue: nextInputWords.join(''),
|
|
||||||
inputWords: nextInputWords,
|
|
||||||
words: nextWords,
|
|
||||||
},
|
|
||||||
() => this.renderPracticeContent().catch(console.error),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
openSelectWordPopup() {
|
openSelectWordPopup() {
|
||||||
@@ -304,10 +304,9 @@ createPage(
|
|||||||
|
|
||||||
onChangeWord(e: WechatMiniprogram.CustomEvent) {
|
onChangeWord(e: WechatMiniprogram.CustomEvent) {
|
||||||
const max = this.getMaxWordsForMode();
|
const max = this.getMaxWordsForMode();
|
||||||
const nextSelectedWords = e.detail.selectedWords as string[];
|
const nextWords = e.detail.selectedWords as string[];
|
||||||
const inputWords = this.data.inputWords as string[];
|
|
||||||
|
|
||||||
if (inputWords.length + nextSelectedWords.length > max) {
|
if (nextWords.length > max) {
|
||||||
wx.showToast({
|
wx.showToast({
|
||||||
title: `最多只能添加 ${max} 个字`,
|
title: `最多只能添加 ${max} 个字`,
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
@@ -315,61 +314,22 @@ createPage(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData(
|
this.applyWords(nextWords, { closePicker: true });
|
||||||
{
|
|
||||||
selectedWords: nextSelectedWords,
|
|
||||||
words: [...inputWords, ...nextSelectedWords],
|
|
||||||
showSelectWordPopup: false,
|
|
||||||
},
|
|
||||||
() => this.renderPracticeContent().catch(console.error),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteWordChip(e: WechatMiniprogram.TouchEvent) {
|
deleteWordChip(e: WechatMiniprogram.TouchEvent) {
|
||||||
const index = Number(e.currentTarget.dataset.index);
|
const index = Number(e.currentTarget.dataset.index);
|
||||||
if (Number.isNaN(index)) return;
|
if (Number.isNaN(index)) return;
|
||||||
|
|
||||||
const inputWords = [...(this.data.inputWords as string[])];
|
const words = [...(this.data.words as string[])];
|
||||||
const selectedWords = [...(this.data.selectedWords as string[])];
|
if (index < 0 || index >= words.length) return;
|
||||||
|
|
||||||
if (index < inputWords.length) {
|
words.splice(index, 1);
|
||||||
inputWords.splice(index, 1);
|
this.applyWords(words);
|
||||||
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),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
clearWords() {
|
clearWords() {
|
||||||
this.setData(
|
this.applyWords([], { closePicker: true });
|
||||||
{
|
|
||||||
words: [],
|
|
||||||
inputWords: [],
|
|
||||||
selectedWords: [],
|
|
||||||
inputValue: '',
|
|
||||||
showSelectWordPopup: false,
|
|
||||||
},
|
|
||||||
() => this.renderPracticeContent().catch(console.error),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
initDefaultWords() {
|
initDefaultWords() {
|
||||||
@@ -383,15 +343,7 @@ createPage(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData(
|
this.applyWords(defaults);
|
||||||
{
|
|
||||||
words: defaults,
|
|
||||||
inputWords: defaults,
|
|
||||||
selectedWords: [],
|
|
||||||
inputValue: defaults.join(''),
|
|
||||||
},
|
|
||||||
() => this.renderPracticeContent().catch(console.error),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,15 +356,7 @@ createPage(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData(
|
this.applyWords(defaults);
|
||||||
{
|
|
||||||
words: defaults,
|
|
||||||
inputWords: defaults,
|
|
||||||
selectedWords: [],
|
|
||||||
inputValue: defaults.join(''),
|
|
||||||
},
|
|
||||||
() => this.renderPracticeContent().catch(console.error),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshWords() {
|
refreshWords() {
|
||||||
@@ -439,15 +383,7 @@ createPage(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData(
|
this.applyWords(supported);
|
||||||
{
|
|
||||||
words: supported,
|
|
||||||
inputWords: supported,
|
|
||||||
selectedWords: [],
|
|
||||||
inputValue: supported.join(''),
|
|
||||||
},
|
|
||||||
() => this.renderPracticeContent().catch(console.error),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
splitToSingleChars(text: string): string[] {
|
splitToSingleChars(text: string): string[] {
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
|
|
||||||
<word-picker
|
<word-picker
|
||||||
show="{{showSelectWordPopup}}"
|
show="{{showSelectWordPopup}}"
|
||||||
selectedWords="{{selectedWords}}"
|
selectedWords="{{words}}"
|
||||||
currentTab="{{pickerCurrentTab}}"
|
currentTab="{{pickerCurrentTab}}"
|
||||||
max="{{pickerMax}}"
|
max="{{pickerMax}}"
|
||||||
bind:onClose="closeSelectWordPopup"
|
bind:onClose="closeSelectWordPopup"
|
||||||
|
|||||||
@@ -45,9 +45,23 @@ Component({
|
|||||||
},
|
},
|
||||||
observers: {
|
observers: {
|
||||||
cardList(newVal: CardList) {
|
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);
|
const selectedWords = newVal.map((item) => item.word);
|
||||||
this.setData({ selectedWords });
|
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