告别写死选项!Unione Form Editor 自定义单选框组件|支持字典/接口动态加载

绝大多数低代码单选框,都卡在「选项写死」

做业务表单开发,单选框是使用频率最高的基础组件之一:状态选择、类型分类、业务判定、开关选项,几乎每个表单都离不开。

但很多开源低代码平台的原生单选框,都有一个致命短板:

只能手动写死固定选项,不支持动态加载、不支持数据联动、不支持后台字典/接口回显。

一旦业务选项需要变更、需要根据上游字段联动、需要对接业务字典,就必须改代码、发版本,完全达不到「低代码快速迭代」的初衷。

今天给大家拆解 Unione Form Editor 自研高级单选框组件,区别于普通静态单选,原生支持动态选项加载、字段联动、事件脚本、横竖布局、提示注释,是真正可落地企业复杂业务的单选组件。


Unione 单选框组件|核心能力亮点

这不是一个普通单选框,是一套动态数据选择解决方案

双布局自由切换:横向横向排版 / 竖向列表排版,适配所有表单样式

动态选项引擎:支持静态选项、字典、接口、数据集四种来源

表单字段联动:支持依赖其他表单字段,联动刷新选项

选项提示注释:每个选项可配置帮助说明,hover 悬浮展示

自定义选择事件:支持 JS 脚本,选中后触发自定义业务逻辑

智能数据转换:自动类型统一、回填、回显,适配后端数据格式

防抖刷新机制:字段频繁变更不重复请求,性能更稳定


一、全套源码逐段解析(Vue3+TS 企业级封装)

1. 模板层:简洁优雅,自带提示+动态渲染

模板采用 Ant Design 原生单选组件二次封装,自动遍历选项、支持悬浮帮助提示、布局动态绑定,UI 简洁且业务能力拉满。

<template>
  <a-radio-group :class="['unione-form-radio',layout]" v-model:value="modelValue">
    <template v-for="option in selectOptions" :key="option.value">
      <a-radio :value="option.value" @click="handleSelect(option)">
        <span>{{option.label}}</span>
        <a-tooltip :title="option.help" v-if="option.help">
          <QuestionCircleOutlined class="icon"/>
        </a-tooltip>
      </a-radio>
    </template>
  </a-radio-group>
</template>

模板核心亮点:

- 动态绑定布局 class,一键切换横/竖排版;

- 原生支持选项帮助提示,复杂选项可备注说明,提升用户填报体验;

- 每一次选择触发自定义事件,留给脚本拓展空间。

2. 核心逻辑层:动态加载+联动+转换器引擎(重点)

这是该组件碾压普通单选框的核心:内置 Unione 专属转换器、字段监听、参数回填、动态刷新机制,完整支撑企业级动态数据场景。

<script setup lang="ts">
import { computed,watch, onMounted, ref } from 'vue'
import type { PropType } from 'vue'
import type { WidgetModel } from '../../typing'
import Convertor from '../../lib/convertor'
import { utils } from 'unione-base-vue'
import useDebounce from '../../lib/debounce'
import { useUfeApi } from '../../lib/ufeApi'

// 组件属性定义
const props = defineProps({
  wid: {
    type: String,
    required: false
  },
  engine: {
    type: Object,
    required: false
  },
  widget: {
    type: Object as PropType<WidgetModel>,
    required: false
  },
  convert: {
    type: Object,
    required: false
  },
  options: {
    type: Array,
    required: false
  },
  formValue:{
    type:Object
  },
  layout:{
    type:String,
    default:'horizontal'  //vertical,horizontal
  }
})

const modelValue = defineModel<string | number>('value')
  
// 选项处理
const convertOptions = ref([])
const selectOptions = computed<any>(() => {
  if (convertOptions.value?.length) {
    return convertOptions.value
  }
  if (props.options?.length) {
    return props.options
  }
  return []
})

// 引用Value
const refValue = computed(()=>{
  const convert = widgetObj.value.convert || props.convert || {}
  if(convert.refValue){
    const fieldValue:any={}
    const fieldNames=convert.refValue.split(',')
    fieldNames.forEach((field:string)=>{
      const tmp=field.split('=')
      if(tmp.length==1){
        fieldValue[tmp[0]]=utils.obj.getValue(props.formValue,tmp[0])
      }else{
        fieldValue[tmp[0]]=utils.obj.getValue(props.formValue,tmp[1])
      }
    })
    console.log(widgetObj.value.name+' ref field:'+convert.refValue+',ref value',fieldValue)
    return fieldValue
  }
  return {};
})

const convertPrams=ref<any>()
onMounted(() => {
  // 转换器参数处理
  const convert = widgetObj.value.convert || props.convert || {}
  convertPrams.value={...(convert.params||{})}
  convert.params={...convertPrams.value}
  convert.params.body={...(convertPrams.value.body||{}),...refValue.value}

  // 加载选项
  convertor.value.load(modelValue.value || widgetObj.value.value).then((options: any) => {
    convertOptions.value = options
  })
})

