主题
布局容器与家庭档案(三)
本页对应 布局容器与家庭档案,共 6 段视频,约 1 小时 4 分。
学习目标
- 能够为添加患者表单定义可选属性的患者类型,初始化响应式表单数据,并使用
v-model完成姓名、身份证号、性别等字段绑定。 - 能够通过
computed的get/set实现复选框布尔值与患者默认就诊人0/1值的双向转换,并在打开弹层时重置表单数据。 - 能够编写姓名和身份证号校验规则并绑定到表单项,在提交时通过表单实例的
validate方法完成整体校验。 - 能够根据身份证号倒数第二位判断性别,与用户选择性别不一致时弹出确认框,并根据确认或取消控制提交流程。
- 能够调用添加患者 API 完成提交,在成功后关闭弹层、刷新患者列表并提示成功。
- 能够复用添加/编辑弹层,根据是否传入患者对象完成表单回填、标题区分和提交 API 合并,并实现带确认框的删除患者流程。
知识点
本节要解决什么
这一节完成“添加患者”弹窗里的表单数据绑定。目标不是只把输入框填上,而是让表单和响应式数据对象建立稳定关系:输入时数据同步变化,打开弹窗时数据是干净的,后续校验和提交才有可靠的数据来源。整体思路是:先准备符合表单字段的 Patient 类型,再初始化响应式对象,用 v-model 绑定普通表单项;复选框因为值类型不匹配,单独用计算属性做转换;最后在打开弹窗时重置表单。
先改类型:表单只需要四个字段
为什么需要改类型?原来的 Patient 有 7 个必填属性,而添加患者表单只填 name、idCard、gender、defaultFlag 四项。如果类型仍是 7 项必填,初始化对象时就会缺少 id、genderValue、age,编辑器直接报类型错误。
怎么想?把表单不需要、但患者对象可能需要的字段改成可选。怎么做:
ts
export interface Patient {
id?: string
name: string
idCard: string
gender: number
defaultFlag: number
genderValue?: string
age?: number
}易错点:不要因为添加时不用 id 就把 id 删掉,编辑患者时还要用;也不要把 name、idCard 等表单字段改成可选,否则绑定和提交时类型约束会变松。
初始化对象并绑定普通表单项
为什么需要初始化对象?v-model 必须绑定到一个响应式数据上,而且表单项需要默认值。怎么想?把四个字段抽成 initPatient,组件初始化时解构一份,后续重置也能复用。怎么做:
ts
const initPatient: Patient = {
name: '',
idCard: '',
gender: 1, // 1 男,0 女
defaultFlag: 0 // 0 不是默认就诊人
}
const patient = ref<Patient>({ ...initPatient })模板中对应绑定:
html
真实姓名:v-model="patient.name"
身份证号:v-model="patient.idCard"
性别单选:v-model="patient.gender"
复选框:v-model="defaultFlag"调试时打开浏览器 Vue Devtools,找到 patient 对象:输入姓名后 name 变化;输入身份证号后 idCard 变化;切换性别后 gender 在 1/0 之间变化。易错点是脚本里访问 ref 要写 patient.value.name,模板里自动解包,写 patient.name 即可。
复选框的 0/1 与 true/false 不匹配
为什么单独处理?复选框组件只认 true/false,而 defaultFlag 设计成 0/1。直接 v-model="patient.defaultFlag" 会出现类型不匹配:勾选后拿到布尔值,和业务需要的数字不一致。
怎么想?展示给复选框的值用布尔,存到患者对象的值仍用 0/1,中间用计算属性转换。怎么做:
ts
const defaultFlag = computed({
get: () => patient.value.defaultFlag === 1,
set: (value: boolean) => {
patient.value.defaultFlag = value ? 1 : 0
}
})复选框绑定改为 v-model="defaultFlag"。易错点:只写 get 不写 set,复选框无法回写;set 里不要写成字符串 '1'/'0'。调试时勾选复选框,刷新 Devtools 看到 defaultFlag 为 1;取消勾选后为 0,说明转换正确。
打开弹窗时重置表单
为什么需要重置?输入内容后关闭弹窗,再次打开时旧数据仍留在表单上,用户会误以为这是新表单。怎么想?把 initPatient 当作初始快照,每次打开“添加患者”时重新赋给 patient。怎么做:
ts
const showPopup = () => {
patient.value = { ...initPatient }
// 再打开弹窗
}初始化组件时也用 { ...initPatient },这样初始值和重置值复用同一份配置。易错点:不要只重置其中一两个字段;赋值时用展开新对象,避免多个地方引用同一对象。调试:输入姓名和身份证号,关闭弹窗再打开,表单应为空;再输入、再关闭、再打开,仍为空。
小结
本节完成了四件事:修改 Patient 类型使表单字段匹配;用 ref 初始化患者对象;普通表单项用 v-model 绑定;复选框用 computed 的 get/set 在布尔值与 0/1 之间转换;最后在打开弹窗时复用 initPatient 重置表单。这样表单数据既能输入同步,也能在关闭后保持干净,为后续表单校验和提交打好了基础。
表单项校验要解决什么问题
为什么需要:添加患者表单里,真实姓名和身份证号是用户手填的,性别、默认就诊人是选择项。手填内容如果为空或格式不对,提交到后台才报错,体验很差,所以要在输入阶段先拦住。
怎么想:表单项校验只校验“当前字段”,姓名校验必填和中文格式,身份证号校验必填和 18 位格式;性别和默认就诊人是 0/1 选择,不需要规则。
怎么做:在 rules.ts 里集中写规则,再导入到患者页面,通过字段的 :rules 绑定。
易错点:rules 前要加冒号,表示绑定变量;不要写成普通字符串。姓名规则转写稿里口误说过“2 到 16”,实际按“2 到 6 个中文字符”处理。
校验规则写在 rules.ts
为什么需要:规则集中管理,页面只负责绑定,后续复用和修改都方便。
怎么想:姓名规则至少两条:必填、中文格式;身份证号也是必填、格式。格式正则可以用 VS Code 的 any-rule 插件生成:输入 @ 后选择“中文姓名”和“二代身份证 18 位”。
怎么做:
ts
// rules.ts
import type { FieldRule } from 'vant'
export const nameRules: FieldRule[] = [
{ required: true, message: '请输入真实的姓名' },
{
pattern: /^[\u4e00-\u9fa5]{2,6}$/,
message: '需要2到6个中文字符'
}
]
export const idCardRules: FieldRule[] = [
{ required: true, message: '请输入身份证号' },
{
pattern: /^\d{17}[\dXx]$/,
message: '身份证号的格式不正确'
}
]易错点:身份证最后一位可能是 X,转写稿调试时少写一个 X 会提示“格式不正确”,补上后通过,大小写 X 都可以。实际项目里建议直接采用 any-rule 生成的完整正则。
把规则绑到字段上
为什么需要:规则写好后不绑定,表单不会执行校验。
怎么想:真实姓名字段绑定 nameRules,身份证号字段绑定 idCardRules。
怎么做:
vue
<van-field
v-model="patient.name"
name="name"
label="真实姓名"
:rules="nameRules"
/>
<van-field
v-model="patient.idCard"
name="idCard"
label="身份证号"
:rules="idCardRules"
/>页面中导入:
ts
import { nameRules, idCardRules } from '@/utils/rules'易错点:字段的 name 要和规则校验对应起来;调试时先让输入框获取焦点再失去焦点,空值应提示“请输入真实的姓名”,输入一个字应提示格式不符,输入“张三”应通过。
提交时整体校验
为什么需要:表单项校验是失焦时逐个触发,用户可能没触发完就点保存,所以提交前要整体校验一次。
怎么想:保存按钮不在 van-form 内部,不能依赖表单原生提交,只能通过 ref 拿到表单实例,再调用实例的 validate 方法。
怎么做:
vue
<van-form ref="formRef">
<!-- 表单项 -->
</van-form>
<van-button type="primary" @click="onSubmit">保存</van-button>ts
import { ref } from 'vue'
import type { FormInstance } from 'vant'
const formRef = ref<FormInstance>()
const onSubmit = async () => {
await formRef.value?.validate()
console.log('校验通过')
}validate() 不传字段名时表示校验整个表单;它返回 Promise,校验失败会阻止后面的代码执行。
易错点:ref 名字要和模板里的 ref="formRef" 一致;调试时先空表点保存,应同时出现姓名和身份证号提示;两项都填正确后再点保存,控制台才打印“校验通过”。如果失败后仍执行后续逻辑,通常是漏写了 await。
本节目标与代码位置
为什么需要:表单项校验和提交时整体校验只能保证“姓名、身份证号格式”等基础规则通过,但身份证号本身包含性别信息。如果用户选择的性别与身份证号倒数第二位推导出的性别不一致,直接提交可能造成家庭档案性别错误,所以提交前要加一次“性别确认提示”。
怎么想:这一步不是普通必填校验,而是“二次确认”。它应放在整体表单校验成功之后、真正调用提交接口之前。整体校验失败时不应该弹性别确认框;只有姓名、身份证号都合法后,才有必要比对性别。
怎么做:在 onSubmit 中先 await form.value.validate(),校验通过后再执行性别判断。转写稿里的“秀 conformform dialogueog”应纠正为 ElMessageBox.confirm,“pasient”应纠正为 patient,“真的/捐子”应纠正为 gender。
易错点:不要把它放到 validate() 之前,否则用户还没填完就弹确认框;不要把它写成新的表单项规则,因为它需要读取身份证号并和 patient.gender 比较,属于提交前的业务确认。
取身份证倒数第二位推导性别
为什么需要:身份证号第 17 位,也就是倒数第二位,奇数为男、偶数为女。我们不需要额外请求后台,直接从当前表单的身份证号中取位计算即可。
怎么想:先把身份证号当成字符串,截取倒数第二位;再转成数字;最后 % 2。余数为 1 代表男,余数为 0 代表女,正好与表单里 gender 的 1/0 对应。
怎么做:转写稿中强调“从倒数第二位开始取,取到倒数第一位”,对应 slice(-2, -1),不是 slice(-2)。代码可写成:
ts
const gender = Number(patient.value.idCard.slice(-2, -1)) % 2
console.log('gender:', gender)易错点:slice(-2) 会取最后两位,参与 %2 的可能是最后一位,结果会错;取出来后是字符串,要 Number() 转数字;身份证最后一位可能是 X,但不影响倒数第二位,只要取位正确即可。
与表单性别比对
为什么需要:只有把身份证推导出的 gender 和用户选择的 patient.value.gender 比较,才能知道是否需要提示。两者一致时直接提交,不一致时才弹确认框。
怎么想:gender 是 0/1,patient.value.gender 也是 0/1。因此直接判断 gender !== patient.value.gender。不相等说明身份证信息与选择性别不一致。
怎么做:
ts
if (gender !== patient.value.gender) {
// 这里弹确认框
}易错点:不要拿 patient.value.genderValue 这种文字字段比较,本节用的是 gender;不要用 == 混用类型,保持 !== 更清晰;确认框只应在不一致时出现。
弹出确认框并控制提交流程
为什么需要:用户可能确实想以手动选择为准,也可能只是选错了。确认框给用户一次纠正机会:点确认继续提交,点取消回到表单重新选择。
怎么想:ElMessageBox.confirm 返回 Promise,所以用 await。点确认后代码继续往下走;点取消会 reject,await 后面的提交代码不会执行。提示文案要换行,避免一行太长。
怎么做:在整体校验成功后写:
ts
const onSubmit = async () => {
await form.value.validate()
const gender = Number(patient.value.idCard.slice(-2, -1)) % 2
console.log('gender:', gender)
if (gender !== patient.value.gender) {
await ElMessageBox.confirm(
'填写的性别和身份证号中的不一致,\n您确认提交吗?',
'温馨提示'
)
}
// 确认通过后,再继续提交
}易错点:忘记 await 会导致确认框还没点,代码就继续提交;点取消时 Promise 会抛错,不要用空的 try/catch 把取消异常吞掉后继续提交;确认框文案中的 \n 用于换行;确认后仍以用户选择的性别提交,取消后让用户重新选择与身份证一致的性别再提交。
调试与验证
为什么需要:性别推导和确认框都属于业务逻辑,必须用真实身份证号验证 0/1 是否取对,确认框是否只在“不一致”时出现。
怎么想:先看控制台打印的 gender。偶数身份证倒数第二位应打印 0,奇数应打印 1。再故意让表单性别与身份证推导性别相反,点保存应弹框。
怎么做:浏览器中打开添加患者,输入姓名和测试身份证号,点保存看控制台;选女但身份证推导为男时点保存,应出现“填写的性别和身份证号中的不一致,您确认提交吗?”;点取消代码不继续,点确认才继续提交。
易错点:如果控制台打印值不符合预期,优先检查 slice(-2, -1) 和 Number();如果点保存不弹框,检查是否整体校验未通过、patient.value.gender 是否与推导值一致;如果点取消仍提交,检查是否漏了 await 或异常被吞掉。
代码示例
示例 1:添加患者表单的数据绑定(含复选框计算属性 + 打开时重置)
vue
<!-- PatientForm.vue -->
<template>
<el-dialog v-model="show" title="添加患者" width="520px">
<el-form ref="formRef" :model="patient" label-width="100px">
<el-form-item label="真实姓名" prop="name">
<el-input v-model="patient.name" placeholder="请输入真实姓名" />
</el-form-item>
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="patient.idCard" placeholder="请输入身份证号" />
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="patient.gender">
<el-radio :label="1">男</el-radio>
<el-radio :label="0">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="默认就诊人" prop="defaultFlag">
<el-checkbox v-model="defaultFlag">设为默认就诊人</el-checkbox>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="show = false">取消</el-button>
<el-button type="primary" @click="onSubmit">保存</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { FormInstance } from 'element-plus'
// 表单只有 4 个字段,其余 3 个属性改成可选
export interface Patient {
id?: string
name: string
idCard: string
gender: number
defaultFlag: number
genderValue?: string
age?: number
}
// 初始化数据:组件初始化和「打开弹层重置」共用
const initPatient: Patient = {
name: '',
idCard: '',
gender: 1, // 1 男 0 女
defaultFlag: 0 // 1 默认就诊人 0 不是
}
const show = ref(false)
const formRef = ref<FormInstance>()
const patient = ref<Patient>({ ...initPatient })
// 复选框只支持 boolean,用计算属性做 0/1 <-> boolean 转换
const defaultFlag = computed({
get: () => patient.value.defaultFlag === 1,
set: (val: boolean) => {
patient.value.defaultFlag = val ? 1 : 0
}
})
// 每次打开弹层都重置表单
const showPopup = () => {
patient.value = { ...initPatient }
show.value = true
}
const onSubmit = () => {
// 校验与提交见示例 2
}
defineExpose({ showPopup })
</script>要点:
Patient里id / genderValue / age必须改成可选(?),否则initPatient只有 4 个字段会直接报类型错误。- 复选框绑定的是
defaultFlag这个计算属性,不能直接写v-model="patient.defaultFlag":el-checkbox只接受true/false,而业务数据是0/1,类型对不上。 - 计算属性必须写成
{ get, set }完整写法,只写函数形式(只读)会在勾选时报 “computed value is readonly”。 - 重置一定要在
showPopup里做,否则关掉再打开,上次输入的数据还留在表单上。
示例 2:表单项校验 + 提交时校验 + 性别确认提示(含添加/编辑合并)
ts
// src/utils/rules.ts
import type { FormItemRule } from 'element-plus'
export const nameRules: FormItemRule[] = [
{ required: true, message: '请输入真实的姓名', trigger: 'blur' },
{
pattern: /^[\u4e00-\u9fa5]{2,16}$/,
message: '请输入2到16个中文字符',
trigger: 'blur'
}
]
export const idCardRules: FormItemRule[] = [
{ required: true, message: '请输入身份证号', trigger: 'blur' },
{
pattern: /^\d{17}[\dXx]$/,
message: '身份证号的格式不正确',
trigger: 'blur'
}
]vue
<!-- PatientForm.vue -->
<template>
<el-dialog v-model="show" :title="patient.id ? '编辑患者' : '添加患者'" width="520px">
<el-form ref="formRef" :model="patient" label-width="100px">
<el-form-item label="真实姓名" prop="name" :rules="nameRules">
<el-input v-model="patient.name" placeholder="请输入真实姓名" />
</el-form-item>
<el-form-item label="身份证号" prop="idCard" :rules="idCardRules">
<el-input v-model="patient.idCard" placeholder="请输入身份证号" />
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="patient.gender">
<el-radio :label="1">男</el-radio>
<el-radio :label="0">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="默认就诊人" prop="defaultFlag">
<el-checkbox v-model="defaultFlag">设为默认就诊人</el-checkbox>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="show = false">取消</el-button>
<el-button type="primary" @click="onSubmit">保存</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, computed, nextTick } from 'vue'
import { ElMessage, ElMessageBox, type FormInstance } from 'element-plus'
import { nameRules, idCardRules } from '@/utils/rules'
import { addPatient, editPatient } from '@/api/user'
interface Patient {
id?: string
name: string
idCard: string
gender: number
defaultFlag: number
genderValue?: string
age?: number
}
const initPatient: Patient = { name: '', idCard: '', gender: 1, defaultFlag: 0 }
const show = ref(false)
const formRef = ref<FormInstance>()
const patient = ref<Patient>({ ...initPatient })
const defaultFlag = computed({
get: () => patient.value.defaultFlag === 1,
set: (val: boolean) => {
patient.value.defaultFlag = val ? 1 : 0
}
})
// 传了 item 就是编辑(回填),没传就是添加(重置)
const showPopup = async (item?: Patient) => {
if (item) {
const { id, name, idCard, gender, defaultFlag } = item
patient.value = { id, name, idCard, gender, defaultFlag }
} else {
patient.value = { ...initPatient }
}
show.value = true
await nextTick()
formRef.value?.clearValidate() // 清掉上一次残留的校验提示
}
const loadList = () => {
// 重新加载患者列表
}
const onSubmit = async () => {
// 1. 整体校验,失败会抛错,后面的代码不会执行
await formRef.value?.validate()
// 2. 取身份证号倒数第二位:奇数=男(1),偶数=女(0)
const gender = Number(patient.value.idCard.slice(-2, -1)) % 2
if (gender !== patient.value.gender) {
await ElMessageBox.confirm(
'填写的性别和身份证号中的不一致,\n您确认提交吗?',
'温馨提示',
{ type: 'warning' }
)
}
// 3. 有 id 走编辑,没有 id 走添加
if (patient.value.id) {
await editPatient(patient.value)
} else {
await addPatient(patient.value)
}
show.value = false
loadList()
ElMessage.success(patient.value.id ? '编辑成功' : '添加成功')
}
defineExpose({ showPopup })
</script>要点:
- 表单项校验用
prop+:rules绑定,prop必须和model里的字段名一致,否则规则不生效。 validate()返回 Promise,必须 await;不 await 的话校验失败也会往下走,照样提交。- 取性别用
slice(-2, -1)(倒数第二位),写成slice(-1)就取成了最后一位。 - 身份证号是字符串,取出来的字符要先
Number()再% 2。 ElMessageBox.confirm点「取消」会 reject,用 await 让它中断后面的提交逻辑即可,不要再加catch吞掉错误,否则取消也会提交。- 关闭弹层再打开要
clearValidate()(框架封装的resetValidation内部就是它),否则上次的红字提示还在。
示例 3:删除患者(按钮条件渲染 + 确认框 + 类型收窄)
ts
// src/api/user.ts
import request from '@/utils/request'
import type { Patient } from '@/types/patient'
// 添加患者 POST /patient/add
export const addPatient = (patient: Patient) => request.post('/patient/add', patient)
// 编辑患者 PUT /patient/update
export const editPatient = (patient: Patient) => request.put('/patient/update', patient)
// 删除患者 DELETE /patient/delete/{id}
export const deletePatient = (id: string) => request.delete(`/patient/delete/${id}`)vue
<!-- PatientForm.vue 的模板底部:删除按钮只在编辑时显示 -->
<template #footer>
<div class="footer">
<el-button v-if="patient.id" type="danger" plain @click="remove">删除</el-button>
<div>
<el-button @click="show = false">取消</el-button>
<el-button type="primary" @click="onSubmit">保存</el-button>
</div>
</div>
</template>
<script setup lang="ts">
// 组件内新增
const remove = async () => {
await ElMessageBox.confirm(
`您确认删除${patient.value.name}患者信息吗?`,
'温馨提示',
{ type: 'warning' }
)
const id = patient.value.id
if (!id) return // 类型收窄:id 可能是 undefined,不能直接传给 deletePatient
await deletePatient(id)
show.value = false
loadList()
ElMessage.success('删除成功')
}
</script>
<style scoped>
.footer {
display: flex;
justify-content: space-between;
}
</style>要点:
- 删除按钮必须
v-if="patient.id":添加时patient里没有id,按钮不该出现。 - 先弹
ElMessageBox.confirm再发请求,用户点「取消」时 Promise reject,请求不会发出。 patient.value.id是string | undefined,而deletePatient要string,直接传会报类型错误——先取出来判断再传,这就是转写稿里说的「类型收窄」。- 删除成功后三件事顺序固定:关闭弹层 → 重新加载列表 → 成功提示。
常见错误
复选框勾选后值变成
true/false或类型报错 → 直接把v-model绑到patient.defaultFlag(0/1),而复选框需要布尔值 → 用computed的get/set做转换:get返回defaultFlag === 1,set回写1/0。初始化表单对象时缺字段报类型错误 →
Patient类型仍有 7 个必填项,而添加表单只提交 4 项 → 将id、genderValue、age等非表单字段改为可选,再初始化表单对象。关闭弹窗再打开,上次输入还在 → 只在组件初始化时赋值,打开弹窗时没有重置 → 抽出
initPatient,在打开“添加患者”时重新赋值patient.value = { ...initPatient }。校验失败提示残留,再次打开仍有红字 → 只重置了数据,没有清空表单校验状态 → 添加场景打开弹窗时调用表单实例的
resetValidation()。点击保存时校验没拦住,或整体校验不生效 → 保存按钮不在
form内,且validateData()返回 Promise 未await→ 通过formRef.value.validateData()做整体校验,并await,通过后再发请求。编辑后变成新增,或标题/删除按钮显示错误 → 添加与编辑共用弹窗,但没有用
id或传入的item区分模式 → 有id走编辑:回填、标题“编辑患者”、显示删除按钮、调编辑 API;无id走添加:重置、标题“添加患者”、隐藏删除按钮、调添加 API。性别确认提示不触发或误触发 → 身份证倒数第二位未转数字或未与所选性别统一比较 → 取
idCard.slice(-2, -1)转数字后% 2,与gender比较;不一致时confirm,确认才提交,取消则终止。删除患者时
id类型报错 →patient.id是可选类型,删除 API 要求string→ 删除前做类型守卫,如if (!patient.value.id) return,再调用删除接口。
练习
- 完成添加患者表单的 4 个字段绑定,并将“是否默认就诊人”改为计算属性绑定,验证勾选/取消后
defaultFlag分别为1/0。 - 实现弹窗打开时的重置逻辑:添加患者时重置表单数据,同时清空校验状态;说明代码应放在哪个打开函数中。
- 为真实姓名、身份证号编写并绑定校验规则,要求姓名符合中文字符规则、身份证号为 18 位格式,提交时整体校验通过后才允许发请求。
- 将添加与编辑合并到同一弹窗:根据
patient.id区分标题、回填/重置、删除按钮显示、调用添加或编辑 API。 - 实现性别一致性确认:从身份证号推导性别,与用户选择不一致时弹出确认框;确认后继续提交,取消后终止提交。
课件图示






原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 家庭档案-添加患者-表单数据绑定 | 14:27 |
| 2 | 家庭档案-添加患者-表单校验-01-表单项和提交时校 | 13:09 |
| 3 | 家庭档案-添加患者-表单校验-02-性别确认提示 | 08:22 |
| 4 | 家庭档案-添加患者-进行提交 | 06:02 |
| 5 | 家庭档案-编辑患者 | 12:40 |
| 6 | 家庭档案-删除患者 | 09:22 |
小结
- 表单类型应按“实际提交字段”设计,非提交字段改为可选,能显著减少初始化与类型噪音。
- 组件值类型约束应在绑定层适配,不要让业务数据为了组件而改成不合理类型。
- 弹窗表单要明确生命周期:打开即初始化,关闭不依赖组件销毁来清理状态。
- 校验应分层:字段规则、提交整体校验、业务一致性确认,顺序不能乱。
- 添加/编辑共用弹窗时,
id是最稳定的模式开关,模式决定标题、回填、接口、按钮和提示。 - 提交、删除等异步动作应遵循“先校验/确认,再请求,成功后关闭、刷新列表、提示”的流程。
- 可选
id在调用删除等 API 前必须做类型守卫,避免把类型问题拖到运行时。