Skip to content

组件值格式转换

为表单组件绑定自定义转换器,用于处理特殊的数据格式或转换逻辑。通过转换器可以实现数据的双向转换:将后端数据转换为表单显示格式(正向),以及将表单数据转换为后端所需格式(逆向)。

转换器概述

转换器提供了两个转换方法:

  • toFormValue: 正向转换,将后端数据转换为表单组件显示的值
  • toValue: 逆向转换,将表单组件的值转换为提交给后端的数据格式

使用场景

转换器适用于以下场景:

  • 数据格式转换(如字符串与数组互转)
  • 数据清理(如去除空格、格式化)
  • 数据类型转换(如字符串与数字互转)
  • 复杂数据结构处理

快速开始

1. 挂载转换器

在 main.js 中全局挂载转换器

ts
import {formCreate} from '/path/to/fcDesignerPro/dist/index.es.js'

formCreate.parser({
    name: 'input',
    merge: true,
    toFormValue(val) {
        return (val || '').trim()
    },
    toValue(val) {
        return (val || '').trim()
    },
})

2. 使用转换器

转换器会在以下场景自动触发:

  • 表单回显时:调用 toFormValue 将后端数据转换为表单显示值
  • 表单提交时:调用 toValue 将表单值转换为提交数据
vue
<template>
    <form-create v-model="formData" :rule="rule" v-model:api="fApi" @submit="handleSubmit"></form-create>
</template>
<script setup>
import {ref} from 'vue';
import {formCreate} from '/path/to/fcDesignerPro/dist/index.es.js'

const formData = ref({});
const fApi = ref(null);
const rule = ref([
    {
        type: 'input',
        field: 'username',
        title: '用户名',
    }
]);

function handleSubmit(formData) {
    // 此时 formData.username 已经通过 toValue 处理,去除了前后空格
    console.log('提交数据:', formData);
}
</script>

完整示例

示例 1: 去除输入框前后空格

自动去除 input 组件输入值的前后空格,确保数据整洁。

js
import {formCreate} from '/path/to/fcDesignerPro/dist/index.es.js'

formCreate.parser({
    name: 'input',
    merge: true,
    // 正向转换:从后端数据转换为表单显示值
    toFormValue(val) {
        // 如果值为 null 或 undefined,返回空字符串
        if (val == null) {
            return '';
        }
        // 去除前后空格
        return String(val).trim();
    },
    // 逆向转换:从表单值转换为提交数据
    toValue(val) {
        // 提交时也去除前后空格
        if (val == null) {
            return '';
        }
        return String(val).trim();
    },
});

使用场景

vue
<template>
    <form-create v-model="formData" :rule="rule" v-model:api="fApi"></form-create>
</template>
<script setup>
import {ref, onMounted} from 'vue';

const formData = ref({});
const fApi = ref(null);
const rule = ref([
    {
        type: 'input',
        field: 'username',
        title: '用户名',
    }
]);

// 模拟从后端加载数据(包含前后空格)
formData.value = {
    username: '  admin  '  // 包含前后空格
};
// 表单回显时,toFormValue 会自动去除空格,显示为 'admin'

function handleSubmit(data) {
    // 用户输入 '  test  ',提交时 toValue 会自动转换为 'test'
    console.log(data.username); // 输出: 'test'
}
</script>

示例 2: Checkbox 值转换为逗号分隔字符串

将 checkbox 组件的数组值转换为逗号分隔的字符串,便于后端存储和处理。

js
import {formCreate} from '/path/to/fcDesignerPro/dist/index.es.js'

formCreate.parser({
    name: 'checkbox',
    merge: true,
    // 正向转换:从后端字符串转换为表单数组
    toFormValue(val) {
        if (!val) {
            return [];
        }
        // 如果是字符串,按逗号分割转换为数组
        if (typeof val === 'string') {
            return val.split(',').map(item => item.trim()).filter(item => item);
        }
        // 如果已经是数组,直接返回
        if (Array.isArray(val)) {
            return val;
        }
        return [];
    },
    // 逆向转换:从表单数组转换为提交字符串
    toValue(val) {
        if (!val || !Array.isArray(val)) {
            return '';
        }
        // 将数组转换为逗号分隔的字符串
        return val.filter(item => item).join(',');
    },
});

使用场景

vue
<template>
    <form-create v-model="formData" :rule="rule" v-model:api="fApi" @submit="handleSubmit"></form-create>
</template>
<script setup>
import {ref, onMounted} from 'vue';

const formData = ref({});
const fApi = ref(null);
const rule = ref([
    {
        type: 'checkbox',
        field: 'hobbies',
        title: '兴趣爱好',
        options: [
            {label: '阅读', value: 'reading'},
            {label: '运动', value: 'sports'},
            {label: '音乐', value: 'music'},
            {label: '旅行', value: 'travel'},
        ]
    }
]);

// 模拟从后端加载数据(字符串格式)
formData.value = {
    hobbies: 'reading,sports,music'  // 后端存储为字符串
};
// 表单回显时,toFormValue 会自动转换为数组 ['reading', 'sports', 'music']

