diff --git a/DEV.md b/DEV.md index 4650af6..230dcd1 100644 --- a/DEV.md +++ b/DEV.md @@ -119,6 +119,7 @@ onPullDownRefresh(async () => { - Never 使用 git stash - Never 切换分支 - Never 过度封装,保持代码简洁明了 +- Never 未确定需求就开始实现 ## Requirements diff --git a/client/src/components/AmountEditor/AmountEditor.vue b/client/src/components/AmountEditor/AmountEditor.vue new file mode 100644 index 0000000..805992a --- /dev/null +++ b/client/src/components/AmountEditor/AmountEditor.vue @@ -0,0 +1,263 @@ + + + + + diff --git a/client/src/components/AmountEditor/amount-edit.ts b/client/src/components/AmountEditor/amount-edit.ts new file mode 100644 index 0000000..6e085d9 --- /dev/null +++ b/client/src/components/AmountEditor/amount-edit.ts @@ -0,0 +1,108 @@ +export type AmountKey = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '00' | '.' +export type AmountActionKey = AmountKey | 'delete' + +export interface AmountEditOptions { + max?: number + maxLength?: number +} + +export interface AmountEditResult { + value: string + cursorIndex: number + accepted: boolean +} + +const DEFAULT_MAX = 9999999.99 +// 对应最大金额字符串 '9999999.99' 的长度。 +const DEFAULT_MAX_LENGTH = 10 +const MAX_DECIMAL_PLACES = 2 + +export function clampCursorIndex(value: string, cursorIndex: number): number { + if (cursorIndex < 0) return 0 + if (cursorIndex > value.length) return value.length + return cursorIndex +} + +export function insertAmountKey( + value: string, + cursorIndex: number, + key: AmountKey, + options: AmountEditOptions = {} +): AmountEditResult { + const safeCursor = clampCursorIndex(value, cursorIndex) + + // 替换模式:当当前值只有一个 '0' 且光标在末尾时,非 '.' 输入应替换 '0' + if (value === '0' && safeCursor === value.length && key !== '.') { + // key 可能是 '00',需要规范化 + const insert = normalizeInsert('', 0, key) + if (insert === '') return { value, cursorIndex: safeCursor, accepted: true } + const next = insert + if (!isValidAmountInput(next, options)) { + return { value, cursorIndex: safeCursor, accepted: false } + } + return { + value: next, + cursorIndex: insert.length, + accepted: true, + } + } + + const normalizedInsert = normalizeInsert(value, safeCursor, key) + const next = value.slice(0, safeCursor) + normalizedInsert + value.slice(safeCursor) + + if (!isValidAmountInput(next, options)) { + return { value, cursorIndex: safeCursor, accepted: false } + } + + return { + value: next, + cursorIndex: safeCursor + normalizedInsert.length, + accepted: true, + } +} + +export function deleteAmountChar(value: string, cursorIndex: number): AmountEditResult { + const safeCursor = clampCursorIndex(value, cursorIndex) + if (safeCursor === 0) { + return { value, cursorIndex: safeCursor, accepted: false } + } + + return { + value: value.slice(0, safeCursor - 1) + value.slice(safeCursor), + cursorIndex: safeCursor - 1, + accepted: true, + } +} + +export function applyAmountKey( + value: string, + cursorIndex: number, + key: AmountActionKey, + options: AmountEditOptions = {} +): AmountEditResult { + if (key === 'delete') return deleteAmountChar(value, cursorIndex) + return insertAmountKey(value, cursorIndex, key, options) +} + +export function isValidAmountInput(value: string, options: AmountEditOptions = {}): boolean { + if (value === '') return true + if (value.length > (options.maxLength ?? DEFAULT_MAX_LENGTH)) return false + if (!/^\d*(\.\d*)?$/.test(value)) return false + + const dotIndex = value.indexOf('.') + if (dotIndex >= 0 && value.length - dotIndex - 1 > MAX_DECIMAL_PLACES) return false + + const num = parseFloat(value) + if (!Number.isNaN(num) && num > (options.max ?? DEFAULT_MAX)) return false + + return true +} + +function normalizeInsert(value: string, cursorIndex: number, key: AmountKey): string { + if ((key === '0' || key === '00') && value === '') return '0' + if ((key === '0' || key === '00') && value === '0' && cursorIndex === value.length) return '' + if (value === '0' && cursorIndex === value.length && key !== '.') return key + // 空值时输入小数点,补前导零,保证合法格式 + if (value === '' && key === '.') return '0.' + return key +} \ No newline at end of file diff --git a/client/src/components/Numpad/Numpad.vue b/client/src/components/Numpad/Numpad.vue index 9503ef5..11f1b47 100644 --- a/client/src/components/Numpad/Numpad.vue +++ b/client/src/components/Numpad/Numpad.vue @@ -1,9 +1,15 @@ @@ -463,18 +482,7 @@ function goBack() { uni.navigateBack() } } .income .slider { transform: translateX(140rpx); } -/* 金额 */ -.amount-row { - text-align: center; padding: $space-sm 0 $space-md; -} -.currency { - font-family: 'Fredoka', sans-serif; font-size: $space-2xl; font-weight: 500; - color: $text-muted; margin-right: $space-xs; -} -.digits { - font-family: 'Fredoka', sans-serif; font-size: 72rpx; font-weight: 600; color: $text; - &.placeholder { color: #D0C4C4; } -} + /* 分类 */ .category-scroll { margin-bottom: $space-md; }