Skip to content

数据联动 ​

数据联动是 d-render 最核心的特性之一,通过 dependOn 机制实现字段间的联动。

dependOn 是什么 ​

dependOn 指定当前字段依赖哪些字段,这些依赖字段变化时,可以触发当前字段的配置变化或值变化。

js
{
  city: {
    type: 'select',
    label: '城市',
    dependOn: ['province']
  }
}

dependOn 的两种写法 ​

字符串数组 ​

js
{
  city: {
    dependOn: ['province', 'country']
  }
}

触发当前字段配置上的全局联动函数:

  • changeConfig
  • changeValue
  • resetValue
  • changeValueByOld
  • asyncOptions

对象数组 ​

js
{
  city: {
    dependOn: [{
      key: 'province',
      effect: {
        changeValue: ({ province }) => ({
          value: province ? `${province}-derived` : ''
        })
      }
    }]
  }
}

适合:一个字段依赖多个源,但每个源触发的行为不同。

dependOn 能做什么 ​

1. changeConfig:修改当前字段配置 ​

当依赖字段变化时,动态修改当前字段的配置。

js
{
  b: {
    label: 'B',
    dependOn: ['a'],
    hideItem: true,
    changeConfig: (config, { a }) => {
      config.hideItem = a === 1
      return config
    }
  }
}

重要:changeConfig 必须返回 config。

可修改的配置包括:

  • hideItem - 是否隐藏
  • disabled - 是否禁用
  • writable - 是否可写
  • required - 是否必填
  • options - 选项列表
  • placeholder - 占位文本
  • 以及其他当前 type 支持的配置

2. changeValue:修改当前字段值 ​

当依赖字段变化时,动态修改当前字段的值。

js
{
  fullName: {
    type: 'input',
    label: '全名',
    dependOn: ['firstName', 'lastName'],
    changeValue: ({ firstName, lastName }) => ({
      value: [firstName, lastName].filter(Boolean).join(' ')
    })
  }
}

返回值格式:

js
{
  value,      // 写入主字段
  otherValue  // 写入 otherKey 字段
}

3. resetValue:清空值 ​

当依赖字段变化时,清空当前字段值。

js
{
  city: {
    type: 'select',
    dependOn: ['province'],
    resetValue: true
  }
}

适用场景:上级选项变化时,下级选项需要重选。

注意:resetValue 会同时清空主字段和 otherKey 字段。

4. changeValueByOld:基于旧值联动 ​

当你需要知道旧值是什么时使用。回调第一个参数除了当前变化字段的 key/oldValue 外,还提供 dependOldValues——dependOn 全部依赖字段变化前的完整旧值快照(结构与 values 一致)。

js
{
  city: {
    type: 'select',
    dependOn: ['province'],
    changeValueByOld: ({ key, oldValue, dependOldValues }, values) => {
      console.log('变化的字段:', key)
      console.log('旧值:', oldValue)
      console.log('依赖字段的旧值快照:', dependOldValues)
      console.log('当前所有依赖值:', values)
      return { value: undefined }
    }
  }
}

场景:依赖字段为多选时,区分新增与减少

当 dependOn 字段是多选(数组)时,仅靠 oldValue 无法方便地对比出是新增还是减少了选项。此时可以用 dependOldValues 拿到该字段变化前的完整数组,与 values 中的当前数组做对比:新增选项不处理,减少选项时清空当前字段。

js
{
  city: {
    type: 'select',
    dependOn: ['tags'], // tags 为多选字段
    changeValueByOld: ({ dependOldValues }, values) => {
      const oldTags = dependOldValues?.tags ?? []
      const newTags = values.tags ?? []
      const isDecrease = oldTags.some(tag => !newTags.includes(tag))
      if (isDecrease) {
        return { value: undefined } // 出现减少时清空
      }
      // 只有新增时不做处理
    }
  }
}

5. asyncOptions:动态拉取选项 ​

最常用的联动方式之一。类型为 TAsyncOptions | string:既可以是异步函数,也可以是设计器里保存的函数体字符串(运行时编译)。函数签名为 (dependOnValues?, outDependOnValues?, extra?) => Promise<unknown[]>,前两参与联动回调中的依赖对象一致;第三参 extra 仅部分内置组件会传入(例如远程搜索)。

