普通视图

发现新文章,点击刷新页面。
昨天 — 2026年1月26日首页

若依框架实现表格列按需显示功能

作者 独守一隅
2026年1月26日 11:01

一、项目背景

本文基于若依框架(RuoYi v3.9.1)进行二次开发,实现表格列的按需显示功能。若依框架是一个基于SpringBoot+Vue3前后端分离的Java快速开发框架,前端技术栈采用Vue3 + Element Plus + Vite。

技术栈版本信息

  • 框架版本:RuoYi v3.9.1
  • Vue版本:3.5.16
  • Element Plus版本:2.10.7
  • Vite版本:6.3.5

二、问题描述

在若依框架的优惠券管理模块中,默认显示所有表格列,包括序号、优惠券名称、面值、有效期开始、有效期结束、状态、使用说明等。但在实际业务场景中,用户可能只需要关注部分字段,过多的列会影响表格的可读性和用户体验。

参考若依框架内置的用户管理页面,发现该页面已经实现了表格列的按需显示功能,用户可以通过右上角的列配置按钮自由选择要显示的列。因此,我们需要在优惠券管理页面中实现相同的功能。

三、解决方案

通过分析用户管理页面的实现方式,我们发现若依框架已经内置了列显示/隐藏的组件RightToolbar,只需要进行以下三步改造:

  1. RightToolbar组件中传入columns属性
  2. 定义列配置对象columns
  3. 为每个表格列添加v-if条件控制显示/隐藏

四、实现步骤

步骤1:修改RightToolbar组件,传入columns属性

在优惠券管理页面的工具栏区域,找到right-toolbar组件,添加:columns="columns"属性。

修改位置src/views/feature/coupon/index.vue 第92行

修改前

<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>

修改后

<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>

步骤2:定义columns响应式对象

在script setup部分,定义columns响应式对象,配置每个列的标签和显示状态。

修改位置src/views/feature/coupon/index.vue 第187-193行

添加代码

const columns = ref({
  couponName: { label: '优惠券名称', visible: true },
  couponValue: { label: '面值', visible: true },
  startTime: { label: '有效期开始', visible: true },
  endTime: { label: '有效期结束', visible: true },
  status: { label: '状态', visible: true },
  remark: { label: '使用说明', visible: true }
})

说明

  • 每个属性对应一个表格列的key
  • label属性用于在列配置面板中显示列名
  • visible属性控制列的默认显示状态(true为显示,false为隐藏)

步骤3:为表格列添加v-if条件

为每个需要控制显示/隐藏的表格列添加v-if条件,绑定到columns对象中对应列的visible属性。

修改位置src/views/feature/coupon/index.vue 第97-115行

修改前

<el-table-column label="优惠券名称" align="center" prop="couponName" />
<el-table-column label="面值" align="center" prop="couponValue" />
<el-table-column label="有效期开始" align="center" prop="startTime" width="180">
  <template #default="scope">
    <span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
  </template>
</el-table-column>
<el-table-column label="有效期结束" align="center" prop="endTime" width="180">
  <template #default="scope">
    <span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
  </template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
  <template #default="scope">
    <dict-tag :options="common_status" :value="scope.row.status"/>
  </template>
</el-table-column>
<el-table-column label="使用说明" align="center" prop="remark" />

修改后

<el-table-column label="优惠券名称" align="center" prop="couponName" v-if="columns.couponName.visible" />
<el-table-column label="面值" align="center" prop="couponValue" v-if="columns.couponValue.visible" />
<el-table-column label="有效期开始" align="center" prop="startTime" width="180" v-if="columns.startTime.visible">
  <template #default="scope">
    <span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
  </template>
</el-table-column>
<el-table-column label="有效期结束" align="center" prop="endTime" width="180" v-if="columns.endTime.visible">
  <template #default="scope">
    <span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
  </template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" v-if="columns.status.visible">
  <template #default="scope">
    <dict-tag :options="common_status" :value="scope.row.status"/>
  </template>
</el-table-column>
<el-table-column label="使用说明" align="center" prop="remark" v-if="columns.remark.visible" />

注意

  • 序号列和操作列不需要添加v-if条件,因为它们应该始终显示
  • 复选框列(type="selection")也不需要添加v-if条件