function handleSubmit(data) {
    // 用户选择了 ['reading', 'travel'],提交时 toValue 会自动转换为 'reading,travel'
    console.log(data.hobbies); // 输出: 'reading,travel'
    // 可以直接提交给后端,无需额外处理
}
</script>

示例 3: 数字格式化

将字符串数字转换为数字类型,或保持数字格式。适用于需要将输入框的字符串值转换为数字类型提交给后端的场景。

js
import {formCreate} from '/path/to/fcDesignerPro/dist/index.es.js'

formCreate.parser({
    name: 'input',
    merge: true,
    // 正向转换:确保显示为字符串(表单组件需要)
    toFormValue(val) {
        if (val == null) {
            return '';
        }
        // 如果是数字,转换为字符串显示
        return String(val);
    },
    // 逆向转换:转换为数字类型提交
    toValue(val) {
        if (val === '' || val == null) {
            return null;
        }
        // 转换为数字
        const num = Number(val);
        // 如果转换失败,返回原值
        return isNaN(num) ? val : num;
    },
});

使用场景

vue
<template>
    <form-create v-model="formData" :rule="rule" v-model:api="fApi" @submit="handleSubmit"></form-create>
</template>
<script setup>
import {ref} from 'vue';

const formData = ref({});
const fApi = ref(null);
const rule = ref([
    {
        type: 'input',
        field: 'price',
        title: '商品价格',
        props: {
            type: 'number',
            placeholder: '请输入价格'
        }
    },
    {
        type: 'input',
        field: 'quantity',
        title: '库存数量',
        props: {
            type: 'number',
            placeholder: '请输入数量'
        }
    }
]);

// 模拟从后端加载数据(数字类型)
formData.value = {
    price: 99.99,      // 后端返回数字类型
    quantity: 100      // 后端返回数字类型
};
// 表单回显时,toFormValue 会自动转换为字符串 '99.99' 和 '100' 显示

function handleSubmit(data) {
    // 用户输入 '199.50' 和 '50',提交时 toValue 会自动转换为数字类型
    console.log(data.price);      // 输出: 199.5 (数字类型)
    console.log(data.quantity);   // 输出: 50 (数字类型)
    console.log(typeof data.price); // 输出: 'number'

    // 可以直接进行数学运算,无需手动转换
    const total = data.price * data.quantity;
    console.log('总价:', total); // 输出: 9975
}
</script>

示例 4: 日期格式化

处理日期格式的转换,将时间戳或日期字符串转换为统一的显示格式。适用于后端存储时间戳,但表单组件需要日期对象的场景。

js
import {formCreate} from '/path/to/fcDesignerPro/dist/index.es.js'

formCreate.parser({
    name: 'datePicker',
    merge: true,
    // 正向转换:将后端日期格式转换为组件需要的格式
    toFormValue(val) {
        if (!val) {
            return null;
        }
        // 如果是时间戳,转换为日期对象
        if (typeof val === 'number') {
            return new Date(val);
        }
        // 如果是字符串,转换为日期对象
        if (typeof val === 'string') {
            return new Date(val);
        }
        return val;
    },
    // 逆向转换:将日期对象转换为时间戳提交
    toValue(val) {
        if (!val) {
            return null;
        }
        // 如果是日期对象,转换为时间戳
        if (val instanceof Date) {
            return val.getTime();
        }
        return val;
    },
});

使用场景

vue
<template>
    <form-create v-model="formData" :rule="rule" v-model:api="fApi" @submit="handleSubmit"></form-create>
</template>
<script setup>
import {ref} from 'vue';

const formData = ref({});
const fApi = ref(null);
const rule = ref([
    {
        type: 'datePicker',
        field: 'birthday',
        title: '出生日期',
        props: {
            type: 'date',
            placeholder: '请选择日期'
        }
    },
    {
        type: 'datePicker',
        field: 'startTime',
        title: '开始时间',
        props: {
            type: 'datetime',
            placeholder: '请选择日期时间'
        }
    }
]);

// 模拟从后端加载数据(时间戳格式)
formData.value = {
    birthday: 631123200000,  // 后端返回时间戳:1990-01-01
    startTime: 1609459200000 // 后端返回时间戳:2021-01-01 00:00:00
};
// 表单回显时,toFormValue 会自动转换为日期对象显示

function handleSubmit(data) {
    // 用户选择了日期,提交时 toValue 会自动转换为时间戳
    console.log(data.birthday);   // 输出: 631123200000 (时间戳)
    console.log(data.startTime);  // 输出: 1609459200000 (时间戳)
    console.log(typeof data.birthday); // 输出: 'number'

    // 可以直接提交给后端,无需手动转换
    // 后端接口通常接收时间戳格式,便于存储和计算
}
</script>

示例 5: 使用上下文信息

转换器函数可以接收上下文参数,用于更复杂的转换逻辑。适用于需要根据组件配置动态决定转换方式的场景。

js
import {formCreate} from '/path/to/fcDesignerPro/dist/index.es.js'

