搜索 K
Appearance
Appearance
表格里的每一格、表单里的每一项、详情里的每一行,都是「一个字段 + 一个场景」交给渲染引擎选出来的组件。本篇讲清楚这套选择机制,以及内置了哪些组件——这是自定义与覆盖的基础。
字段渲染只有两个场景:
input(表单态):可编辑,对应组件 FieldInput,用在表单页与高级搜索区。cell(展示态):只读,对应组件 FieldCell,用在列表单元格与详情行。FieldInput / FieldCell 本身几乎不含逻辑——它们只做一件事:调用 selectFieldWidget(scene, field, model) 拿到真正的组件,再把 field/value 透传下去:
<!-- FieldInput.vue 几乎全部逻辑 -->
<script setup lang="ts">
const comp = computed(() => selectFieldWidget('input', props.field, props.model))
</script>
<template>
<component :is="comp" :field="field" :value="value"
@update:value="(v) => emit('update:value', v)" />
</template>selectFieldWidget 主要看字段 schema 的两个字段:
component:后端推导出的中立组件名(text / select / money …)——这是选组件的首要依据。dataType:字段数据类型(STRING / ENUM / BOOLEAN …)——component 没匹配上时的次要依据。「中立」的意思是:后端不下发「用 antd 的 a-select」,只下发 component: 'select',由前端决定 select 用什么实现。这层解耦让换 UI 库、换组件成为可能。
component | 组件 | 渲染 |
|---|---|---|
text(兜底) | TextInput | a-input 单行文本 |
textarea | TextareaInput | 多行文本域 |
number | NumberInput | 数字输入 |
money | MoneyInput | 金额输入 |
switch | SwitchInput | 开关 |
select | SelectInput | 下拉选择(用 field.options) |
date | DateInput | 日期选择 |
datetime | DatetimeInput | 日期时间选择 |
未匹配到任何注册项时,input 场景兜底回退到 TextInput。
component | 组件 | 渲染 |
|---|---|---|
| 默认(兜底) | DefaultCell | 纯文本展示 |
money | MoneyCell | 金额格式化 |
select | TagCell | 语义色 tag(取 option.label,按枚举序循环配色) |
switch | TagCell | 是/否 tag |
未匹配到时,cell 场景兜底回退到 DefaultCell。
为什么单元格比输入组件少
展示态大量字段共享「纯文本」表现(DefaultCell 兜住),只有金额、枚举/布尔需要特殊呈现(金额格式化、tag 着色)。输入态则每种数据类型都要专门的录入控件,所以输入组件更细分。
无论内置还是自定义,字段组件都遵循统一的 props 契约——这正是自定义与覆盖能即插即用的前提:
// 输入组件(scene = input)
defineProps<{ field: FieldSchema; value: any; model?: string }>()
defineEmits<{ 'update:value': [any] }>() // 双向绑定靠它
// 单元格组件(scene = cell)
defineProps<{ field: FieldSchema; value: any; record?: any; model?: string }>()
// 只读,无 emitfield:当前字段的完整 schema(含 label / options / length / readonly 等)。value:当前值。model:当前模型编码(用于按模型维度匹配,自定义时常用)。record(仅 cell):整行记录,需要联动其他字段时可用。一个内置组件长这样(看完你就能照着写自定义的):
<!-- inputs/TextInput.vue 全文 -->
<script setup lang="ts">
defineProps<{ field: FieldSchema; value: any }>()
const emit = defineEmits<{ 'update:value': [any] }>()
</script>
<template>
<a-input :value="value" :placeholder="`请输入${field.label}`"
:maxlength="field.length || undefined" :disabled="field.readonly"
allow-clear @update:value="(v) => emit('update:value', v)" />
</template>理解了「字段如何选到组件」,下一步就是改变这个选择——