五、完整代码示例

以下是优惠券管理页面的完整代码,重点标注了修改部分:

<template>
  <div class="app-container">
    <!-- 搜索表单区域 -->
    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="88px">
      <!-- ... 搜索表单内容 ... -->
    </el-form>

    <!-- 操作按钮栏 -->
    <el-row :gutter="10" class="mb8">
      <!-- ... 操作按钮 ... -->
      <!-- 修改点1:添加columns属性 -->
      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
    </el-row>

    <!-- 数据表格 -->
    <el-table v-loading="loading" :data="couponList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="序号" type="index" align="center" width="80" :index="indexMethod" />
      
      <!-- 修改点2:为每个列添加v-if条件 -->
      <el-table-column label="优惠券名称" align="center" prop="couponName" v-if="columns.couponName.visible" />
      <el-table-column label="面值" align="center" prop="couponValue" v-if="columns.couponValue.visible" />
      <el-table-column label="有效期开始" align="center" prop="startTime" width="180" v-if="columns.startTime.visible">
        <template #default="scope">
          <span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="有效期结束" align="center" prop="endTime" width="180" v-if="columns.endTime.visible">
        <template #default="scope">
          <span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="状态" align="center" prop="status" v-if="columns.status.visible">
        <template #default="scope">
          <dict-tag :options="common_status" :value="scope.row.status"/>
        </template>
      </el-table-column>
      <el-table-column label="使用说明" align="center" prop="remark" v-if="columns.remark.visible" />
      
      <!-- 操作列始终显示 -->
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template #default="scope">
          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['feature:coupon:edit']">修改</el-button>
          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['feature:coupon:remove']">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    
    <!-- 分页组件 -->
    <pagination
      v-show="total>0"
      :total="total"
      v-model:page="queryParams.pageNum"
      v-model:limit="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 对话框 -->
    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
      <!-- ... 对话框内容 ... -->
    </el-dialog>
  </div>
</template>

<script setup name="Coupon">
import { listCoupon, getCoupon, delCoupon, addCoupon, updateCoupon } from "@/api/feature/coupon"

const { proxy } = getCurrentInstance()
const { common_status } = proxy.useDict('common_status')

// 修改点3:定义columns响应式对象
const columns = ref({
  couponName: { label: '优惠券名称', visible: true },
  couponValue: { label: '面值', visible: true },
  startTime: { label: '有效期开始', visible: true },
  endTime: { label: '有效期结束', visible: true },
  status: { label: '状态', visible: true },
  remark: { label: '使用说明', visible: true }
})

const couponList = ref([])
const open = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const title = ref("")

const data = reactive({
  form: {},
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    couponName: null,
    couponValue: null,
    startTime: null,
    endTime: null,
    status: null,
  },
  rules: {
  }
})

const { queryParams, form, rules } = toRefs(data)

/** 序号计算方法 */
function indexMethod(index) {
  return (queryParams.value.pageNum - 1) * queryParams.value.pageSize + index + 1
}

/** 查询优惠券管理列表 */
function getList() {
  loading.value = true
  listCoupon(queryParams.value).then(response => {
    couponList.value = response.rows
    total.value = response.total
    loading.value = false
  })
}

// ... 其他方法 ...
</script>

六、功能说明

实现完成后,优惠券管理页面将具备以下功能:

  1. 列配置按钮:在表格右上角工具栏中会显示一个列配置图标
  2. 列显示/隐藏:点击列配置按钮,会弹出列配置面板,用户可以勾选或取消勾选要显示的列
  3. 实时生效:勾选状态变化后,表格列会立即显示或隐藏,无需刷新页面
  4. 默认全部显示:所有列默认visible: true,用户可以根据需要隐藏不需要的列
  5. 固定列:序号列、复选框列和操作列始终显示,不受列配置控制

七、扩展应用

这个功能可以轻松应用到若依框架的其他列表页面中,只需按照上述三个步骤进行修改:

  1. right-toolbar组件中添加:columns="columns"属性
  2. 定义columns响应式对象,配置需要控制的列
  3. 为对应的表格列添加v-if="columns.列名.visible"条件