formCreate.parser({
    name: 'select',
    merge: true,
    // 正向转换:使用上下文信息
    toFormValue(val, ctx) {
        // ctx 包含当前规则、表单数据等信息
        const {prop} = ctx;

        // 根据规则配置决定转换方式
        if (prop.props?.multiple) {
            // 多选模式:字符串转数组
            if (typeof val === 'string') {
                return val.split(',').filter(item => item);
            }
            return Array.isArray(val) ? val : [];
        } else {
            // 单选模式:直接返回
            return val || '';
        }
    },
    // 逆向转换:使用上下文信息
    toValue(val, ctx) {
        const {prop} = ctx;

        if (prop.props?.multiple) {
            // 多选模式:数组转字符串
            return Array.isArray(val) ? val.join(',') : '';
        } else {
            // 单选模式:直接返回
            return val || '';
        }
    },
});

使用场景

vue
<template>
    <form-create v-model="formData" :rule="rule" v-model:api="fApi" @submit="handleSubmit"></form-create>
</template>
<script setup>
import {ref} from 'vue';

const formData = ref({});
const fApi = ref(null);
const rule = ref([
    {
        type: 'select',
        field: 'category',
        title: '商品分类',
        props: {
            multiple: false,  // 单选模式
            placeholder: '请选择分类'
        },
        options: [
            {label: '电子产品', value: 'electronics'},
            {label: '服装', value: 'clothing'},
            {label: '食品', value: 'food'}
        ]
    },
    {
        type: 'select',
        field: 'tags',
        title: '商品标签',
        props: {
            multiple: true,  // 多选模式
            placeholder: '请选择标签'
        },
        options: [
            {label: '热销', value: 'hot'},
            {label: '新品', value: 'new'},
            {label: '推荐', value: 'recommend'}
        ]
    }
]);

// 模拟从后端加载数据
formData.value = {
    category: 'electronics',        // 单选:后端返回字符串
    tags: 'hot,new,recommend'       // 多选:后端返回逗号分隔字符串
};
// 表单回显时:
// - category 单选:直接显示 'electronics'
// - tags 多选:toFormValue 自动转换为数组 ['hot', 'new', 'recommend']

function handleSubmit(data) {
    // 用户操作后提交:
    // - category 单选:直接返回字符串 'clothing'
    // - tags 多选:toValue 自动转换为字符串 'hot,new'
    console.log(data.category);  // 输出: 'clothing' (字符串)
    console.log(data.tags);       // 输出: 'hot,new' (字符串,多选自动转换)

    // 同一个转换器根据组件配置自动适配不同的转换逻辑
    // 无需为单选和多选分别注册不同的转换器
}
</script>

转换器方法说明

toFormValue

功能:将后端数据转换为表单组件显示的值

调用时机

  • 表单初始化时
  • 调用 fApi.reload() 方法时
  • 设置表单数据时

参数

  • value: 后端数据的值
  • ctx: 上下文对象,包含以下属性:
    • prop: 当前组件的规则配置
    • $api: 表单 API 实例

返回值:转换后的表单显示值

toValue

功能:将表单组件的值转换为提交给后端的数据格式

调用时机

  • 表单提交时
  • 调用 fApi.submit() 方法时
  • 获取表单数据时

参数

  • value: 表单组件的当前值
  • ctx: 上下文对象,包含以下属性:
    • prop: 当前组件的规则配置
    • $api: 表单 API 实例

返回值:转换后的提交数据

注意事项

  1. 转换器作用域:转换器是按组件名称(name)绑定的,同一个组件名称只能注册一个转换器,后注册的会覆盖先注册的。

  2. 性能考虑:转换器会在每次数据转换时调用,避免在转换器中执行耗时操作。

  3. 数据一致性:确保 toFormValuetoValue 是互逆的,即 toValue(toFormValue(val)) 应该等于原始值(或等价值)。

  4. 空值处理:建议在转换器中妥善处理 nullundefined 和空字符串等边界情况。

  5. 类型安全:注意数据类型的转换,避免类型不匹配导致的错误。

数据结构

ts
type Parser = (name: string, parser: ParserConfig) => void;

interface ParserConfig {
    // 是否合并内置解析规则, 内置组件请保持 true
    merge?: true;
    // 正向转换:将后端数据转换为表单显示值
    toFormValue?: (value: any, ctx: ParserContext) => any;
    // 逆向转换:将表单值转换为提交数据
    toValue?: (value: any, ctx: ParserContext) => any;
}

interface ParserContext {
    // 当前组件的规则配置
    rule: Rule;
    // 表单 API 实例
    $api: Api;
}

merge 参数说明

  • 内置组件(如 inputselect 等)在添加解析器时,请配置 merge: true,以免覆盖系统内置的解析逻辑。
  • 自定义扩展组件若无默认解析逻辑,可以省略该参数。

通过自定义转换器,您可以灵活处理各种数据格式转换需求,实现表单数据与后端数据的无缝对接,提升开发效率和用户体验。