Razor 视图 + JavaScript + Vue/iView 组件实现的表格自定义列

**摘要:**本文介绍在 ASP.NET Core Razor 视图项目中实现表格自定义列的实用方案:由 Razor 视图承载页面骨架与初始数据,通过 JavaScript 初始化 Vue 实例,再结合 iView Table 组件动态生成列配置。服务端负责权限校验、数据查询和列定义,前端负责弹窗交互、勾选状态与即时渲染。文章从前置知识、整体数据流、Razor 视图、JavaScript 初始化、服务端数据模型和列设置本地持久化等方面展开,并总结动态列排序、列宽控制、大数据量分页、权限过滤和特殊模板列等常见注意事项。

一、背景与需求

在企业后台系统或内部管理系统中,数据表格几乎是最常见的页面元素。无论是订单列表、用户管理还是报表查询,我们都需要把服务端返回的数据以表格形式展示给用户。但在实际业务里,同一个表格往往会被不同角色使用:有的角色只需要查看基础字段,有的角色需要同时查看金额、状态、操作等多个字段。如果每次都写死一套列配置,就很难同时满足大家的需求。

在 ASP.NET Core Razor 视图项目中,页面通常由服务端渲染,HTML 结构和初始数据会在服务端生成。这种模式的好处是直观、稳定,但当表格列很多,或者用户希望自己决定显示哪些列时,纯粹的固定列配置就会显得不够灵活:字段一多,表格会变得拥挤,阅读效率也会下降。

本文要解决的就是这个问题。我们采用一种前后端协同的轻量方案:由 Razor 视图承载页面骨架和初始数据,通过 JavaScript 初始化 Vue 实例,再结合 iView 的 Table 组件实现表格自定义列。用户可以打开「自定义列」弹窗,勾选自己关注的字段,表格会立刻按新的列配置重新渲染,而不需要重新请求页面。

这样做有几个明显的好处:

  • 职责清晰:服务端继续负责权限校验、数据查询和列定义的生成,前端负责交互、列配置和即时渲染。
  • 体验更好:用户勾选列之后,表格无需刷新页面就能直接更新,操作更加流畅。
  • 易于扩展:后续可以继续加入列排序、列宽记忆、用户级配置保存等能力。

阅读本文需要哪些前置知识

要跟着本文实现,你需要对 ASP.NET Core Razor 视图、JavaScript 和 Vue 的基础语法有一定了解。Razor 视图负责在服务端输出 HTML 和 JSON 数据;JavaScript 和 Vue 负责在浏览器中完成交互;iView 是一个基于 Vue 的组件库,其中的 Table 组件用 columns 配置描述表格列。即使你之前没有深度用过 iView,只要理解"数据驱动视图"的思路,也可以顺利读懂后面的代码。

二、整体思路

整个功能可以理解为:服务端先把"有哪些列、每列叫什么、默认是否显示"定义好,随页面一起交给浏览器;浏览器端用 Vue 把这些列保存为响应式数据,并提供勾选界面;用户勾选完成后,Vue 根据勾选结果计算出 Table 组件真正需要的列配置,Table 组件再自动更新表格。

从实现上看,整个方案可以分为三部分:

  • Razor 视图:输出页面容器、初始化数据、列定义和权限信息,并为 Vue 提供一个挂载点。
  • JavaScript:负责启动 Vue,管理列配置弹窗、勾选状态和表格列渲染。
  • Vue/iView :使用 iTable 组件展示数据,Table 的 columns 配置由 Vue 的响应式数据动态生成。

核心思路在于把「自定义列」抽象成一组可勾选的列对象。每个列对象至少包含字段键、标题、宽度和是否勾选等信息。用户勾选或取消勾选列后,前端根据最新勾选结果重新生成 iView Table 需要的 columns 配置;如果后续需要保存用户的列宽、顺序或显隐状态,也都可以挂在这些列对象上,集中管理。

一次交互的数据流

可以把一次"修改列配置并刷新表格"的过程简化为下面几步:

  1. 服务端在渲染 Razor 视图时,把列定义和行数据序列化成 JSON,写入页面的 window.__tableConfig。
  2. 页面加载后,JavaScript 读取 window.__tableConfig,初始化 Vue 数据。
  3. 用户点击「自定义列」按钮,打开列选择弹窗。
  4. 用户勾选或取消勾选某一列,allColumns 中对应项的 checked 状态发生变化。
  5. Vue 的计算属性 tableColumns 自动过滤出勾选的列,并生成 Table 组件需要的列配置。
  6. iView Table 使用新的 columns 自动重新渲染表格。

