Appearance
defineModel初始值不同步问题
本节目标:理解“defineModel初始值不同步问题”的核心思路,并能把它用于实际开发或面试表达。
<template>
<!--顶部操作
-->
<div class="group-action" v-if="!readonly && !disabled">
<a-button
type="primary"
ghost
size="small"
:disabled="disabled"
@click="addHolder"
>
<plus-outlined />新增股东
</a-button>
</div>
<!--股东表单列表
-->
<a-form
v-for="(holder, i) in model"
:key="i"
:model="holder"
layout="vertical"
class="holder-form"
>
<a-form-item
v-for="field in props.formFieldSettings"
:key="field.name"
:label="field.label"
:name="field.name"
:rules="[
field.required
? { required: true, message: '请输入
' + field.label }
: {},
]"
>
<component
:is="getComponent(field.controlType).component"
v-model:value="holder[field.name]"
v-bind="{
readonly,
options: field.options ?? [],
}"
/>
</a-form-item>
<!--删除按钮
-->
<div v-if="model.length > 1 && !readonly && !disabled" class="remove-box">
<a-button danger ghost size="small" @click="removeHolder(i)">
<delete-outlined />删除股东
</a-button>
</div>
<a-divider v-if="i !== model.length - 1" />
</a-form>
</template>
<script setup>
import { defineProps, defineModel, watch } from "vue";
import { PlusOutlined, DeleteOutlined } from "@ant-design/icons-vue";
import CONTROL_MAP from "../";
const props = defineProps({
formFieldSettings: { type: Array, required: true },
disabled: { type: Boolean, default: false },
readonly: { type: Boolean, default: false },
});
const model = defineModel("value", {
type: Array,
});
watch(
model,
(newModel) => {
if (!newModel) {
model.value = [{}];
}
},
{ immediate: true }
);
function addHolder() {
if (props.disabled || props.readonly) return;
model.value.push({});
}
function removeHolder(i) {
if (props.disabled || props.readonly) return;
if (model.value.length > 1) model.value.splice(i, 1);
}
function getComponent(type, more = {}) {
const map = {
text: CONTROL_MAP.MInput,
phone: CONTROL_MAP.MPhone,
country: CONTROL_MAP.MCountry,
number: CONTROL_MAP.MInputNumber,
password: CONTROL_MAP.MPasswordInput,
textarea: CONTROL_MAP.MTextarea,
select: CONTROL_MAP.MSelect,
multiple_select: CONTROL_MAP.MSelect,
checkbox: CONTROL_MAP.MCheckbox,
radio: CONTROL_MAP.MRadio,
file: CONTROL_MAP.MFileUpload,
image: CONTROL_MAP.MImageUpload,
date: CONTROL_MAP.MDatePicker,
time: CONTROL_MAP.MTimePicker,
agrement: CONTROL_MAP.MAgreementReader,
stockholder: CONTROL_MAP.MStockholder,
percent: CONTROL_MAP.MPercent,
};
const comp = map[type] || CONTROL_MAP.MInput;
return { component: comp, props: more };
}
</script>
<style scoped lang="scss">
.group-action {
display: flex;
justify-content: flex-end;
margin-bottom: 16px;
}
.holder-form {
border-radius: 4px;
margin-bottom: 24px;
}
.remove-box {
text-align: right;
margin-top: 8px;
}
</style>