refactor: 抽出 replaceAllKeys 共享工具,消除两处重复的表单重置

This commit is contained in:
文睿 张 2026-09-21 15:22:23 +08:00
parent 7f19a37591
commit f3124e96cc
4 changed files with 73 additions and 15 deletions

View File

@ -0,0 +1,14 @@
// 用 values 的键值对整体替换 target 的内容:先清空 target 已有的全部键,再赋值。
//
// 不能直接用 Object.assign(target, values):assign 只覆盖 target 上**已存在**的键,
// 不会删除多余的键。表单对象上残留的 id / organization_id 这类字段就是这么漏进来的 ——
// values(新建用的空表单)里没有 id,assign 便原样保留了上一次编辑并进来的 id,
// 于是新增请求体里带上了别人的 id。这里先 delete 再赋值,保证结果只含 values 的键。
//
// target 通常是 Vue 的 reactive 对象,delete 与赋值都会触发 Proxy 的拦截,
// 视图能正常同步;同样也适用于普通对象。
export function replaceAllKeys(target, values) {
Object.keys(target).forEach(key => { delete target[key] })
Object.assign(target, values)
return target
}

View File

@ -0,0 +1,51 @@
import { describe, expect, test } from 'vitest'
import { reactive } from 'vue'
import { replaceAllKeys } from './replaceAllKeys'
describe('replaceAllKeys', () => {
test('删掉 values 里没有的键,不留残留字段', () => {
const form = { id: 1, code: 'A', name: '旧名称' }
replaceAllKeys(form, { code: '', name: '' })
expect(form).toEqual({ code: '', name: '' })
expect(Object.keys(form)).not.toContain('id')
})
test('这一点正是 Object.assign 做不到的', () => {
const assigned = { id: 1, code: 'A' }
Object.assign(assigned, { code: 'B' })
expect(assigned.id).toBe(1)
const replaced = { id: 1, code: 'A' }
replaceAllKeys(replaced, { code: 'B' })
expect(replaced.id).toBeUndefined()
})
test('覆盖同名的键并写入新增的键', () => {
const form = { code: 'A', name: '旧名称' }
replaceAllKeys(form, { code: 'A09', name: '新增类', parent_id: null })
expect(form).toEqual({ code: 'A09', name: '新增类', parent_id: null })
})
test('用于 reactive 对象时,键集合与取值都同步更新', () => {
const form = reactive({ id: 7, code: 'A' })
replaceAllKeys(form, { code: 'B', status: 'active' })
expect(Object.keys(form)).toEqual(['code', 'status'])
expect(form.id).toBeUndefined()
expect(form.code).toBe('B')
expect(form.status).toBe('active')
})
test('返回 target,便于链式书写', () => {
const form = {}
expect(replaceAllKeys(form, { code: 'A' })).toBe(form)
})
})

View File

@ -106,6 +106,7 @@
import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { codeRulesApi, materialCategoriesApi, unitsApi } from '../../api/masterData'
import { replaceAllKeys } from '../../utils/replaceAllKeys'
const tabs = [
{ key: 'categories', label: '物料分类', api: materialCategoriesApi },
@ -150,16 +151,11 @@ async function ensureCategories() {
categories.value = response.data || []
}
// 与 PartnerList.vue 同理:Object.assign 删不掉上一次编辑残留的 id,
// 新增前必须把表单整体清空。
function resetForm(values) {
Object.keys(form).forEach(key => { delete form[key] })
Object.assign(form, values)
}
async function openCreate() {
editingId.value = null
resetForm(defaultForm())
// 用 replaceAllKeys 而不是 Object.assign:openEdit 会把 row 的全部字段并进 form,
// 其中 id / organization_id 在新增时必须清掉,assign 做不到。
replaceAllKeys(form, defaultForm())
if (activeTab.value === 'rules') await ensureCategories()
dialogVisible.value = true
}

View File

@ -38,6 +38,7 @@
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { partnersApi } from '../../api/partners'
import { replaceAllKeys } from '../../utils/replaceAllKeys'
const props = defineProps({ role: { type: String, required: true }, title: { type: String, required: true } })
const rows = ref([])
@ -54,13 +55,9 @@ const form = reactive(emptyForm())
function emptyForm() { return { partner_code: '', legal_name: '', unified_credit_code: '', contact_name: '', contact_phone: '', address: '', status: 'active', is_supplier: false, is_customer: false } }
async function load() { loading.value = true; try { const response = await partnersApi.list(props.role); rows.value = response.data || [] } finally { loading.value = false } }
// Object.assign 只覆盖已存在的键,不会删掉上次编辑残留的 id,
// 所以这里先清空再整体赋值,保证新增提交的是一张干净的表单。
function resetForm(values) {
Object.keys(form).forEach(key => { delete form[key] })
Object.assign(form, values)
}
function openCreate() { editingId.value = null; resetForm(emptyForm()); dialogVisible.value = true }
// 用 replaceAllKeys 而不是 Object.assign:后者删不掉上次编辑残留的 id,
// 新增时会把别人的 id 一起提交上去。
function openCreate() { editingId.value = null; replaceAllKeys(form, emptyForm()); dialogVisible.value = true }
function openEdit(row) { editingId.value = row.id; Object.assign(form, row, { status: row[statusField.value] || 'active' }); dialogVisible.value = true }
function payload() {
return {