这条链路中,前端不需要手动操作 DOM,只需要维护好数据状态,界面就会跟着数据变化,这也是 Vue 响应式机制带来的主要收益。

三、Razor 视图部分

Razor 视图的部分要做三件事:第一,提供一个页面容器,也就是一个带 id 的 div,让 Vue 可以挂载;第二,把服务端准备好的列定义和数据序列化成 JSON,方便浏览器端读取;第三,在容器内放置自定义列按钮、表格和列设置弹窗等页面元素。

写入 JSON 时需要注意安全。因为我们要把服务端数据原样放进页面,如果字段值里夹杂了恶意脚本,直接输出可能会引发脚本注入问题。实际项目中应当先对数据进行 HTML 编码处理,再使用 Html.Raw(Json.Serialize(...)) 输出,避免注入风险。

html 复制代码
<div id="customTableApp" v-cloak>
    <div class="toolbar">
        <i-button type="primary" @@click="openColumnSetting">自定义列</i-button>
    </div>

    <i-table :columns="tableColumns" :data="tableData" border stripe></i-table>

    <i-modal v-model="columnSettingVisible" title="自定义列" @@on-ok="applyColumnSetting">
        <div v-for="column in allColumns" :key="column.key">
            <i-checkbox v-model="column.checked">{{ column.title }}</i-checkbox>
        </div>
    </i-modal>
</div>

<script>
    window.__tableConfig = {
        columns: @Html.Raw(Json.Serialize(Model.Columns)),
        data: @Html.Raw(Json.Serialize(Model.Rows))
    };
</script>

下面简单解释这段模板中几个关键点:

  • v-cloak:用于在 Vue 实例初始化完成前隐藏未编译的模板,避免用户看到花括号等原始表达式。
  • @@click :Razor 视图中 @@ 会被转义为单个 @,所以最终浏览器里读到的是 Vue 需要的 @click。
  • :columns 和 :data :分别把 Vue 实例中的 tableColumns 和 tableData 以绑定的形式传给 iView Table。
  • v-model :把弹窗的显示状态和 Vue 数据 columnSettingVisible 双向绑定,打开和关闭弹窗都通过修改这个值来实现。
  • v-for :遍历 allColumns 生成一组复选列,每个复选框通过 v-model="column.checked" 直接绑定到对应列对象的勾选状态。

这里给出的只是核心示例。真实项目中,你需要根据项目情况在布局文件中引入 Vue、iView 以及具体的业务脚本;如果页面已经有全局的脚本管理方式,也可以将示例代码拆分到独立的 JavaScript 文件中维护。

四、JavaScript 初始化部分

页面加载完成后,前端从 window.__tableConfig 中读取服务端注入的列定义和行数据,然后创建 Vue 实例。初始化时,我们不会直接把服务端列定义丢给 Table 组件,而是先转换成前端需要维护的列对象:每个对象包含 key、title、width 和 checked。其中 checked 由 defaultVisible 决定,服务端标记为默认显示的列会被勾选。

javascript 复制代码
new Vue({
    el: '#customTableApp',
    data: function () {
        return {
            columnSettingVisible: false,
            allColumns: window.__tableConfig.columns.map(function (item) {
                return {
                    key: item.key,
                    title: item.title,
                    width: item.width,
                    checked: item.defaultVisible !== false
                };
            }),
            tableData: window.__tableConfig.data
        };
    },
    computed: {
        tableColumns: function () {
            return this.allColumns
                .filter(function (column) {
                    return column.checked;
                })
                .map(function (column) {
                    return {
                        key: column.key,
                        title: column.title,
                        width: column.width,
                        align: 'left'
                    };
                });
        }
    },
    methods: {
        openColumnSetting: function () {
            this.columnSettingVisible = true;
        },
        applyColumnSetting: function () {
            this.columnSettingVisible = false;
        }
    }
});

这段代码分为三个部分:

  • data :定义弹窗显示状态、可配置列列表和表格数据。所有列先保留一个 checked 状态,默认勾选的列会进入表格显示配置。
  • computed :tableColumns 是一个计算属性。它先过滤出 checked 为真的列,再把每列转换成 iView Table 需要的配置格式,并设置对齐方式。
  • methods:提供打开列设置弹窗和确认设置两个方法。

