动态个数的输入框
DynamicInput 使用
查看代码
vue
<template>
<a-form layout="vertical">
<a-form-item label="单一">
<DynamicInput :options="state1.option" v-model:data="state1.data" />
{{ state1.data }}
</a-form-item>
<a-form-item label="二列">
<DynamicInput :options="state2.option" v-model:data="state2.data" />
{{ state2.data }}
</a-form-item>
<a-form-item label="三列">
<DynamicInput :options="state3.option" v-model:data="state3.data" />
{{ state3.data }}
</a-form-item>
</a-form>
</template>
<script setup lang="ts">
import { Form as AForm, FormItem as AFormItem } from 'ant-design-vue'
import { reactive } from 'vue'
import DynamicInput, { type Options } from './DynamicInput.vue'
type State1 = { value: string }
const state1 = reactive({
option: [{ key: 'value', placeholder: '请输入值' }] as Options<State1>[],
data: [] as State1[]
})
type State2 = { label: string; desc: string }
const state2 = reactive({
option: [
{ key: 'label', placeholder: '请输入标签', span: 8 },
{ key: 'desc', placeholder: '请输入描述', span: 16, required: false }
] as Options<State2>[],
data: [] as State2[]
})
type State3 = { name: string; age: string; address: string }
const state3 = reactive({
option: [
{ key: 'name', placeholder: '请输入姓名', span: 4 },
{ key: 'age', placeholder: '请输入年龄', span: 4 },
{ key: 'address', placeholder: '请输入地址', span: 16, required: false }
] as Options<State3>[],
data: [] as State3[]
})
</script>vue
<template>
<a-row :key="rowIndex" align="middle" style="margin-bottom: 4px" v-for="(row, rowIndex) in state">
<a-col flex="1">
<a-row :gutter="[4, 4]">
<a-col
:key="colIndex"
:span="col.span || 24 / options.length"
v-for="(col, colIndex) in options"
>
<a-input
:placeholder="col.placeholder"
v-model:value="row[col.key || `value${colIndex}`]"
/>
</a-col>
</a-row>
</a-col>
<a-col>
<div class="actions-container">
<PlusCircleOutlined
@click="handleAdd"
class="anticon add"
v-if="rowIndex === state.length - 1"
/>
<DeleteOutlined
@click="handleRemove(rowIndex)"
class="anticon remove"
v-if="state.length !== 1"
/>
</div>
</a-col>
</a-row>
</template>
<script lang="ts" setup>
import { DeleteOutlined, PlusCircleOutlined } from '@ant-design/icons-vue'
import { Col as ACol, Input as AInput, Row as ARow, message } from 'ant-design-vue'
import { ref, watch } from 'vue'
export type Options<T extends Record<string, any>> = {
/** 第几列属性的key */
key: keyof T & string
/** 输入框默认占位显示 */
placeholder?: string
/** a-col 的栅格占位 */
span?: number
/** 默认必填 */
required?: boolean
}
const props = withDefaults(
defineProps<{
/** 列配置 */
options?: Options<Record<string, any>>[]
}>(),
{
options: () => [{ key: 'value', placeholder: '属性' }] as Options<Record<string, any>>[]
}
)
const generateEmptyItem = (): Record<string, any> => {
const keys = props.options?.map(c => c.key) ?? []
return keys.reduce((acc, key) => {
acc[key] = ''
return acc
}, {} as Record<string, any>)
}
const data = defineModel<Record<string, any>[]>('data')
const state = ref<Record<string, any>[]>([generateEmptyItem()])
watch(
() => state.value,
stateData => {
data.value = stateData
},
{ deep: true }
)
function validate() {
const notHasNull = state.value.find(s => {
const rowHasNull = props?.options?.find((c, i) => {
const required = c.required ?? true
if (!required) {
return false
}
return !s[c.key || `value${i}`]
})
return rowHasNull
})
return !notHasNull
}
function handleAdd() {
const notHasNull = validate()
if (!notHasNull) {
message.warning('请将之前的填写完成后再新增')
return
}
state.value.push(generateEmptyItem())
}
function handleRemove(rowIndex: number) {
if (rowIndex === 0 && state.value.length === 1) {
return
}
state.value.splice(rowIndex, 1)
}
defineExpose({
validate,
initData(data: Record<string, any>[]) {
state.value = data
}
})
</script>
<style scoped>
.actions-container {
width: 60px;
padding: 0 4px;
.anticon {
font-size: 18px;
padding: 0 4px;
cursor: pointer;
transition: transform 0.1s;
&:hover {
transform: scale(1.1);
}
&.add {
color: #1677ff;
}
&.remove {
color: #ff4d4f;
}
}
}
</style>