diff --git a/packages/uniapp-components/cascader/cascader.vue b/packages/uniapp-components/cascader/cascader.vue index 882e9c4db..db6a43b77 100644 --- a/packages/uniapp-components/cascader/cascader.vue +++ b/packages/uniapp-components/cascader/cascader.vue @@ -573,7 +573,7 @@ export default { this.isSearching = false; }, onFilterChange(e) { - const value = e?.value ?? e?.detail?.value ?? ''; + const value = coalesce(e?.value, e?.detail?.value, ''); if (!this.filterDebounced) { this.filterDebounced = debounce((kw) => this.applyFilter(kw), 200); } @@ -583,7 +583,7 @@ export default { this.resetFilter(); }, applyFilter(rawKeyword) { - const keyword = String(rawKeyword ?? '').trim(); + const keyword = String(coalesce(rawKeyword, '')).trim(); if (!keyword) { this.resetFilter(); return; diff --git a/packages/uniapp-components/form-item/form-item.vue b/packages/uniapp-components/form-item/form-item.vue index 90f34e6b3..11a00dbeb 100644 --- a/packages/uniapp-components/form-item/form-item.vue +++ b/packages/uniapp-components/form-item/form-item.vue @@ -11,7 +11,7 @@ ' ' + formItemClass + '__' + - name + + dataNameKey + ' ' + errorClasses + ' ' + @@ -86,7 +86,7 @@ import { isNumeric } from '../common/validator'; import TIcon from '../icon/icon.vue'; import usingConfig from '../mixins/using-config'; -import { validateRules, ValidateStatus } from './form-model'; +import { validateRules, ValidateStatus, getByPath } from './form-model'; import props from './props'; const parentComponentName = 'form'; @@ -160,6 +160,10 @@ export default { const contentAlign = this.dataContentAlign; return contentAlign ? `text-align: ${contentAlign}` : ''; }, + // 供 :class 拼接使用:name="pat.acctType" → "pat-acctType",避免 CSS class 里的点号被当成选择器分隔符 + dataNameKey() { + return String(this.name || '').replace(/\./g, '-'); + }, }, watch: {}, created() { @@ -217,7 +221,8 @@ export default { const { name } = this; if (name && this.form) { const { formData } = this.form; - this.initialValue = formData[name]; + // 支持 name="pat.acctType" 嵌套路径:走 form 表单层的 data 兜底(form-item 拿不到时回退到 form.data) + this.initialValue = coalesce(getByPath(formData, name), getByPath(this.form.data, name)); } }, @@ -234,7 +239,7 @@ export default { const { name } = this; if (name && this.form) { const { formData } = this.form; - return formData[name]; + return coalesce(getByPath(formData, name), getByPath(this.form.data, name)); } return undefined; }, @@ -267,7 +272,7 @@ export default { return { [this.name]: true }; } - const value = data[this.name]; + const value = getByPath(data, this.name); const context = { formData: data, name: this.name }; const results = await validateRules(value, filteredRules, context); diff --git a/packages/uniapp-components/form-item/form-model.ts b/packages/uniapp-components/form-item/form-model.ts index 8aec568a8..d2a595b1a 100644 --- a/packages/uniapp-components/form-item/form-model.ts +++ b/packages/uniapp-components/form-item/form-model.ts @@ -140,3 +140,46 @@ export async function validate(value: any, rules: any[], context?: { formData: a // 兼容已有导入 export const validateRules = validate; + +/** + * 按点号路径从对象中取值。 + * 支持 'a'、'a.b'、'a.b.c' 三种形式;当任意一级不是对象或不存在时返回 undefined。 + * 用于支持 form-item 的 name="pat.acctType" 嵌套对象属性命名。 + */ +export function getByPath(obj: any, path: string): any { + if (obj == null || !path) return undefined; + if (path.indexOf('.') === -1) return obj[path]; + + const keys = path.split('.'); + let cursor: any = obj; + for (let i = 0; i < keys.length; i += 1) { + if (cursor == null) return undefined; + cursor = cursor[keys[i]]; + } + return cursor; +} + +/** + * 按点号路径向对象中写值,沿途不存在的中转对象会被创建为 {}。 + * 返回 boolean 表示是否成功写入(仅 path 非法时返回 false)。 + */ +export function setByPath(obj: any, path: string, value: any): boolean { + if (obj == null || !path) return false; + if (path.indexOf('.') === -1) { + obj[path] = value; + return true; + } + + const keys = path.split('.'); + let cursor: any = obj; + for (let i = 0; i < keys.length - 1; i += 1) { + const key = keys[i]; + const next = cursor[key]; + if (next == null || typeof next !== 'object') { + cursor[key] = {}; + } + cursor = cursor[key]; + } + cursor[keys[keys.length - 1]] = value; + return true; +} diff --git a/packages/uniapp-components/form/_example/horizontal/index.vue b/packages/uniapp-components/form/_example/horizontal/index.vue index a3a6c433e..eb71be6a2 100644 --- a/packages/uniapp-components/form/_example/horizontal/index.vue +++ b/packages/uniapp-components/form/_example/horizontal/index.vue @@ -79,10 +79,10 @@ /> - + val > 3, message: '分数过低会影响整体评价' }], gender: [{ validator: (val) => val !== '', message: '不能为空' }], birth: [{ validator: (val) => val !== '', message: '不能为空' }], - place: [{ validator: (val) => val !== '', message: '不能为空' }], + 'address.city': [{ validator: (val) => val !== '', message: '不能为空' }], resume: [{ validator: (val) => val !== '', message: '不能为空' }], }, }; @@ -456,7 +458,7 @@ export default { onChangeCascader(e) { const { selectedOptions } = e; const placeText = selectedOptions?.map((item) => item.label).join('/'); - this.formData.place = placeText; + this.formData.address.city = placeText; this.visibleCascader = false; }, showCascader() { diff --git a/packages/uniapp-components/form/form.vue b/packages/uniapp-components/form/form.vue index a0d837d40..7d51f6ff1 100644 --- a/packages/uniapp-components/form/form.vue +++ b/packages/uniapp-components/form/form.vue @@ -12,6 +12,7 @@ import { coalesce } from '../common/utils'; import tools from '../common/utils.wxs'; import props from './props'; +import { getByPath, setByPath } from '../form-item/form-model'; const name = `${prefix}-form`; @@ -83,7 +84,8 @@ export default { // 更新表单数据 updateFormData(name, value) { const { formData } = this; - formData[name] = value; + // 支持 name="pat.acctType" 嵌套路径:按点号路径写值,自动补齐中转对象 + setByPath(formData, name, value); this.formData = formData; }, @@ -230,7 +232,7 @@ export default { if (resetType === 'empty') { this.updateFormData(child.name, this.getEmptyValue(child.name)); } else if (resetType === 'initial') { - this.updateFormData(child.name, initialData[child.name]); + this.updateFormData(child.name, getByPath(initialData, child.name)); } child.resetField(); }); @@ -265,7 +267,8 @@ export default { // 获取空值 getEmptyValue(name) { const { formData } = this; - const currentValue = formData[name]; + // 支持 name="pat.acctType" 嵌套路径 + const currentValue = getByPath(formData, name); if (Array.isArray(currentValue)) { return []; diff --git a/packages/uniapp-components/image-viewer/image-viewer.vue b/packages/uniapp-components/image-viewer/image-viewer.vue index 68bcce807..91cc70c28 100644 --- a/packages/uniapp-components/image-viewer/image-viewer.vue +++ b/packages/uniapp-components/image-viewer/image-viewer.vue @@ -113,7 +113,7 @@