八、注意事项

  1. 列名一致性columns对象中的属性名必须与表格列的prop属性保持一致
  2. 默认显示状态:根据业务需求设置visible的默认值,通常建议默认显示所有列
  3. 固定列:序号列、复选框列和操作列等关键列不建议添加显示/隐藏控制
  4. 响应式数据columns必须使用ref定义,确保响应式更新

九、总结

通过参考若依框架内置的用户管理页面,我们成功为优惠券管理页面添加了表格列按需显示功能。该实现方式简单高效,充分利用了若依框架已有的组件和功能,避免了重复开发。用户可以根据自己的需求自由选择要显示的列,提升了表格的可读性和用户体验。

这种实现方式具有良好的可复用性和扩展性,可以快速应用到其他类似的列表页面中,是若依框架二次开发中的一个实用技巧。

解决 Vite 开发服务器启动报错:spawn EPERM

作者 独守一隅
2026年1月26日 09:05

问题现象

在 Windows 系统下运行 Vite 开发服务器时,控制台报错:

Error: spawn EPERM 
     at ChildProcess.spawn (node:internal/child_process:420:11) 
     at Object.spawn (node:child_process:787:9) 
     at baseOpen (file:///E:/标签溯源/code/TraceGuard-UI/node_modules/vite/dist/node/chunks/dep-DBxKXgDP.js:26487:36)

开发服务器无法正常启动,或者启动后立即崩溃。

发生原因

这个错误通常由以下几个因素导致:

1. Vite 自动打开浏览器功能

vite.config.js 配置文件中,server.open 选项被设置为 true

server: {
  port: 80,
  host: true,
  open: true,  // 这里是问题所在
  // ...
}

open: true 时,Vite 会在服务器启动后自动调用系统默认浏览器打开开发地址。

2. Windows 权限限制

在 Windows 系统上,Node.js 的 child_process.spawn() 方法尝试启动浏览器进程时,可能会遇到以下权限问题:

  • 权限不足:当前进程没有足够的权限启动外部应用程序
  • 安全策略限制:Windows Defender 或企业安全策略阻止了进程创建
  • 路径问题:浏览器可执行文件路径无法正确解析

3. 端口占用(次要原因)

如果配置的端口(如 80)已被其他进程占用,也可能导致类似的错误。

解决方案

方案一:禁用自动打开浏览器(推荐)

修改 vite.config.js 文件,将 open 选项设置为 false

// vite.config.js
export default defineConfig(({ mode, command }) => {
  return {
    server: {
      port: 80,
      host: true,
      open: false,  // 改为 false,禁用自动打开浏览器
      proxy: {
        // ... 其他配置
      }
    },
    // ... 其他配置
  }
})

优点

  • 彻底解决权限问题
  • 不影响开发服务器功能
  • 可以手动选择浏览器访问

使用方式: 启动开发服务器后,在浏览器中手动访问 http://localhost:80

方案二:指定浏览器路径

如果确实需要自动打开浏览器,可以指定浏览器可执行文件的完整路径:

server: {
  port: 80,
  host: true,
  open: 'chrome',  // 或者指定完整路径
  // 或者
  open: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'
}

注意:这种方式仍然可能遇到权限问题,不如方案一稳定。

方案三:以管理员身份运行

以管理员身份运行终端或 IDE,然后执行 npm run dev 命令。

缺点

  • 不是长期解决方案
  • 可能带来安全风险
  • 不推荐作为常规做法

验证修复

修改配置后,重新启动开发服务器:

npm run dev

应该看到类似以下的成功输出:

  VITE v6.3.5  ready in xxx ms

  ➜  Local:   http://localhost:80/
  ➜  Network: http://192.168.x.x:80/
  ➜  press h + enter to show help

然后在浏览器中手动访问 http://localhost:80 即可。

总结

spawn EPERM 错误是 Windows 系统下 Vite 开发服务器的常见问题,主要原因是自动打开浏览器功能触发了权限限制。最简单有效的解决方案是禁用自动打开浏览器功能,改为手动访问开发地址。

这种方式不仅解决了权限问题,还提供了更好的开发体验——你可以自由选择浏览器和打开时机。

相关资源

❌
❌