js
{
  city: {
    type: 'select',
    label: '城市',
    dependOn: ['province'],
    resetValue: true,
    asyncOptions: async ({ province }) => {
      if (!province) return []
      return await fetchCityOptions(province)
    }
  }
}

dependOn 触发时传入的 values ​

回调函数会接收一个对象,包含所有依赖字段的值:

js
{
  city: {
    dependOn: ['province', 'country'],
    changeConfig: (config, { province, country }) => {
      console.log('省份:', province)
      console.log('国家:', country)
      return config
    }
  }
}

immediateChangeValue ​

控制初始化时是否执行 changeValue。

js
{
  city: {
    dependOn: ['province'],
    immediateChangeValue: true,
    changeValue: ({ province }) => ({
      value: getDefaultCity(province)
    })
  }
}

默认:初始化阶段不执行,只有真实变更后才执行。

outDependOn ​

依赖当前作用域外部的数据,常用于:

  • table 子项依赖表单外层字段
  • 嵌套结构中子层依赖父层字段
js
{
  table: {
    type: 'table',
    columns: [
      {
        key: 'city',
        config: {
          type: 'select',
          outDependOn: ['province'],
          changeConfig: (config, values, outValues) => {
            console.log('外层省份:', outValues.province)
            return config
          }
        }
      }
    ]
  }
}

联动最佳实践 ​

控制显示隐藏 ​

js
{
  detail: {
    type: 'input',
    dependOn: ['type'],
    changeConfig: (config, { type }) => {
      config.hideItem = type !== 'custom'
      return config
    }
  }
}

控制禁用状态 ​

js
{
  city: {
    type: 'select',
    dependOn: ['province'],
    changeConfig: (config, { province }) => {
      config.disabled = !province
      return config
    }
  }
}

级联选择 ​

js
{
  province: {
    type: 'select',
    label: '省份',
    asyncOptions: async () => {
      return await fetchProvinceOptions()
    }
  },
  city: {
    type: 'select',
    label: '城市',
    dependOn: ['province'],
    resetValue: true,
    changeConfig: (config, { province }) => {
      config.disabled = !province
      return config
    },
    asyncOptions: async ({ province }) => {
      if (!province) return []
      return await fetchCityOptions(province)
    }
  },
  district: {
    type: 'select',
    label: '区县',
    dependOn: ['city'],
    resetValue: true,
    changeConfig: (config, { city }) => {
      config.disabled = !city
      return config
    },
    asyncOptions: async ({ city }) => {
      if (!city) return []
      return await fetchDistrictOptions(city)
    }
  }
}

动态派生值 ​

js
{
  discount: {
    type: 'number',
    label: '折扣'
  },
  price: {
    type: 'number',
    label: '原价'
  },
  finalPrice: {
    type: 'number',
    label: '最终价格',
    dependOn: ['discount', 'price'],
    changeValue: ({ discount = 100, price = 0 }) => ({
      value: price * discount / 100
    })
  }
}

表格中的联动注意点 ​

表格只读列默认不会响应 dependOn,需要开启 dynamic: true。

js
{
  education: {
    type: 'select',
    label: '学历',
    writable: false,  // 只读
    dependOn: ['sex'],
    dynamic: true,    // 必须开启才能响应联动
    changeConfig: (config, { sex }) => {
      config.writable = !!sex
      return config
    }
  }
}

dependOn 规范建议 ​

简单依赖:用字符串数组 ​

js
dependOn: ['a']

适合:

  • 改 visible / disabled / required
  • 改 options
  • 简单 reset

复杂依赖:用对象数组 ​

js
dependOn: [
  {
    key: 'a',
    effect: {
      changeConfig: (config) => config
    }
  }
]

适合:

  • 多依赖源有不同副作用
  • 想区分某个源变化时只执行局部 effect

选择合适的联动方式 ​

  • 只改配置 → changeConfig
  • 改值 → changeValue
  • 上游切换后必须重选 → resetValue