AddTargetDialog.vue
2.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<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="5rem"
: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 // 阻止关闭
}
}
// 触发确认事件,传递表单数据 (保持与 fields 结构一致)
emit('confirm', localFields.value)
return true // 允许关闭
}
return true // 取消时允许关闭
}
</script>
<style lang="less" scoped>
// 使用 Tailwind CSS 类名,无需额外样式
</style>