AddTargetDialog.vue 3.07 KB
<template>
    <van-dialog
        :show="show"
        :title="title"
        width="90%"
        show-cancel-button
        confirmButtonColor="#4caf50"
        :before-close="onBeforeClose"
        @update:show="updateShow"
    >
        <div class="p-4">
            <div v-for="field in localFields" :key="field.id">
                <van-field
                    v-model="field.value"
                    label-width="4rem"
                    :label="field.label"
                    :placeholder="'请输入' + field.label"
                    :type="field.type === 'textarea' ? 'textarea' : 'text'"
                    :rows="field.type === 'textarea' ? 2 : 1"
                    :autosize="field.type === 'textarea'"
                    class="border-b border-gray-100"
                    :required="field.required"
                />
            </div>
        </div>
    </van-dialog>
</template>

<script setup>
import { ref, watch } from 'vue'
import { showToast } from 'vant'

const props = defineProps({
    /**
     * 是否显示弹窗
     */
    show: {
        type: Boolean,
        required: true
    },
    /**
     * 弹窗标题
     */
    title: {
        type: String,
        default: '添加对象'
    },
    /**
     * 表单字段配置
     * @type {Array<{id: string, label: string, type: string, required: boolean}>}
     */
    fields: {
        type: Array,
        required: true
    },
    /**
     * 初始数据(用于编辑回显)
     */
    initialValues: {
        type: Object,
        default: () => ({})
    }
})

const emit = defineEmits(['update:show', 'confirm'])

// 本地表单字段状态
const localFields = ref([])

// 监听弹窗显示状态和字段配置变化,初始化表单
watch([() => props.show, () => props.fields], ([showVal, fieldsVal]) => {
    if (showVal) {
        // 初始化字段,添加 value 属性
        localFields.value = fieldsVal.map(field => ({
            ...field,
            value: (props.initialValues && props.initialValues[field.id]) || ''
        }))
    }
}, { immediate: true, deep: true })

/**
 * 更新弹窗显示状态
 * @param {boolean} val - 显示状态
 */
const updateShow = (val) => {
    emit('update:show', val)
}

/**
 * 弹窗关闭前的回调
 * @param {string} action - 动作类型 'confirm' | 'cancel'
 * @returns {boolean} 是否允许关闭
 */
const onBeforeClose = (action) => {
    if (action === 'confirm') {
        // 校验必填项
        for (const field of localFields.value) {
            if (field.required && !field.value.trim()) {
                showToast(`请填写${field.label}`)
                return false // 阻止关闭
            }
        }

        // 收集表单数据
        const formData = localFields.value.reduce((acc, field) => {
            acc[field.id] = field.value
            return acc
        }, {})

        // 触发确认事件,传递表单数据
        emit('confirm', formData)
        return true // 允许关闭
    }
    return true // 取消时允许关闭
}
</script>

<style lang="less" scoped>
// 使用 Tailwind CSS 类名,无需额外样式
</style>