Vue3 + Ant Design Vue 实现 Table 表格嵌套 Radio 单选框

Vue3 + Ant Design Vue 实现 Table 表格嵌套 Radio 单选框

在后台管理系统开发中,表格(Table)是核心展示组件,而在表格中嵌套单选框(Radio)实现行内评分 / 选择是非常常见的需求。本文将基于你的实际业务代码,详细讲解如何在 Vue3 + TypeScript + Ant Design Vue 技术栈下,实现表格单元格嵌套 Radio 单选框,并完成评分联动、总分计算等核心交互逻辑。

核心需求拆解

核心需求拆解

基于你的业务场景,我们需要实现:

  1. 在 Table 表格中,为指定列嵌套 Radio 单选框,实现行内分值选择
  2. 单选框选中值实时绑定到表单状态,支持表单校验
  3. 表格底部展示总分汇总,提升数据展示完整性

完整实现代码

1. 基础组件结构(Vue3 + TS + Ant Design Vue)

vue 复制代码
<template>
  <div class="w-[80%] p-8">
    <!-- 表单容器:用于数据绑定和校验 -->
    <Form
      name="osiForm"
      :model="formState"
      :label-col="{ span: 0 }"
      :wrapper-col="{ span: 22 }"
      ref="formRef"
      scroll-to-first-error
    >
      <!-- 核心表格组件 -->
      <Table
        :columns="situationColumns"
        :data-source="situation"
        :pagination="false"
        bordered
        class="score-table"
      >
        <!-- 表格单元格自定义插槽:核心嵌套逻辑 -->
        <template #bodyCell="{ column, record }: any">
          <!-- 1. 问题文本列:仅展示文本 + 表单校验 -->
          <template v-if="column.key === 'situation'">
            <FormItem
              :name="record.value"
              :rules="[{ required: true, message: `请选择${record.label}` }]"
            >
              <div>{{ record.label }}</div>
            </FormItem>
          </template>

          <!-- 2. Y/N联动列:禁用的下拉框,展示联动结果 -->
          <template v-if="column.key === 'yn'">
            <Select
              :disabled="true"
              v-model:value="selectValue[record.value]"
              placeholder="请选择"
              style="width: 80px"
              :options="[
                { label: '是/Y', value: '1' },
                { label: '否/N', value: '2' }
              ]"
            />
          </template>

          <!-- 3. 分值列:核心 - 嵌套Radio单选框 -->
          <template v-if="column.key !== 'yn' && column.key !== 'situation'">
            <div class="flex items-center justify-center">
              <RadioGroup
                v-model:value="formState[record.value]"
                :style="{ display: 'flex', gap: '8px' }"
                @change="handleRadioChange(record.value)"
              >
                <Radio :value="column.value">{{ column.value }}分</Radio>
              </RadioGroup>
            </div>
          </template>
        </template>

        <!-- 表格汇总行:展示总分 -->
        <template #summary>
          <TableSummary>
            <TableSummaryRow>
              <TableSummaryCell style="background: #fafafa" :index="0">
                总分
              </TableSummaryCell>
              <TableSummaryCell :col-span="6" :index="1">
                {{ formState?.situationScore ?? '-' }}
              </TableSummaryCell>
            </TableSummaryRow>
          </TableSummary>
        </template>
      </Table>
    </Form>
  </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
// 引入Ant Design Vue组件
import {
  Form,
  FormItem,
  Radio,
  RadioGroup,
  Select,
  Table,
  TableSummary,
  TableSummaryCell,
  TableSummaryRow,
} from 'ant-design-vue';

// ========== 数据定义 ==========
// 表格数据源:评估问题列表
const situation = [
  { value: 'exceedExpectedFrequency', label: 'a.自伤行为发生的频率超过了你的预期?' },
  { value: 'growingSeriousness', label: 'b.自伤行为导致的后果越来越严重(例如,伤口更深,面积更大)?' },
  { value: 'mustIncreaseTolerance', label: 'c.需要更频繁或强度更大的自伤行为才能达到最初自伤所达到的效果?' },
  { value: 'consumeMuchTime', label: 'd.自伤行为或想法会消耗你大量的时间(例如,计划和思考,收集和隐藏利器等)?' },
  { value: 'outOfSelfControl', label: 'e.尽管有想要减少或控制这种行为的愿望,但却无法做到?' },
  { value: 'knowHarmfulStillDo', label: 'f.尽管已经意识到自伤行为对你的身体和/或情绪危害,仍继续该行为?' },
  { value: 'abandonImportant', label: 'g.因为自伤行为的发生导致放弃或减少了重要的社交、家庭、学校或创造性活动?' },
];

