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组件展示总分,提升数据可视化效果
相关推荐
phltxy2 小时前
Vue3 + Vite:从入门到实战——核心指令全解析
vue.js·vue
静小谢2 小时前
vue3实现语言切换vue-i18n
前端·javascript·vue.js
Highcharts.js2 小时前
如何使用Highcharts Flutter的官方使用文档
javascript·flutter·开发文档·highcharts
东东5162 小时前
资产管理信息系统ssm+vue
前端·javascript·vue.js
利刃大大2 小时前
【Vue】声明式导航与传参 && 编程式导航与传参 && 嵌套与守卫
javascript·vue.js·ecmascript
a1117762 小时前
拼图小游戏(HTML5、CSS3、JavaScript)
javascript·css3·html5
东东5164 小时前
基于ssm的网上房屋中介管理系统vue
前端·javascript·vue.js
harrain5 小时前
什么!vue3.4开始,v-model不能用在prop上
前端·javascript·vue.js
阿蒙Amon10 小时前
TypeScript学习-第7章:泛型(Generic)
javascript·学习·typescript