应用效果:

错误代码:

src\views\capital\CapitalAllocate\comps\CapitalAllocateWorkflowInstanceDialog.vue

// 处理使用人改变,当前数据模型 value 的数据样式为 ['CWK', '096'] 或 undefined(清除内容时为 undefined)
const handleUserPersonChange = async (value: CascaderValue) => {
  if (value) {
    allocateDatas.value[0].userPerson = String((value as string[])[1]);
  }
};

            <el-table-column prop="userMoniker" label="使用人" width="120" show-overflow-tooltip>
              <template #default="{ row }">
                <el-cascader
                  v-if="editableFieldNames.includes(`userMoniker`)"
                  ref="cascaderRef"
                  v-model="row.userPerson"
                  :options="departmentUserTreeData"
                  :show-all-levels="false"
                  clearable
                  filterable
                  @change="handleUserPersonChange" />
              </template>
            </el-table-column>

分析

请解决:handleUserPersonChange 逻辑错误:该函数写死修改 allocateDatas.value[0],而模板中 v-model 已绑定当前行 row.userPerson,导致其他行的修改会错误地更新第一行。

问题出在 handleUserPersonChange 函数错误地将所有行的修改都应用到第一行(allocateDatas.value[0]),导致其他行的选择无法正确更新。根本原因是模板中的 @change 事件没有传递当前行对象,而函数内部固定使用索引 [0]

解决方案

有两种主要解决思路:

方案一(推荐):简化级联选择器配置,无需手动转换

如果业务上只需要存储用户编码(字符串),可以配置级联选择器的 emitPath: false,让 v-model 直接绑定用户编码,移除 @change 监听。

方案二:在 @change 中传递当前行,并手动赋值

保留原有数据格式(数组),但在事件处理中接收当前行,修改对应行的 userPerson

具体修改步骤(采用方案一)

1. 修改模板中的级联选择器

添加 :props="{ emitPath: false }" 属性,使 v-model 的值变为最后一级的 value(即用户编码),而不是路径数组。同时移除 @change 事件监听。

html

<el-cascader
  v-if="editableFieldNames.includes(`userMoniker`)"
  v-model="row.userPerson"
  :options="departmentUserTreeData"
  :props="{ emitPath: false }"
  :show-all-levels="false"
  clearable
  filterable
/>

2. 删除 handleUserPersonChange 函数

因为不再需要手动转换,整个函数可以移除。

3. 类型调整(可选)

确保 CapitalAllocateDetail 类型中 userPerson 字段类型为 string | undefined(与级联选择器值类型一致)。如果之前是数组类型,需要同步修改。

为什么这样做更合理?

  • 简洁:利用组件内置功能,避免手动维护数据格式。

  • 可靠:消除索引错误,每行数据独立更新。

  • 性能:减少不必要的函数调用和重复赋值。

如果确实需要保留路径数组(例如同时存储部门和用户信息),则采用方案二,但务必在模板中传递当前行对象。

方案二备用代码(如需保留数组格式)

模板修改

html

<el-cascader
  v-if="editableFieldNames.includes(`userMoniker`)"
  v-model="row.userPerson"
  :options="departmentUserTreeData"
  :show-all-levels="false"
  clearable
  filterable
  @change="(value) => handleUserPersonChange(value, row)"
/>

函数修改

typescript

const handleUserPersonChange = (value: CascaderValue, row: CapitalAllocateDetail) => {
  if (value) {
    // 假设数组第二项为用户编码,根据实际数据结构调整
    row.userPerson = String((value as string[])[1]);
  } else {
    row.userPerson = undefined;
  }
};

总结

推荐采用方案一,因为它更简单且符合大多数业务场景。修改后,每行的级联选择器将正确绑定到对应的 userPerson 字段,无需额外处理。请根据实际业务需求选择合适方案。


// emitPath 在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值,默认为 true
// 处理使用人改变,当前emitPath默认为true,value的数据模型样式为 ['CWK', '096'] 或 undefined(清除内容时为 undefined)
const handleUserPersonChange = async (value: CascaderValue, row: CapitalAllocateDetail) => {
  if (value) {
    console.log("value", value);
    row.userPerson = String((value as string[])[1]);
  }
};

                <el-cascader
                  v-if="editableFieldNames.includes(`userMoniker`)"
                  ref="cascaderRef"
                  v-model="row.userPerson"
                  :options="departmentUserTreeData"
                  :show-all-levels="false"
                  clearable
                  filterable
                  @change="(value: CascaderValue, row: CapitalAllocateDetail) => handleUserPersonChange(value, row)" />