// 表格列配置:包含分值映射
const situationColumns = [
  { title: '情况选项', dataIndex: 'situation', key: 'situation', width: '29%' },
  { title: '从来没有', dataIndex: 'never', key: 'never', value: '0', width: 120, align: 'center' },
  { title: '偶尔', dataIndex: 'occasionally', key: 'occasionally', value: '1', width: 100, align: 'center' },
  { title: '有时', dataIndex: 'sometimes', key: 'sometimes', value: '2', width: 100, align: 'center' },
  { title: '经常', dataIndex: 'often', key: 'often', value: '3', width: 100, align: 'center' },
  { title: '总是', dataIndex: 'always', key: 'always', value: '4', width: 100, align: 'center' },
  { title: 'Y/N', dataIndex: 'yn', key: 'yn', width: 120, align: 'center' },
];

// ========== 状态管理 ==========
const formRef = ref<any>(); // 表单引用
const formState = ref<any>({}); // 表单状态(存储单选框选中值、总分)
const selectValue = ref<any>({}); // Y/N列联动值

// ========== 核心方法 ==========
/**
 * Radio单选框变更事件:计算总分 + 联动Y/N状态
 * @param keys 当前行的唯一标识
 */
const handleRadioChange = (keys: string) => {
  // 1. 联动Y/N状态:≥2分显示"是/Y",否则显示"否/N"
  selectValue.value[keys] = Number(formState.value[keys]) >= 2 ? '1' : '2';
  
  // 2. 计算所有行的总分
  const situationKeys = situation.map((item) => item.value);
  let total: number | undefined = 0;
  
  situationKeys.forEach((key: string) => {
    if (formState.value[key] !== null && formState.value[key] !== undefined) {
      total += Number(formState.value[key]);
    }
  });
  
  // 3. 更新总分到表单状态
  formState.value.situationScore = total;
  
  // 4. 清除当前行的表单校验提示(可选)
  formRef.value?.clearValidate(keys);
};
</script>

<style scoped>
.score-table {
  --ant-table-header-text-color: #333;
  --ant-table-body-text-color: #666;
}
/* 优化Radio单选框的展示样式 */
:deep(.ant-radio-group) {
  display: flex;
  gap: 8px;
}
</style>

关键技术点详解

1. Table 表格嵌套 Radio 的核心逻辑

Ant Design Vue 的 Table 组件提供了#bodyCell插槽,用于自定义单元格内容,这是实现嵌套的核心:

vue 复制代码
<template #bodyCell="{ column, record }: any">
  <!-- 只在分值列渲染Radio -->
  <template v-if="column.key !== 'yn' && column.key !== 'situation'">
    <RadioGroup v-model:value="formState[record.value]">
      <Radio :value="column.value">{{ column.value }}分</Radio>
    </RadioGroup>
  </template>
</template>

column:当前列的配置(包含value字段,即分值 0-4)

record:当前行的数据(包含value字段,即行唯一标识)

v-model:value="formState[record.value]":将 Radio 选中值绑定到表单状态,实现 "行 + 列" 维度的精准绑定

2. Radio 选中值的绑定与联动

  • 值绑定

    :通过

    复制代码
    formState[record.value]

    将每一行的 Radio 选中值存储到表单状态中,例如:

    • 第一行(exceedExpectedFrequency)选中 "2 分" → formState.exceedExpectedFrequency = 2
    • 第二行(growingSeriousness)选中 "3 分" → formState.growingSeriousness = 3
  • 联动逻辑

    复制代码
    handleRadioChange

    方法在 Radio 值变更时触发,完成两个核心操作:

    1. 根据分值自动设置 Y/N 列的状态(≥2 分为 "是",否则为 "否")
    2. 遍历所有行的分值,累加计算总分并展示在表格汇总行

3. 表单校验与体验优化

  • 为每个问题行添加必填校验:FormItem绑定record.value作为校验字段,确保用户必须选择分值
  • 单选框变更时清除校验提示:formRef.value.clearValidate(keys),避免选中后仍显示校验错误
  • 表格汇总行:使用TableSummary组件展示总分,提升数据可视化效果
相关推荐
ZC跨境爬虫17 小时前
跟着 MDN 学JavaScript day_10:数组——数据的有序集合
android·java·开发语言·前端·javascript
晓131318 小时前
【Cocos Creator 2.x】篇——第五章 游戏常用关键技术
前端·javascript·vue.js·游戏引擎
W_LuYi18518 小时前
Tauri + Rust + Vue 3 打造极速轻量桌面应用
java·开发语言·vue.js·rust
qq43569470118 小时前
Vue03
javascript·vue.js
樱花的浪漫18 小时前
Typescript、Zod基础
前端·javascript·人工智能·语言模型·自然语言处理·typescript
用户5495916575019 小时前
TinyVue Tree树形控件完全指南
vue.js
竹林81819 小时前
监听智能合约事件,我用 wagmi v2 踩了三天坑,终于找到了稳定方案
前端·javascript
用户8524950718419 小时前
Bun 到底是什么?一个比 Node.js "更快更香"的 JS 运行时
javascript·程序员
Momo__19 小时前
SSR 懒水合四件套 — 99%的人不知道 Vue 3.5 藏了这些水合策略
前端·vue.js·性能优化
riuphan19 小时前
JavaScript 事件循环:单线程异步编程的核心机制
前端·javascript