这里最关键的是 tableColumns。因为它是计算属性,所以只要 allColumns 中任意一列的 checked 发生变化,Vue 就会自动重新计算 tableColumns,iView Table 也就会拿到新的列配置并更新表格。整个过程完全由数据驱动,不需要再写额外的 DOM 更新代码。如果后续要加入列排序,只需要在生成 tableColumns 前对列数组进行排序即可。

五、服务端数据模型

为了让 Razor 视图能够稳定地输出列配置,服务端最好用一个统一的模型来描述每一列,而不是在 Controller 里随意拼接匿名对象。这样既能保证字段命名一致,也方便根据用户权限、页面配置在服务端提前过滤列。

下面是一个最小可用的 TableColumnConfig 模型:

csharp 复制代码
public class TableColumnConfig
{
    public string Key { get; set; }
    public string Title { get; set; }
    public int? Width { get; set; }
    public bool DefaultVisible { get; set; } = true;
}
  • Key:列的字段键,对应数据行中的字段名,Table 组件会按这个键取值。
  • Title:列在表头显示的标题。
  • Width :列宽,可选。设置为 null 时可以让 Table 组件自动分配宽度。
  • DefaultVisible :默认是否显示。默认值为 true,表示所有列默认勾选;对于不常用或敏感字段,可以在服务端设置为 false。

接口返回的表格数据通常采用字典列表的形式,这样 iView Table 可以根据列的 key 从字典中直接取值。当然,项目里也可以继续使用强类型对象列表,再由 Razor 视图或服务端完成到字典形式的转换。选择哪种方式主要看项目现有风格:如果只是给 Table 组件消费,字典形式更直接;如果对象还会被其他逻辑复用,保留强类型对象通常更好。

六、优化体验:保存用户列设置

如果列配置只保存在页面的 Vue 实例里,用户每次刷新页面都会回到服务端定义的默认状态。为了让用户下次打开页面时仍然看到自己的配置,可以把勾选结果保存到浏览器的 localStorage 中。

基本思路是:启动时先读取本地存储,把之前保存的列键列表恢复成勾选状态;用户确认修改后,再把当前勾选的列键写回本地存储。这样即使是跨会话访问,配置也能保留下来。

javascript 复制代码
var columnStorageKey = 'customTableColumns';

function loadColumnSettings(columns) {
    var saved = localStorage.getItem(columnStorageKey);
    if (!saved) {
        return;
    }

    try {
        var savedKeys = JSON.parse(saved);
        columns.forEach(function (column) {
            column.checked = savedKeys.indexOf(column.key) !== -1;
        });
    } catch (e) {
        // 本地存储数据异常时使用默认配置
    }
}

function saveColumnSettings(columns) {
    var checkedKeys = columns
        .filter(function (column) {
            return column.checked;
        })
        .map(function (column) {
            return column.key;
        });

    localStorage.setItem(columnStorageKey, JSON.stringify(checkedKeys));
}

这里有两个核心函数:

  • loadColumnSettings :读取本地存储字符串,解析出之前保存的列键列表,然后遍历所有列,把命中的列设为勾选。读取和解析都放在 try/catch 中,一旦本地数据损坏,就继续使用默认配置,避免页面启动失败。
  • saveColumnSettings :从列数组里过滤出已经勾选的列,提取它们的 key,以 JSON 字符串的形式写回本地存储。

在实际接入时,通常在 Vue 实例创建完成后调用一次 loadColumnSettings 恢复配置;在用户点击弹窗的"确定"按钮后调用 saveColumnSettings 保存配置。如果后续字段版本发生变化,比如某列被删除,本地保存的旧列键会自然失效,因为 indexOf 匹配不到对应列,代码仍然可以安全地回退到默认状态。

七、常见问题与注意事项

  • 动态列与顺序 :如果需要支持拖拽排序,可以在列设置弹窗中加入上移、下移按钮,并调整 allColumns 的顺序。
  • 列宽控制 :固定列宽有助于避免表格抖动;如果允许用户调整列宽,可以记录宽度变化并合并到 tableColumns 中。
  • 大数据量:当行数很多时,建议服务端分页,iView Table 只负责渲染当前页数据。
  • 权限过滤:某些字段如果用户没有权限查看,应在服务端生成列配置时直接排除,而不是只在前端隐藏。
  • 特殊模板列 :对于操作列或需要自定义渲染的列,可以通过 render 函数返回 Vue 模板或 h 函数生成的节点。