// 组件定义
const widgetEvent = ref<any>({})
const widgetObj = computed(() => {
  const widget = props.engine?.widget(props.wid) || props.widget || {}

  // 组件事件处理
  if (widget.event) {
    Object.keys(widget.event).forEach((eName: string) => {
      const eventObj = widget.event[eName]
      if (!eventObj) {
        return
      }
      widgetEvent.value[eName] = null
      if (typeof eventObj === 'function') {
        widgetEvent.value[eName] = eventObj
      } else if (eventObj.enable && eventObj.scriptText) {
        widgetEvent.value[eName] = new Function('value', 'ctx', eventObj.scriptText)
      }
    })
  }

  return widget
})
const ufeApi = useUfeApi()

/**
 * 转换器
 * 1、字典:dict
 * 2、接口:rest
 * 3、数据集:dbtable
 */
const convertor = computed(() => {
  const convert = widgetObj.value.convert || props.convert || {}
  return new Convertor(convert)
})
watch(()=>modelValue.value,()=>{
  if(modelValue.value){
    modelValue.value=modelValue.value+''
  }
})

// 监听转换器变化,重新加载选项
watch(()=>widgetObj.value.convert || props.convert || {},useDebounce(() => {
  convertor.value.load().then((options: any) => {
    convertOptions.value = options
  })
}),{deep:true})

watch(()=>refValue.value,()=>{
  console.log(widgetObj.value.name+' ref value changed',refValue.value)
  if(refValue.value){
    const convert = widgetObj.value.convert || props.convert || {}
    convert.params={...convertPrams.value}
    convert.params.body={...(convertPrams.value.body||{}),...refValue.value}
    convertor.value.load(undefined,true).then((options: any) => {
      convertOptions.value = options
    })
  }
},{deep:true})

function handleSelect(option:any){
  if(widgetEvent.value?.select){
    widgetEvent.value.select.call(ufeApi,option.value,{
      option,
      formValue:props.formValue
    })
  }
}

</script>

核心逻辑深度解读

1. 多层选项优先级机制

组件内置优先级:动态转换选项 > 后台静态配置选项,自动适配字典、接口、数据集数据源,无需改代码切换。

2. 跨字段联动能力

支持绑定表单其他字段作为参数,上游字段改变,自动重载单选选项,完美适配「分类联动二级选项」场景。

3. 自定义脚本事件

选中单选框可执行自定义 JS 脚本,实现赋值、弹窗、显隐、赋值、校验等任意业务逻辑。

4. 防抖高性能更新

参数频繁变更不会频繁请求,防抖处理,保证表单流畅度。

5. 智能数据格式化

自动统一值类型,解决前后端类型不一致导致的回显失效问题。

3. 样式层:双布局适配,简洁统一

<style scoped lang="less">
.unione-form-radio {
  &.vertical {
    display: flex;
    flex-direction: column;
  }
  .icon{
    margin-left: 2px;
  }
}
</style>

极简样式设计,横向适合短选项、竖向适合多选项,和平台整体 UI 风格完全统一。


二、组件接入方式

延续 Unione 低代码极简扩展理念:

1. 引入组件源码;

2. 全局注册自定义组件;

3. 表单设计器直接拖拽使用。

接入后完整支持平台所有能力:数据联动、显隐规则、表单校验、权限控制、打印导出、多端适配。


三、业务落地价值

彻底告别硬编码选项:后台配置字典、接口动态渲染,改业务不用改代码

支持级联联动场景:分类、状态、业务类型多级联动,适配复杂台账系统

可拓展业务逻辑:选中即可触发自定义脚本,实现复杂交互

适配工业级表单:稳定、防抖、容错、类型兜底,生产系统可用

零学习成本:Vue3 标准写法,源码清晰,可二次自由拓展


四、Docker 一键部署体验完整引擎

想要体验单选框、二维码、条形码、卡片布局全套自定义组件能力,本地一键私有化部署:

git clone https://gitee.com/unione-cloud/unione-docker.git 
cd unione-docker 
docker-compose up -d

平台访问地址:http://localhost

默认登录账号/密码:admin / admin

开源仓库地址

表单引擎 unione-form-editor

Gitee:https://gitee.com/unione-cloud/unione-form-editor

GitHub:https://github.com/unione-cloud/unione-cloud

流程引擎 unione-flow-editor

Gitee:https://gitee.com/unione-cloud/unione-flow-editor

GitHub:https://github.com/unione-cloud/unione-form-editor

Unione Cloud 平台主仓库

Gitee:https://gitee.com/unione-cloud/unione-cloud

GitHub:https://github.com/unione-cloud/unione-cloud

Docker一键部署脚本仓库

Gitee:https://gitee.com/unione-cloud/unione-docker


最后想说

真正好用的低代码,不是「能拖拽」,而是能灵活适配业务变化

普通单选框只能做简单选择,而 Unione 自定义单选组件,凭借动态数据源、字段联动、自定义事件,完全可以支撑企业级复杂表单交互场景。

后续持续更新复选框、下拉选择、级联选择等全套高级自定义组件教程,欢迎点赞收藏关注!

Logo

一站式 AI 云服务平台

更多推荐