当前修改成这样,报错:
Unhandled error during execution of component event handler Proxy(Object) {…} at <ElCascader>
at <TableTdWrapper>
at <ElTableBody>
at <ElScrollbar>
at <ElTable>
at <ElForm>
at <ElDialogContent>
at <ElFocusTrap>
at <ElOverlay>
at <BaseTransition>
at <Transition>
at <Teleport>
at <ElDialog>
at <CapitalAllocateWorkflowInstanceDialog>
at <CapitalAllocate>
at <RouterView>
at <ElMain>
at <ElContainer>
at <ElContainer>
at <Main>
at <RouterView>
at <App> (22) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
instance.appContext.config.warnHandler @ element-plus.js?v=5daf9760:29622
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
warn$1 @ chunk-CYLFN3VO.js?v=5daf9760:2101
logError @ chunk-CYLFN3VO.js?v=5daf9760:2336
handleError @ chunk-CYLFN3VO.js?v=5daf9760:2328
(anonymous) @ chunk-CYLFN3VO.js?v=5daf9760:2282
Promise.catch
callWithAsyncErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2281
emit @ chunk-CYLFN3VO.js?v=5daf9760:8533
(anonymous) @ chunk-CYLFN3VO.js?v=5daf9760:10242
set @ element-plus.js?v=5daf9760:12431
set value @ chunk-CYLFN3VO.js?v=5daf9760:1845
onUpdate:modelValue @ element-plus.js?v=5daf9760:12929
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
callWithAsyncErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2279
emit @ chunk-CYLFN3VO.js?v=5daf9760:8533
(anonymous) @ chunk-CYLFN3VO.js?v=5daf9760:10242
(anonymous) @ element-plus.js?v=5daf9760:11992
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
callWithAsyncErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2279
baseWatchOptions.call @ chunk-CYLFN3VO.js?v=5daf9760:8342
job @ chunk-CYLFN3VO.js?v=5daf9760:2002
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
flushJobs @ chunk-CYLFN3VO.js?v=5daf9760:2480
Promise.then
queueFlush @ chunk-CYLFN3VO.js?v=5daf9760:2394
queueJob @ chunk-CYLFN3VO.js?v=5daf9760:2389
effect2.scheduler @ chunk-CYLFN3VO.js?v=5daf9760:7573
trigger @ chunk-CYLFN3VO.js?v=5daf9760:516
endBatch @ chunk-CYLFN3VO.js?v=5daf9760:574
trigger @ chunk-CYLFN3VO.js?v=5daf9760:963
set @ chunk-CYLFN3VO.js?v=5daf9760:1244
doCheck @ element-plus.js?v=5daf9760:11607
handleCheckChange @ element-plus.js?v=5daf9760:11847
doCheck @ element-plus.js?v=5daf9760:11249
handleCheck @ element-plus.js?v=5daf9760:11292
handleClick @ element-plus.js?v=5daf9760:11271
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
callWithAsyncErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2279
invoker @ chunk-CYLFN3VO.js?v=5daf9760:11271
CapitalAllocateWorkflowInstanceDialog.vue:361 Uncaught (in promise) TypeError: Cannot set properties of undefined (setting 'userPerson')
    at Proxy.handleUserPersonChange (CapitalAllocateWorkflowInstanceDialog.vue:361:1)
    at onChange (CapitalAllocateWorkflowInstanceDialog.vue:565:1)
    at callWithErrorHandling (chunk-CYLFN3VO.js?v=5daf9760:2272:19)
    at callWithAsyncErrorHandling (chunk-CYLFN3VO.js?v=5daf9760:2279:17)
    at emit (chunk-CYLFN3VO.js?v=5daf9760:8533:5)
    at chunk-CYLFN3VO.js?v=5daf9760:10242:45
    at ComputedRefImpl.set [as setter] (element-plus.js?v=5daf9760:12431:9)
    at set value (chunk-CYLFN3VO.js?v=5daf9760:1845:12)
    at onUpdate:modelValue (element-plus.js?v=5daf9760:12929:89)
    at callWithErrorHandling (chunk-CYLFN3VO.js?v=5daf9760:2272:19)
handleUserPersonChange @ CapitalAllocateWorkflowInstanceDialog.vue:361
onChange @ CapitalAllocateWorkflowInstanceDialog.vue:565
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
callWithAsyncErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2279
emit @ chunk-CYLFN3VO.js?v=5daf9760:8533
(anonymous) @ chunk-CYLFN3VO.js?v=5daf9760:10242
set @ element-plus.js?v=5daf9760:12431
set value @ chunk-CYLFN3VO.js?v=5daf9760:1845
onUpdate:modelValue @ element-plus.js?v=5daf9760:12929
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
callWithAsyncErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2279
emit @ chunk-CYLFN3VO.js?v=5daf9760:8533
(anonymous) @ chunk-CYLFN3VO.js?v=5daf9760:10242
(anonymous) @ element-plus.js?v=5daf9760:11992
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
callWithAsyncErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2279
baseWatchOptions.call @ chunk-CYLFN3VO.js?v=5daf9760:8342
job @ chunk-CYLFN3VO.js?v=5daf9760:2002
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
flushJobs @ chunk-CYLFN3VO.js?v=5daf9760:2480
Promise.then
queueFlush @ chunk-CYLFN3VO.js?v=5daf9760:2394
queueJob @ chunk-CYLFN3VO.js?v=5daf9760:2389
effect2.scheduler @ chunk-CYLFN3VO.js?v=5daf9760:7573
trigger @ chunk-CYLFN3VO.js?v=5daf9760:516
endBatch @ chunk-CYLFN3VO.js?v=5daf9760:574
trigger @ chunk-CYLFN3VO.js?v=5daf9760:963
set @ chunk-CYLFN3VO.js?v=5daf9760:1244
doCheck @ element-plus.js?v=5daf9760:11607
handleCheckChange @ element-plus.js?v=5daf9760:11847
doCheck @ element-plus.js?v=5daf9760:11249
handleCheck @ element-plus.js?v=5daf9760:11292
handleClick @ element-plus.js?v=5daf9760:11271
callWithErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2272
callWithAsyncErrorHandling @ chunk-CYLFN3VO.js?v=5daf9760:2279
invoker @ chunk-CYLFN3VO.js?v=5daf9760:11271

