Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | // src/libs/vee/fieldCatalog.ts
export type TFunc = (key: string, params?: Record<string, any>) => string
export type FieldCatalogOptions<Name extends string> = {
/** 例: 'domain.address' */
baseKey: string
/** 例: ['purposeId','countryCode',...] as const */
names: readonly Name[]
/** vue-i18n の t */
t: TFunc
/** validation.rules.* の既定枝 */
rulesDefaultBranch?: 'string' | 'number'
}
export type ResolveField<Name extends string> = (backendPath: string) => { name: Name }
/** 422: code+params → メッセージ(未定義は invalid フォールバック) */
export function buildMsgFromCode(
t: TFunc,
rulesDefault: 'string' | 'number' = 'string',
) {
return function msgFromCode(code?: string, params: any = {}) {
if (!code) return t('validation.invalid')
const p = params || {}
const fieldLabel =
typeof p.fieldKey === 'string' ? t(p.fieldKey) :
typeof p.field === 'string' ? t(p.field) : undefined
// rules.* は .string/.number を補完
let key = String(code)
if (key.startsWith('validation.rules.') && !/\.string$|\.number$/.test(key)) {
key += `.${rulesDefault}`
}
const len = p.len ?? p.param
const max = p.max ?? p.param
const min = p.min ?? p.param
const msg = t(key, { field: fieldLabel, len, max, min })
if (msg && msg !== key) return msg
// 旧式フォールバック
if (p?.tag === 'max' && (p?.param || p?.max)) {
return t('validation.rules.max.string', { field: fieldLabel ?? '', max: p.param || p.max })
}
if (p?.tag === 'len' && (p?.param || p?.len)) {
return t('validation.rules.len.string', { field: fieldLabel ?? '', len: p.param || p.len })
}
return t('validation.invalid')
}
}
/** フィールド・カタログ(camel専用)。i18nキー, L/PH, resolveField を提供 */
export function makeFieldCatalog<Name extends string>(opts: FieldCatalogOptions<Name>) {
const { baseKey, names, t, rulesDefaultBranch = 'string' } = opts
const labelKeyOf = (name: Name) => `${baseKey}.${name}.label`
const phKeyOf = (name: Name) => `${baseKey}.${name}.placeholder`
const L = Object.fromEntries(names.map(n => [n, t(labelKeyOf(n))])) as Record<Name, string>
const PH = Object.fromEntries(names.map(n => [n, t(phKeyOf(n))])) as Record<Name, string>
// details のキーは `${baseKey}.${camel}` で来る前提
const backendFieldMap = Object.fromEntries(names.map(n => [`${baseKey}.${n}`, n])) as Record<string, Name>
const resolveField: ResolveField<Name> = (backendPath: string) => {
if (backendFieldMap[backendPath]) return { name: backendFieldMap[backendPath] }
const last = backendPath?.split('.')?.pop() as Name
return { name: last }
}
const msgFromCode = buildMsgFromCode(t, rulesDefaultBranch)
return { L, PH, labelKeyOf, phKeyOf, resolveField, msgFromCode }
}
|