除上述问题外,还有几个细节值得留意:

  • 空数据展示:当筛选结果为空或接口暂时没有数据时,可以给 Table 配置空状态提示,避免页面看起来像出错。
  • 固定操作列:如果"操作"列需要在横向滚动时保持可见,可以结合 Table 的固定列配置来设置。
  • 列配置与业务语义一致:列标题、排序字段和导出字段等最好都由服务端统一定义,避免前端另起一套配置造成不一致。

八、小结

本文围绕 ASP.NET Core Razor 视图项目中「表格自定义列」这一常见需求,完整梳理了一套前端与后端协同工作的实现方案。相比固定列配置,该方案让用户可以按角色、按场景自由选择需要展示的列,同时把权限校验和数据查询交给服务端,把交互逻辑和列配置交给前端,实现了职责清晰、响应及时的效果。即使项目并没有引入完整的前端工程化体系,也依然可以用这套思路渐进式地增强表格能力。

从整体实现来看,方案的核心可以归纳为三层配合:

  • Razor 视图层:负责承载页面结构,并通过安全的 JSON 序列化方式,把列定义与行数据注入页面;
  • JavaScript 层 :负责启动 Vue 实例,维护列勾选状态,并通过计算属性动态生成 iView Table 所需的 columns 配置;
  • iView 组件层:利用响应式数据驱动表格渲染,使用户对列配置的修改可以即时反映到界面上。

在服务端,列配置可以通过 TableColumnConfig 等模型统一描述字段键、显示标题、列宽和默认显隐状态,既保证了列定义的规范性,也便于根据用户权限进行前置过滤。接口返回的数据通常采用字典或可转换的强类型列表,方便前端按 key 取值并交给 Table 组件渲染。

在体验优化方面,文章还给出了基于 localStorage 的列设置持久化方案。前端启动时读取保存的列配置,用户在确认列设置后写入本地存储,这样即使刷新页面或下次访问,也能恢复用户过去的选择。针对本地存储数据异常、版本变更等情况,代码中还可以通过 try/catch 进行兜底,避免因存储数据错误导致页面初始化失败。

对于实际落地场景,还需要重点关注几个方向:动态列顺序可以通过上移、下移或拖拽排序来实现;列宽控制与固定宽度结合,可以在灵活性和界面稳定性之间取得平衡;大数据量场景应优先考虑服务端分页,减小客户端渲染压力;权限控制应在服务端生成列配置时就完成过滤,而不能只依赖前端隐藏;操作列、自定义渲染列则需要借助 render 函数生成相应的模板或节点。此外,空数据提示、固定操作列以及列配置与业务语义的一致性,也会直接影响用户长期使用的体验。

总体而言,这套方案没有过度引入新的前端工程化依赖,而是充分发挥了现有 Razor、Vue 和 iView 的能力,适合在传统 Razor 视图项目或需要渐进式增强表格能力的系统中快速落地。通过前端响应式列管理、服务端统一配置、本地持久化以及合理的性能与权限设计,开发者可以显著提升复杂表格的可配置性和用户体验,并为后续进一步扩展列排序、列缓存、用户级个性化配置等能力留下清晰空间。

相关推荐
系统集成-鹰翔15 小时前
ERP图片改名后仍命中识别缓存:缓存键、十分钟到期与调用计数
javascript·经验分享·后端
FlyWIHTSKY16 小时前
vue2和vue3有什么区别,用法上有哪些区别
前端·javascript·vue.js
程序员大阳18 小时前
副队长JavaScript教程(66)-本地存储实战:待办事项网页的开发
javascript·json·实战·待办事项·本地存储·案例
Dream-Y.ocean18 小时前
鸿蒙PC平台逻控 PLC 编辑器适配实战:基于 Electron 壳方案的纯 JavaScript 梯形图编程与仿真引擎开发
javascript·编辑器·harmonyos
前端小D20 小时前
JS核心底层
前端·javascript
Dovis(誓平步青云)21 小时前
启元 T1 Pro 人机相声搭档:语音-触摸双通道的启元机器人节奏引擎设计
开发语言·前端·javascript·microsoft·交互
codeejun1 天前
每日一Go·MySQL-11、覆盖索引 & 回表原理
javascript·数据库·mysql
用户298698530141 天前
React 前端将 Excel 工作表导出为图片的技术实践
javascript·react.js·excel
麦麦麦当劳大王1 天前
【油猴脚本】Boss直聘 岗位职责复制
开发语言·前端·javascript