错误原因是在模板中编写 @change 事件时,错误地声明了两个参数,而 el-cascader 的 change 事件只传递一个参数(选中的值),导致第二个参数 row 被赋值为 undefined,从而在函数中尝试设置 undefined.userPerson 报错。

正确写法

利用模板作用域中的 row 变量,直接在事件处理中传入:

html

<el-cascader
  v-if="editableFieldNames.includes(`userMoniker`)"
  v-model="row.userPerson"
  :options="departmentUserTreeData"
  :show-all-levels="false"
  clearable
  filterable
  @change="(value) => handleUserPersonChange(value, row)"   <!-- 只传递 value,row 从模板作用域获取 -->
/>

完整修改步骤

  1. 模板修改:删除多余的参数声明,只保留 value 参数。

  2. 函数保持不变handleUserPersonChange 接收 value 和 row 两个参数,并直接修改 row.userPerson

  3. 确保作用域正确#default="{ row }" 已经存在,所以 row 在模板中可用。

修改后的模板部分:

html

<el-table-column prop="userMoniker" label="使用人" width="120" show-overflow-tooltip>
  <template #default="{ row }">
    <el-cascader
      v-if="editableFieldNames.includes(`userMoniker`)"
      v-model="row.userPerson"
      :options="departmentUserTreeData"
      :show-all-levels="false"
      clearable
      filterable
      @change="(value) => handleUserPersonChange(value, row)" />
  </template>
</el-table-column>

为什么会报错?

  • 原先的写法 @change="(value: CascaderValue, row: CapitalAllocateDetail) => ..." 在编译后会生成一个函数,该函数期望接收两个参数。但 el-cascader 的 change 事件只传递一个参数(选中的值),所以第二个参数 row 收到的是 undefined

  • 由于 row 为 undefined,执行 row.userPerson = ... 自然抛出 Cannot set properties of undefined

补充建议

如果业务上只需要存储用户编码(而非完整路径),可以设置 :props="{ emitPath: false }",这样 v-model 直接绑定用户编码,无需手动转换,连 handleUserPersonChange 函数都可以移除,代码更简洁。根据实际需求选择即可。

现在按照上述修改,错误将消失,且每行的使用人能正确更新。


正确代码示例1:@change="(value: CascaderValue) => handleUserPersonChange(value, row)"

src\views\capital\CapitalAllocate\comps\CapitalAllocateWorkflowInstanceDialog.vue

// emitPath 在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值,默认为 true
// 处理使用人改变,当前emitPath默认为true,value的数据模型样式为 ['CWK', '096'] 或 undefined(清除内容时为 undefined)
const handleUserPersonChange = async (value: CascaderValue, row: CapitalAllocateDetail) => {
  if (value) {
    row.userPerson = String((value as string[])[1]);
  }
};

            <el-table-column prop="userMoniker" label="使用人" width="120" show-overflow-tooltip>
              <template #default="{ row }">
                <el-cascader
                  v-if="editableFieldNames.includes(`userMoniker`)"
                  ref="cascaderRef"
                  v-model="row.userPerson"
                  :options="departmentUserTreeData"
                  :show-all-levels="false"
                  clearable
                  filterable
                  @change="(value: CascaderValue) => handleUserPersonChange(value, row)" />
              </template>
            </el-table-column>

正确代码示例2::props="{ emitPath: false }"

src\views\capital\CapitalAllocate\comps\CapitalAllocateWorkflowInstanceDialog.vue

            <el-table-column prop="userMoniker" label="使用人" width="120" show-overflow-tooltip>
              <template #default="{ row }">
                <el-cascader
                  v-if="editableFieldNames.includes(`userMoniker`)"
                  ref="cascaderRef"
                  v-model="row.userPerson"
                  :options="departmentUserTreeData"
                  :show-all-levels="false"
                  clearable
                  filterable
                  :props="{ emitPath: false }" />
              </template>
            </el-table-column>

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