Vol框架AI Skill业务开发


当前ai skill可以自动完成大部分前后端业务逻辑处理及功能实现。 【点击下载ai skill】

1. 使用方式:以cursor为例,同时打开下载的vol-skill.md文件与前后端项目,添加描述:2. 按vol-skill.md清单,在【表.vue】文件中编辑弹出框【字段】后加一个选择按钮,从弹出框选择数据,弹出框使用voltable表格配置,表格的数据从【xxx】表获取,选择数据后回写到表单

AI 执行契约(最高优先级,冲突时以本节为准)

收到需求
  → §12.Z 关键词检索
  → 判定端:仅后台 / 仅前端 / 前后端
  → 判定模式:存在 Edit.vue?→ 新页面编辑(§12.K) : 标准 view-grid({表}.vue)
  → 写文件:Partial/{表}Service.cs | {表}.vue | Edit.vue | 新建 SelectXxx.vue
  → 禁止:.jsx 业务 | options.js 手改 | 非 Partial Service/Controller | ViewGrid 源码
需求信号AI 动作Skill在线文档(仅 .html)
查不到数据、保存写库、审核后写关联表Partial Service,Func 赋值 + base.xxx()§12.A.0–A.../cs/service/guid.html
searchBefore/addBefore、按钮、弹框流程{表}.vue Props Hook§12.C/D.../view-grid/methods/{名}.html
字段 onChange/回车/明细列计算{表}.vue onInit/onInited + getFormOption/columns§12.E.../view-grid/components.html · event/
弹框选数、复杂 UI 效果{表}.vue + 子组件,参照示例§12.J.../web/...
独立页新建编辑Edit.vue + vol-edit Hook§12.K.../edit/
自定义 APIPartial 三件套§12.J.10.../cs/dev/api.html · JsonNormal → case.html
数据库访问 / 分页查询 / vol-table 自定义接口Partial/{表}Service.cs EF + repository§12.B.0.../cs/dev/db.html

数据库默认规则: 一律 EF LINQ + repository(见 §12.B.0);禁止默认使用 EFsqlopen in new window / Dapper 原生 SQL,除非 EF 无法实现。所有分页 MUST TakePage(Page, Rows);vol-table / selectTable 自定义查询 MUST options.ConvertQueryFilter<{实体}>()

文档源: 优先本 Skill §十二代码 → 不足时 WebFetch http://v3.volcore.xyz/docs/{路径}.html。Skill 内 doc.web/docs/ 路径仅作 URL 映射,不要假设用户仓库有 doc.web。

JSON 返回: 全局 Json() 为小驼峰;自定义 Partial 接口返回业务数据 MUST return JsonNormal(...)(与实体/columns.field 原大小写一致)。标准 getPageData 已在 ApiBaseController 内 JsonNormal。见 §12.B / case.mdopen in new window


层级技术
后端.NET 8、ASP.NET Core WebAPI、Entity Framework Core、Autofac
前端Vue 3 Composition API、script setup lang="jsx"、Element Plus、TypeScript
核心组件view-grid(列表+表单+明细)、vol-formvol-tablevol-box
HTTPproxy.http.get/post(前端)、ApiBaseController 标准路由(后端)

零、AI 工作流

** 在线文档:http://v3.volcore.xyz

开发者描述业务需求(§十一)

① 本 Skill §12.Z 检索 → 定位 §12.A–J 代码模板(优先)

② 模板不够/边界不清 → 访问 v3.volcore.xyz 对应页(§十三 URL 映射表)

③ 读取用户项目 api + web.vite 中实际 {表}.vue / Partial Service

④ 只改业务文件:Partial/{表}Service.cs、{表}.vue 或 Edit.vue、新建 SelectXxx.vue 等(**禁止改 .jsx**)

⑤ 勿改 options.js、非 Partial 的生成 Controller/Service
步骤数据源说明
优先本 Skill §十二含可执行代码模板 + 全量索引,覆盖常见业务
补充v3.volcore.xyz与离线 doc.web 同源;AI 用 WebFetch/浏览查具体章节、截图说明、边界 case
必须用户项目源码web.vite/src/views/...api/.../Partial/
不依赖本地 doc.web、Demo_Order 示例用 Skill + v3.volcore.xyz + 用户项目源码即可

在线文档规则(AI):

  • URL:http://v3.volcore.xyz/docs/{相对路径}.html
  • 顺序:§12 模板 → 在线文档补细节 → 读用户 {表}.vue / Partial Service 对齐命名

文档体系与代码归属(AI 路由必读)

官方在线文档:http://v3.volcore.xyz(与离线 doc.web/docs/ 同路径)。
四类文档职责不同,AI 必须先判断需求属于哪一类,再查对应章节。

文档四分法

文档分区在线入口讲什么不是什么
后台开发 docs/cs//docs/cs/open in new window数据库操作;生成表 Service.cs 业务处理(查询/新建/修改/删除/审批/导入导出等 操作前、操作后 钩子)不是前端 UI
生成页面文档 docs/view-grid//docs/view-grid/open in new windowview-grid 容器的属性、Hook、Slots;内嵌 vol-form / vol-table 的字段事件见 components.mdevent/不是 vol-form/vol-table 完整 API,也不是 web 示例集
前端开发 docs/web//docs/web/open in new window生成页之上的 常用功能示例(弹框选数、自定义按钮、联动、table 样式…Recipes)不是 view-grid 完整 API 列表
新页面编辑 docs/edit//docs/edit/open in new window生成器选 「新页面编辑」 时:新建/编辑在 独立页面 Edit.vue 打开,而非列表弹框不是标准 {表}.vue 弹框模式

组件示例(独立使用):docs/form/docs/table/docs/box/ — vol-form / vol-table 完整 API;生成页内嵌用法以 view-grid/components.md + event/ 为准。

view-grid 与 vol-form、vol-table(生成页必知)

view-grid 不重复实现表单/表格,而是内嵌子组件并透传配置:

内嵌ref配置来源字段/列事件绑定
查询 vol-formsearchFormsearchFormFields/OptionsgridRef.getSearchFormOption('字段').onChange
主表 vol-tabletablecolumnscolumns[].click/formatter + Hook :rowClick
编辑 vol-formformeditFormFields/OptionsgridRef.getFormOption('字段').onKeyPress/onChange/blur
明细 vol-tabledetaildetailOptions.columnsdetailOptions.columns[].onChange/onKeyPress/formatter

三层扩展(由外到内): ① ViewGrid Hook(:searchBefore:addBefore…)→ ② onInit 改 gridRef 属性 → ③ 字段/列事件(§12.E)。
文档: http://v3.volcore.xyz/docs/view-grid/components.html · event 目录 · form/table 仅查组件独有 API。

后台:Service 数据库前/后处理

写代码位置: api/.../Services/.../Partial/{表}Service.cs(及 Partial Controller 自定义 API)。

实现方式(框架约定): 业务扩展点定义在 ApplicationServiceBase.csFunc/Action 属性);ServiceBase.csGetPageData/Add/Update/Del 等 virtual 方法内 Invoke。Partial 中 重写对应方法 → 给委托属性赋 Lambda → return base.xxx(...),不要复制框架 CRUD。详见 §12.A.0guid.mdopen in new window

阶段典型 Func 属性含义
查询前QueryRelativeListQueryRelativeExpressionQuerySql改查询条件 / LINQ / 原生 SQL
查询后GetPageDataOnExecutedSummaryExpress改结果行 / 表格合计
保存前(原始数据)AddOnExecuteUpdateOnExecute操作 SaveModel,实体转换前
保存前(实体)AddOnExecutingUpdateOnExecutingDelOnExecutingImportOnExecuting写库 之前校验、赋默认值
保存后(同事务)AddOnExecutedUpdateOnExecutedDelOnExecutedImportOnExecuted写关联表;Error 回滚
文档docs/cs/service/guid 总览 + search/add/update… 完整示例

前端模式 A:标准生成页(列表 + 弹框编辑)

写代码位置: web.vite/src/views/{类库}/{文件夹}/{表}/{表}.vue(及同目录子组件 SelectXxx.vue 等)。

  • 使用 <view-grid> 组件
  • 业务通过 Props 传入 Hook:onInit:searchBefore:addBefore…)
  • 查文档:view-grid(属性/方法)+ web(实现某效果的示例)

前端模式 B:新页面编辑(独立页新建/编辑)

生效条件: 代码生成器 编辑模式 = 新页面编辑

写代码位置: views/{类库}/{文件夹}/{表}/Edit.vue(或 {表}/edit.vue,以生成结果为准)。

  • 新建/编辑在 新路由页面 打开,不是 view-grid 弹框
  • Hook 名与 view-grid 类似但独立一套loadFormBeforeloadFormAfterloadTableBefore…)
  • 查文档:docs/edit/;Skill §12.K

禁止:{表}.jsx 不写业务代码

文件说明
extension/.../{表}.jsx 或生成遗留 .jsx仅兼容旧版项目,新版本业务 一律不写此处
AI MUST NOT.jsx 中新增或修改业务逻辑
正确做法标准页 → {表}.vue;新页面编辑 → Edit.vue;后端 → Partial/{表}Service.cs

若项目 {表}.vueimport extend from '...jsx',保留空 jsx 或仅作兼容引用,新 Hook 写在 .vue

AI 需求路由表

开发者描述改哪里查哪类文档Skill 章节
只能查自己的单、审核后写库存Partial Servicecs/service§12.A
保存前校验、明细默认值Partial Service + 或前端 Hookcs/service + view-grid§12.A + §12.C
表单/明细字段 onChange、回车、实时计算{表}.vue onInitview-grid event/ + components§12.E
view-grid 属性/方法/Hook{表}.vueview-grid methods/properties§12.C/D
弹框选客户、select 联动{表}.vue + 子组件web 示例为主§12.J
新页面编辑页保存前校验Edit.vueedit§12.K
自定义 APIPartial 三件套cs/dev/api§12.J.10 + JsonNormal
vol-table / selectTable 自定义分页接口Partial Service + Controllercs/dev/db · selectTable§12.B.0 + §12.J.3/J.10
数据库增删改查(非生成页 Hook)Partial/{表}Service.cs repositorycs/dev/db§12.B.0

一、项目结构

1.0 业务代码写哪里(速查)

场景业务写这里禁止写这里
后台 DB 前/后处理Partial/{表}Service.cs非 Partial 的 Service.cs
标准生成页(列表+弹框){表}.vue、同目录 *.vue 子组件options.js(会被覆盖)、.jsx
新页面编辑{表}/Edit.vue.jsx
前端静态列/按钮配置{表}.vueEdit.vueonInit 改 options直接改 options.js 文件

1.1 前端 (web.vite)

web.vite/src/views/{类库}/{文件夹}/{表名}/
│   ├── {表名}.vue              ← 【标准模式】view-grid 业务 Hook(★写这里)
│   ├── {表名}/options.js       ← 生成器配置(会被覆盖;动态改写在 onInit)
│   ├── {表名}/Edit.vue         ← 【新页面编辑模式】独立页新建/编辑(★写这里)
│   └── {表名}/*.vue            ← 子组件 SelectXxx、gridFooter 等
extension/{模块}/{表名}/
│   └── {表名}.jsx              ← ⚠ 旧版兼容,【禁止写业务】
components/basic/ViewGrid/      ← 框架核心,勿改

完整代码模板:§十二;URL 映射见 §十三

1.2 后端 (api)

api/
├── VolPro.WebApi/
│   ├── Controllers/{Module}/{表}Controller.cs           ← 自动生成(会被覆盖)
│   ├── Controllers/{Module}/Partial/{表}Controller.cs   ← 自定义 API(不会覆盖)
│   └── Template/                                        ← 代码生成模板
├── VolPro.{Module}/
│   ├── Services/{folder}/{表}Service.cs                 ← 自动生成(会被覆盖)
│   ├── Services/{folder}/Partial/{表}Service.cs         ← 业务逻辑(不会覆盖)★
│   ├── IServices/{folder}/Partial/I{表}Service.cs       ← 接口声明(不会覆盖)
│   └── Repositories/{folder}/{表}Repository.cs          ← 一般不改
├── VolPro.Core/
│   ├── BaseProvider/ServiceBase.cs                      ← virtual 业务方法
│   ├── BaseProvider/ApplicationServiceBase.cs           ← 钩子委托
│   ├── Filters/ServiceFunFilter.cs                      ← 钩子签名参考
│   └── Controllers/Basic/ApiBaseController.cs           ← 标准 REST 端点
└── VolPro.Entity/DomainModels/                          ← 实体

二、核心原则(AI 必须遵守)

2.1 文件与 Partial 规则

文件代码生成后AI 是否写业务
{表}.vueEdit.vue、子组件 .vue不覆盖
Partial/ 下 Service/Controller/IService不覆盖
{表}.jsx不覆盖否(禁止写业务,仅旧项目兼容)
options.js、非 Partial Service/Controller会覆盖否(在 onInit 动态改)

2.1.1 旧版 .jsx 说明

  • 早期项目用 extension/.../{表}.jsxmethods 写 Hook;新版本已全部迁移到 {表}.vue
  • AI 遇到 jsx import 时:不要往 jsx 追加逻辑;在对应 .vueEdit.vue 实现同等 Hook。

2.2 扩展三件套(自定义 API 必做)

  1. IServices/.../Partial/I{表}Service.cs — 声明方法
  2. Services/.../Partial/{表}Service.cs — 实现方法
  3. Controllers/.../Partial/{表}Controller.cs — 暴露端点

路由规则:主 Controller 有 [Route("api/{表名}")],Partial 中用 [HttpPost("ActionName")],最终 URL = api/{表名}/{ActionName}

2.3 前后端职责划分

需求后端 Partial Service(DB 前/后)前端
查询过滤、合计、SQLGetPageData + Query* / OnExecuted{表}.vue searchBefore
保存校验、写关联表Add/Update OnExecuting / OnExecutedaddBefore / updateBefore
审核后写库存Audit OnExecutedauditBefore
UI 按钮、弹框、布局{表}.vue onInit / slots;示例查 web
view-grid 属性/方法view-grid 文档
独立页新建编辑同左 Service 钩子Edit.vue(新页面编辑模式)

三、代码生成器默认能力

生成一张表后,框架已内置以下能力(无需重写即可用,业务在 Partial/Hook 中扩展):

3.1 后端默认 API(ApiBaseController)

HTTP路由Service 方法用途
POSTapi/{表}/getPageDataGetPageData主表分页查询
POSTapi/{表}/getPageDataAsyncGetPageDataAsync异步查询
POSTapi/{表}/GetDetailPageGetDetailPage明细表分页
POSTapi/{表}/AddAdd新建(含主子表)
POSTapi/{表}/UpdateUpdate编辑(含明细增删改)
POSTapi/{表}/DelDel删除(可级联明细)
POSTapi/{表}/ImportImportExcel 导入
GETapi/{表}/DownLoadTemplateDownLoadTemplate下载导入模板
POSTapi/{表}/ExportExportExcel 导出
POSTapi/{表}/UploadUpload文件上传
POSTapi/{表}/AuditAudit审核/审批
POSTapi/{表}/antiAuditAntiAudit反审
POSTapi/{表}/cancelAuditCancelAudit撤销审批
POSTapi/{表}/urgentAuditUrgentAudit催办

勾选「异步接口」后,对应 *Async 路由生效。

3.2 前端 ViewGrid 默认能力

  • 查询表单 + 快捷查询 + 高级查询
  • 主表分页、排序、多选、导出、导入、打印、审批按钮
  • 编辑弹框(新建/编辑/复制/连续添加)
  • 主从表(一个明细):detail 配置
  • 一对多明细details[] 多 tabs
  • 三级明细subDetails
  • 行内编辑、拖拽排序、单元格合并、tableV2 高性能模式
  • 字典自动绑定(loadKey + 后台字典接口)
  • 按钮权限(filterPermission

四、后端 Service 业务扩展(完整技能)

位置: Services/{folder}/Partial/{表}Service.cs
基类: ServiceBase<TEntity, I{T}Repository>
钩子定义参考: VolPro.Core/Filters/ServiceFunFilter.cs

4.1 两种写法

A. 构造函数注册(固定逻辑):

public Demo_OrderService(...) : base(dbRepository) {
    QueryRelativeExpression = q => q.Where(x => x.Enable == 1);
    AddOnExecuting = (order, list) => { /* 校验 */ return webResponse.OK(); };
}

B. override 内临时赋值(按请求动态):

public override WebResponseContent Add(SaveModel saveDataModel) {
    AddOnExecuting = (order, list) => { /* ... */ return webResponse.OK(); };
    return base.Add(saveDataModel);
}

4.2 查询 GetPageData

成员类型时机与用途
QueryRelativeListAction<List<SearchParameters>>查询前改/删条件项;可取某字段后设 item.Value=null 让框架忽略
QueryRelativeExpressionFunc<IQueryable<T>, IQueryable<T>>LINQ 过滤(权限、状态、Include 明细)
QuerySqlstring原生 SQL(select 必须返回实体全部列,防注入)
OrderByExpression多字段排序{ x.CreateDate, QueryOrderBy.Desc }
SummaryExpressFunc<IQueryable<T>, object>主表底部合计行
GetPageDataOnExecutedAction<PageGridData<T>>结果返回前改 rows
options.Valueobject接收前端 searchBefore 传入的额外参数
IsMultiTenancybool多租户过滤开关

override: GetPageData(PageDataOptions options) / GetPageDataAsync

明细:

  • GetDetailPage(Async) — 明细分页、弹窗选单
  • GetDetailSummary<Detail> — 明细合计
  • DetailQuery<Detail> — 明细 IQueryable 过滤

OR 查询: 前端 wheres 项设 Fields 数组,多字段 OR。

4.3 新建 Add

流水线: SaveModel → AddOnExecute → 转实体 → AddOnExecuting → [事务入库+明细] → AddOnExecuted → 审计+流程

钩子参数说明
AddOnExecuteSaveModel原始 JSON 校验前
AddOnExecuting(T main, object detail)入库前;主从:detail as List<TDetail>
AddOnExecuted同上入库后,同事务,可 return Error 回滚
AddWorkFlowExecutingT进审批流程前,return false 阻止
AddWorkFlowExecuted(T, List<int> userIds)流程写入后

一对多 SaveModel: Details[] 每项含 TableDataDelKeys

4.4 修改 Update

钩子参数
UpdateOnExecuteSaveModel
UpdateOnExecuting(T main, object addList, object updateList, List<object> delKeys)
UpdateOnExecuted同上

addList/updateList 强转为 List<TDetail>

4.5 删除 Del

钩子说明
DelOnExecuting删除前校验(审核状态等)
DelOnExecuted删除后清理,同事务

参数:object[] keysDel(keys, delList: true) 默认级联删明细。

4.6 审批 / 反审 / 流程

方法/钩子说明
Audit / AuditAsync审核入口
AuditOnExecuting / AuditOnExecuted简单审核(非多级流程)
AuditWorkFlowExecuting(T, AuditStatus, bool isLast) 流程节点审核前
AuditWorkFlowExecuted(T, AuditStatus, List<int> nextUserIds, bool isLast) 流程节点审核后
AntiAudit + AntiAuditOnExecuting/Executed反审及业务回滚
CancelAudit / UrgentAudit撤销 / 催办
SubmitWorkFlowAudit批量提交进流程

获取流程信息(Service 内): GetTableWorkflow()GetTableCurrentFlowStep()GetTablePreFlowStep()

4.7 导入 / 导出 / 上传

操作钩子/配置
导出ExportOnExecutingExportColumnsExportCustomValueLimit(最大行数)
导入模板DownLoadTemplateColumns
导入ImportOnExecutingImportOnExecutedImportOnReadCellValueImportStartRowIndexExcelHeaderMapImportIgnoreSelectValidationColumns
上传UploadFolderIsRoot

4.8 打印

不在 ServiceBase 内。通过 PrintContainer(Startup 注册)+ PrintFilter 子类重写 Query/QueryDetail。前端 Hook:printBeforeprintModelClose

4.9 主从 vs 一对多

模式EntitySaveModelAddOnExecuting 明细
主从(一对一明细)DetailTable = new[] { typeof(TDetail) }DetailData + DelKeysobject as List<TDetail>
一对多DetailTable = new[] { typeof(D1), typeof(D2) }Details[]Update 分 add/update/del

4.10 Partial Controller 模板

namespace VolPro.MES.Controllers
{
    public partial class Stock_in_headController
    {
        private readonly IStock_in_headService _service;

        [ActivatorUtilitiesConstructor]
        public Stock_in_headController(IStock_in_headService service, IHttpContextAccessor httpContextAccessor)
            : base(service)
        {
            _service = service;
        }

        [HttpPost("GetPoItem")]
        public async Task<object> GetPoItem([FromBody] Dictionary<string, object> data)
        {
            int paramsType = Convert.ToInt32(data["paramsType"]);
            string paramsValue = data["paramsValue"]?.ToString();
            return await _service.GetPoItem(paramsType, paramsValue);
        }

        [HttpGet("GetStockInInfo")]
        public List<Stock_in_head> GetStockInInfo() => _service.GetStockInInfo();
    }
}

4.11 Partial Service 模板

public partial class Stock_in_headService
{
    public async Task<object> GetPoItem(int paramsType, string paramsValue)
    {
        return await Task.FromResult(repository.Find(x => x.field == paramsValue));
    }
}

4.12 后台常用扩展场景(非生成页 Hook)

场景做法
跨表写数据ctor 注入 IOtherRepository,在 AddOnExecuted 中写
自定义分页 API§12.B.0.4:ConvertQueryFilter + TakePage + Partial 三件套
vol-table 自定义 url 查询§12.B.0.4 + §12.J.10 B
接口免登录Controller Action 加 [AllowAnonymous]
重写权限Partial Controller 或 interfaceauth 模式
EF 事务repository.DbContextBeginTransaction(() => {})
当前用户UserContext.Current.UserId
单据号框架编码规则服务
租户过滤QueryRelativeExpression + 租户字段

五、前端 ViewGrid 扩展(完整技能)

Hook 权威列表: web.vite/src/components/basic/ViewGrid/ViewGridFilter.js
页面写法:{表}.vue 定义函数,通过 :onInit="onInit" 等 props 传入 <view-grid>

5.1 标准页面模板

<template>
  <view-grid ref="grid"
    :columns="columns" :detail="detail" :details="details"
    :editFormFields="editFormFields" :editFormOptions="editFormOptions"
    :searchFormFields="searchFormFields" :searchFormOptions="searchFormOptions"
    :table="table" :extend="extend"
    :onInit="onInit" :onInited="onInited"
    :searchBefore="searchBefore" :searchAfter="searchAfter"
    :addBefore="addBefore" :updateBefore="updateBefore"
    :modelOpenBefore="modelOpenBefore" :modelOpenAfter="modelOpenAfter"
    :rowClick="rowClick">
    <template #gridFooter><!-- 自定义 --></template>
  </view-grid>
</template>
<script setup lang="jsx">
import viewOptions from './{表}/options.js'
import { ref, reactive, getCurrentInstance } from 'vue'
const grid = ref(null)
const { proxy } = getCurrentInstance()
const { table, editFormFields, editFormOptions, searchFormFields, searchFormOptions, columns, detail, details } = reactive(viewOptions())
let gridRef  // onInit 中赋值 gridRef = $vm
</script>

5.2 生命周期 Hook

Hook时机典型用途
onInit($vm)最早获 gridRef;改 buttons/columns/url;配 detail;绑字典 data
onInited()数据对象初始化后height、detailOptions.columns、beginEdit
dicInited(dic)字典加载完级联刷新
showAdvancedSearchBefore()高级查询前return false 阻止
const onInit = async ($vm) => {
  gridRef = $vm
  detail.value = { enabled: true, loadKey: 'Order_Id', url: 'api/Demo_Order/getDetailPage' }
}
const onInited = async () => {
  gridRef.height = gridRef.height - 280
  gridRef.detailOptions.columns.forEach(c => {
    if (c.field === 'Qty') c.require = true
  })
}

5.3 查询 Hook

Hook参数说明
searchBefore(param)wheres/page/sort/url/valuereturn false 取消;param.wheres.push({name,value,displayType})
searchAfter(param, result)结果后处理 rows
searchDetailBefore(param, callBack, table, item)明细弹框/列表明细加载前
searchDetailAfter(param, data)明细加载后
searchSubDetailBefore/After三级明细
resetSearchFormAfter()查询重置后

传参到后端: searchBefore 中设 param.value = { key: val },后端 options.Value 读取。

5.4 新建/编辑/保存 Hook

Hook说明
modelOpenBefore(row) / modelOpenBeforeAsync弹框打开前
modelOpenAfter(row, currentAction, isCopyClick)弹框打开后,设默认值/焦点
saveConfirm(callback, formData, isAdd)保存前确认,需调 callback() 继续
addBefore(formData, isCopyClick) / addBeforeAsync新建保存前,return false 阻止
addAfter(result, isCopyClick)新建保存后
updateBefore(formData) / updateBeforeAsync编辑保存前
updateAfter(result)编辑保存后
submitBefore/After(formData, isAdd, isCopyClick)统一提交(async)
onModelClose(iconClick)return false 阻止关闭
continueAddAfter(formFields, formData, res)连续新建后
copyDataBefore(rows)复制前
resetAddFormBefore/After / resetUpdateFormBefore/After表单重置
resetEditFormBefore/After(row, isAdd, isCopyClick)编辑表单重置

5.5 删除 Hook

Hook说明
delBefore(ids, rows) / delBeforeAsync主表删除前
delAfter(result)主表删除后
getDelMessage(rows)自定义删除提示文案
delRowBefore/After(rows, table, item, index)弹框明细行删除(仅前端 table)
delDetailRow(rows, table)同上(旧)

5.6 审批/流程/打印 Hook

Hook说明
auditBefore(ids, rows, callback) / auditAfter审批前/后
auditModelOpenBefore(rows, isAnti, view)审批弹框前
boxAuditOptionOpenBefore(row)审批选项弹框
getAuditTable(rows, isAnti, view)指定审批表名
flowLoadAfter(form, result)流程信息加载后
cancelAuditBefore/After / urgentAuditAfter / antiAfter撤销/催办/反审
printBefore(rows) / printModelClose()打印

5.7 导入/导出 Hook

Hook说明
importBefore(formData, callback)FormData 可加额外字段;异步则 callback()
importAfter(data)导入后刷新
importDetailBefore(formData, callback) / importDetailAfter明细导入
exportBefore(param) / exportAfter(res, param)导出
getFileName(isDetail)自定义导出文件名
reloadDicSource()重载字典

5.8 表格交互 Hook

Hook说明
rowClick / rowDbClick行点击/双击
rowChange / selectionChange复选框
selectable(row, index)是否可选
beginEdit / endEditBefore行内编辑
spanMethod / detailSpanMethod单元格合并
sortEnd / detailSortEnd拖拽排序
tableAddRowBefore / getDefaultRow行内编辑默认行
headerDragend / detailHeaderDragend列宽拖动
tabClick(name)表单分组
fullscreen(full)全屏

5.9 明细表 Hook

Hook说明
detailAddRowBefore(table, item)添加行,return {}[{}] 默认值
addDetailRow(table, item, index)二/三级表默认行
detailRowClick/Change/Selectable明细行事件
detailTabsClick(table)一对多 tabs
detailOptions.beginEdit/endEditBefore/endEditAfter明细编辑控制(onInited 中配置)
checkEdit(row, column, index)动态是否可编辑

5.10 字段级事件(editFormOptions / columns)

// 编辑表单字段
const opt = gridRef.getFormOption('字段名')
opt.onChange = (val) => { }
opt.onKeyPress = ($event) => { }
opt.blur = () => { }
opt.focus = () => { }

// 查询表单
gridRef.getSearchFormOption('字段名')

// 明细列 formatter 实时计算
c.formatter = (row) => row.A + row.B

5.11 Slots 插槽

Slot位置
gridHeader / gridBody / gridFooter列表页上/中/下
btnLeft / btnRight工具栏按钮区
modelHeader / modelBody / modelFooter编辑弹框
detailContent明细表按钮左侧
modelBtn / modelRight弹框按钮
importContent / auditContent / auditButton / printContent导入/审批/打印

5.12 ViewGrid 实例方法(gridRef)

方法说明
search() / refresh()刷新主表
getSelectRows() / getSelected()主表选中行
getDetailSelectRows(table)明细选中
getTable(table) / getTableRef(table)vol-table 实例
getFormOption(field) / getSearchFormOption(field)字段配置
getSearchParameters()当前查询参数
add() / edit(row) / del(row)CRUD
clearSelection() / toggleRowSelection(row)选择
setFixedSearchForm(visiable)固定查询栏
updateDetailTableSummaryTotal(fields, table)明细合计
searchFocus(field) / editFocus(field)焦点
initDicKeys()刷新字典
filterPermission(table, action)按钮权限

组件通信:

// 子组件 → ViewGrid
proxy.$emit('parentCall', p => p.search())
// 父 → 子 refs
proxy.$refs.gridHeader / gridFooter / modelBody

5.13 常用前端模式

字典/下拉绑定:

const fieldOption = editFormOptions.flat().find(o => o.field === 'stock_in_no')
fieldOption.data = [{ key: '1', value: '选项1' }]
fieldOption.dataKey = 'key'
fieldOption.dataShowKey = 'value'

字段 extra 搜索按钮:

fieldOption.extra = {
  icon: 'el-icon-search', text: '搜索',
  click: async () => { await handleSearch() }
}

HTTP:

await proxy.http.get('api/Controller/Action', {}, true)
await proxy.http.post('api/Controller/Action', { param: value }, true)
// 第三参数 true = 原始 JSON 响应

vol-box 弹窗:

<vol-box v-model="visible" title="选单" :width="900" :lazy="true">
  <!-- 内容 -->
  <template #footer>
    <el-button type="primary" @click="confirm">确定</el-button>
  </template>
</vol-box>

明细表操作:

const detailTable = gridRef.getTable()
detailTable.rowData.push({ Field1: val })
gridRef.detailOptions.columns.forEach(c => {
  if (c.field === 'qty') c.min = 1
})

自定义按钮:

gridRef.buttons.splice(1, 0, {
  name: '自定义', icon: 'el-icon-document', type: 'primary', plain: true,
  onClick: () => { const rows = gridRef.getSelectRows() }
})
// 列按钮:columns[].render + click
// 明细按钮:detailOptions.buttons
// 弹框按钮:boxButtons

5.14 关键属性(onInit/onInited 中修改)

主表: tableV2, columnIndex, ck, buttons, splitButtons, boxButtons, load, height, pagination, boxOptions, editFormFields/Options, searchFormFields/Options, single, summary, doubleEdit, fixedSearchForm, queryFields, url, hasDetail, continueAdd, rowKey, sortable, showTableAudit, subDetails, dyScript

detailOptions: columns, height, buttons, url, load, edit, doubleEdit, pagination, beginEdit, summary, tableV2


六、独立组件 API(非 ViewGrid 场景)

6.1 vol-form

Props: formFields, formRules(二维数组), loadKey, labelWidth, eventNext

字段 type: input, textarea, decimal, number, date/datetime, select/selectList/selectTable/selectBox/cascader/treeSelect, switch, radio, checkbox, img/file/excel, editor, password

方法: validate(), reset(), focus(field), initDicKeys(), clearValidate()

字段事件: onKeyPress, onChange, blur, focus, uploadBefore/After, validator, render

6.2 vol-table

Props: columns, url/tableData, tableV2, ck, pagination, height, beginEdit/endEditBefore, spanMethod, reserveSelection, rowKey

Emit: loadBefore, loadAfter, selectionChange, rowClick, rowDbClick, paginationChange

方法: load(), addRow(), delRow(), getSelected(), setEdit(), updateSummary(), validate(), focus()

columns: field, title, edit.type, bind, formatter, render, click, summary, checkEdit, onChange

6.3 vol-box

Props: v-model, title, width, height, padding, lazy, onModelClose
Slot: #footer


七、前端业务场景速查(生成页常用)

场景主要 Hook/配置
查询默认值searchFormFields 赋值;searchBefore 改 wheres
隐藏按钮onInit 中 filter buttons/boxButtons;filterPermission
弹框选数据回填modelOpenAfter + vol-box/selectTable
select 联动getFormOption('A').onChange 改 B 的 data
明细实时计算columns.formatter 或 onChange
动态隐藏字段getFormOption('x').hidden = true
只读/必填formReadonly 模式;column.require / readonly
多 tab 页面tabs 路由工具
页面多开tables 配置多菜单
大批量表格tableV2: true
列表下展示子表#gridFooter + 子组件
导入带参数importBefore append FormData
导出自定义名getFileName
保存前确认saveConfirm
调用其他页编辑openEdit 模式

八、问题排查(AI 诊断清单)

现象检查项
下拉无数据onInit 是否设 data/dataKey/dataShowKey;字典 loadKey;API 格式
POST 404Partial Controller 是否 [HttpPost];URL api/{表}/{Action};方法名大小写
表格有数据但列空白 / field 为 undefined自定义 API 是否 return JsonNormal(勿用 Json);columns.field 与实体属性大小写一致
「未找到请求地址」http.js 报错 → 路由/HTTP 方法不匹配
CS1513 缺少 }Partial 文件 namespace/class 花括号
CS0535 未实现接口IService 声明了方法但 Service 未实现
保存后数据未写入是否 return webResponse.Error;OnExecuted 是否抛异常
明细未保存一对多是否用 Details[];Update 是否处理 add/update/del
options 修改丢失是否写在 options.js(会被覆盖)→ 改 onInit
审核后业务未执行AuditOnExecuted / AuditWorkFlowExecuted 是否注册
SSR/文档站脚本VuePress client.ts 用 onMounted 加载第三方脚本

九、AI 开发工作流

9.1 新增自定义 API

  1. Partial/I{表}Service.cs 声明
  2. Partial/{表}Service.cs 实现(可用 repository.Find/Queryable)
  3. Partial/{表}Controller.cs 暴露 [HttpPost("Name")]返回业务数据用 return JsonNormal(...)(勿用 Json,否则字段变小驼峰与前端 columns 对不上)
  4. 前端 proxy.http.post('api/{表}/Name', data, true)

9.2 扩展生成页业务

  1. 只改 {表}.vue(及子组件),不改 ViewGrid 源码
  2. onInit:buttons、detail、字典、url
  3. onInited:列属性、高度、detailOptions
  4. 按需挂 Hook:searchBefore、addBefore、modelOpenAfter 等
  5. 复杂 UI 用 slots + vol-box

9.3 扩展后台 CRUD

  1. 只改 Partial/{表}Service.cs
  2. 优先钩子,必要时 override + base.Xxx()
  3. 事务内逻辑放 OnExecuted,勿重复开事务
  4. 跨表注入其他 Repository

十、完整技能检查清单

A. 框架基础

  • 区分会被覆盖 vs 不会覆盖的文件
  • Partial 三件套(IService + Service + Controller)
  • 自定义 API 返回业务数据用 JsonNormal(勿 Json 小驼峰)
  • 标准 API 路由规则 api/{表}/{Action}
  • proxy.http 第三参数 true
  • gridRef = $vm 获取 ViewGrid 实例

B. 后端查询

  • QueryRelativeList / QueryRelativeExpression
  • QuerySql / OrderByExpression / SummaryExpress
  • GetPageDataOnExecuted / options.Value
  • GetDetailPage / GetDetailSummary
  • OR 查询 Fields

C. 后端 CRUD

  • AddOnExecuting / AddOnExecuted
  • UpdateOnExecuting(add/update/del 明细)
  • DelOnExecuting / DelOnExecuted
  • 主从明细 List 强转
  • 一对多 Details[]

D. 后端审批/导入导出

  • AuditOnExecuted / AuditWorkFlowExecuting
  • AntiAuditOnExecuted
  • ImportOnExecuting / ExportOnExecuting
  • DownLoadTemplateColumns / ExcelHeaderMap

E. 前端生命周期

  • onInit / onInited / dicInited
  • searchBefore(wheres + param.value)
  • modelOpenBefore/After
  • addBefore/updateBefore/addAfter/updateAfter
  • delBefore / saveConfirm / onModelClose

F. 前端明细/表格

  • detail / details / subDetails 配置
  • detailAddRowBefore / searchDetailBefore
  • beginEdit / spanMethod / sortEnd
  • importBefore / exportBefore / printBefore
  • auditBefore / flowLoadAfter

G. 前端 UI 扩展

  • slots(gridFooter/modelBody/detailContent)
  • 自定义 buttons/boxButtons/columns 列按钮
  • vol-box 选单弹窗
  • getFormOption 字段事件
  • parentCall 组件通信

H. 独立组件

  • vol-form formRules + validate
  • vol-table loadBefore + 行内编辑
  • vol-box 弹窗

十一、用户自然语言需求 → AI 解析

AI 从用户消息中提取下表字段;用户不必写框架术语。

【表名】{TableName}
【模块路径】{module}/{folder}(如 mes/stock)
【编辑模式】标准弹框 / 新页面编辑(Edit.vue)— 不确定则看项目是否存在 Edit.vue
【前端/后端/都要】
【业务描述】例:入库单明细从采购单弹框多选;审核通过后写库存;仅查当前用户数据
【规则】触发时机、字段映射、校验、单选/多选
【数据来源】(可选)选哪张表 / API 名

AI 收到后按 §零 工作流 处理。


十二、全量业务逻辑实现手册

AI MUST:§12.Z 索引§12 代码模板 生成业务代码。
AI SHOULD: 模板不足或需确认官方写法时,查 v3.volcore.xyz(§十三 URL 表,与离线 doc.web 同路径)。
AI MUST: 读取并修改用户项目中的 {表}.vueEdit.vue(新页面编辑)、Partial/{表}Service.cs禁止.jsx 写业务;勿假设存在 Demo_Order。

12.0 章节目录

章节内容
§12.A后台 Service Func 委托业务(ApplicationServiceBase + base.xxx 调用)
§12.B数据库访问 EF(§12.B.0 重点) + cs/dev 常用功能
§12.CViewGrid 全部 Hook(生命周期/CRUD/审批/导入导出/表格/明细)
§12.DViewGrid 公共方法、属性、Slots、主从一对多
§12.E字段级事件(主表表单/明细表)
§12.F前端 general 通用功能
§12.G前端 web/table 查询与表格
§12.H前端 web/form 编辑表单
§12.I独立组件 vol-form / vol-table / vol-box
§12.J弹出框选数据、自定义按钮、API 等 J.1–J.13(含对应关系总表 + 完整代码)
§12.K新页面编辑 Edit.vue + vol-edit Hook(独立页新建/编辑)
§12.Z全量操作索引(开发者说法 → 章节号)

§12.A 后台 Service 业务扩展(Partial/{表}Service.cs)

文档类型: docs/cs/service/ — 与 ApplicationServiceBase.cs Func 定义一一对应。
在线总览: http://v3.volcore.xyz/docs/cs/service/guid.html
源码: VolPro.Core/BaseProvider/ApplicationServiceBase.cs(委托声明)、ServiceBase.cs(Invoke 时机)

A.0 Func 委托机制(AI 必须遵守)

统一写法:

public override WebResponseContent Add(SaveModel saveDataModel)
{
    var res = new WebResponseContent();
    AddOnExecuting = (TEntity main, object list) => {
        var details = list as List<TDetail>;
        return res.OK(); // res.Error("msg") 中止
    };
    AddOnExecuted = (TEntity main, object list) => res.OK(); // 同事务,Error 回滚
    return base.Add(saveDataModel); // MUST:框架在此 Invoke 上述 Func
}

规则:

  1. Func 在 return base.xxx(...) 之前 赋值到 this 上。
  2. WebResponseContent.ErrorStatus=false → 中止后续步骤;*OnExecuted 内 Error → 事务回滚
  3. 异步接口(生成器勾选):重写 AddAsync 等,可另赋 AddOnExecutingAsync;与同步版 链式都执行
  4. 禁止 在 Partial 中重写整个 Save 逻辑;仅通过 Func 扩展。完整自定义 API 用 Partial Controller(§12.J.10)。

执行顺序速查:

操作顺序
GetPageDataQueryRelativeList → QuerySql/租户 → QueryRelativeExpression → 分页 → SummaryExpress → GetPageDataOnExecuted
AddAddOnExecute → 实体转换校验 → AddOnExecuting(Async) → 事务 Insert → AddOnExecuted(Async) → AddWorkFlow*
UpdateUpdateOnExecute → 转换校验 → UpdateOnExecuting(Async) → 事务 Update 主从 → UpdateOnExecuted(Async)
DelDelOnExecuting(Async) → 事务 Delete → DelOnExecuted(Async)

Func 与子文档:

Func 属性文档
QueryRelativeList / QueryRelativeExpression / SummaryExpress / GetPageDataOnExecutedsearch.md
AddOnExecute / AddOnExecuting / AddOnExecuted / AddWorkFlow*add.md
UpdateOnExecute / UpdateOnExecuting / UpdateOnExecutedupdate.md
DelOnExecuting / DelOnExecuteddel.md
Audit* / AntiAudit*audit.md / antiAudit.md
Import* / Export*importData.md / exportData.md

A.1 主表查询 GetPageData

public override PageGridData<{T}> GetPageData(PageDataOptions options)
{
    object extraValue = options.Value; // 前端 searchBefore 的 param.value

    QueryRelativeList = (List<SearchParameters> parameters) => {
        foreach (var item in parameters) {
            if (item.Name == "{字段}") { var v = item.Value; item.Value = null; }
        }
    };

    QueryRelativeExpression = (IQueryable<{T}> q) => {
        // q = q.Where(x => x.CreateID == UserContext.Current.UserId);
        // q = q.Include(x => x.{明细});
        return q;
    };

    // QuerySql = @"select 全部列 from {表} where ..."; // 原生 SQL

    OrderByExpression = x => new Dictionary<object, QueryOrderBy>() {
        { x.CreateDate, QueryOrderBy.Desc }
    };

    SummaryExpress = (IQueryable<{T}> q) => q.GroupBy(x => 1).Select(g => new {
        TotalQty = g.Sum(x => x.Qty)
    }).FirstOrDefault();

    GetPageDataOnExecuted = (PageGridData<{T}> grid) => {
        // 改 grid.rows
    };

    return base.GetPageData(options);
}

A.2 明细表查询 GetDetailPage / 明细合计

public override object GetDetailPage(PageDataOptions pageData)
{
    // 一对多:pageData.TableName 区分明细表
    // return base.GetDetailPage(pageData);
    return base.GetDetailPage(pageData);
}

protected override object GetDetailSummary<{TDetail}>(IQueryable<{TDetail}> queryable)
{
    return queryable.GroupBy(x => 1).Select(g => new { Total = g.Sum(x => x.Amount) }).FirstOrDefault();
}

A.3 OR 多字段查询

前端 searchBefore:

param.wheres.push({ name: 'Keyword', value: 'xxx', displayType: 'like', fields: ['Field1','Field2','Field3'] })

A.4 新建 Add

public override WebResponseContent Add(SaveModel saveDataModel)
{
    var res = new WebResponseContent();
    // 可选:实体转换前操作原始 SaveModel
    AddOnExecute = (SaveModel m) => res.OK();

    AddOnExecuting = ({T} main, object list) => {
        var details = list as List<{TDetail}>;
        // 校验、赋默认值(写库前)
        return res.OK();
    };
    AddOnExecuted = ({T} main, object list) => {
        // 同事务写关联表;return res.Error("msg") 回滚
        return res.OK();
    };
    AddWorkFlowExecuting = (main) => true;
    AddWorkFlowExecuted = (main, userIds) => { };
    return base.Add(saveDataModel);
}

A.5 修改 Update

public override WebResponseContent Update(SaveModel saveDataModel)
{
    var res = new WebResponseContent();
    // 非前端提交字段:saveDataModel.MainData["字段"]="值" 或 UpdateOnExecute
    UpdateOnExecute = (SaveModel m) => res.OK();

    UpdateOnExecuting = ({T} main, object addList, object updateList, List<object> delKeys) => {
        var add = addList as List<{TDetail}>;
        var upd = updateList as List<{TDetail}>;
        // 一对多:MultipleTableEntity.GetAddList(typeof(TDetail2), null)
        return res.OK();
    };
    UpdateOnExecuted = ({T} main, object addList, object updateList, List<object> delKeys) => res.OK();
    return base.Update(saveDataModel);
}

A.6 删除 Del

public override WebResponseContent Del(object[] keys, bool delList = true)
{
    var res = new WebResponseContent();
    DelOnExecuting = (_keys) => {
        // 已审核不可删
        return res.OK();
    };
    DelOnExecuted = (_keys) => res.OK();
    return base.Del(keys, delList);
}

A.7 审批 Audit

public override WebResponseContent Audit(object[] keys, int? auditStatus, string auditReason)
{
    var res = new WebResponseContent();
    AuditWorkFlowExecuting = ({T} order, AuditStatus status, bool lastAudit) => res.OK();
    AuditWorkFlowExecuted = ({T} order, AuditStatus status, List<int> nextUserIds, bool lastAudit) => {
        if (lastAudit) { /* 流程结束写业务 */ }
        return res.OK();
    };
    AuditOnExecuting = (List<{T}> list) => res.OK();
    AuditOnExecuted = (List<{T}> list) => res.OK();
    return base.Audit(keys, auditStatus, auditReason);
}

流程信息: order.GetTableWorkflow() / GetTableCurrentFlowStep() / GetTablePreFlowStep()

A.8 反审 AntiAudit

public override WebResponseContent AntiAudit(AntiData antiData)
{
    var res = new WebResponseContent();
    AntiAuditOnExecuting = (entity) => res.OK();
    AntiAuditOnExecuted = (entity) => { /* 回滚库存等 */ return res.OK(); };
    return base.AntiAudit(antiData);
}

A.9 导入 Import / 模板 / 导出 Export

public override WebResponseContent Import(List<IFormFile> files)
{
    ImportOnExecuting = (List<{T}> rows) => { /* 校验 */ return new WebResponseContent().OK(); };
    ImportOnExecuted = (List<{T}> rows) => new WebResponseContent().OK();
    ImportStartRowIndex = 1;
    // ExcelHeaderMap, ImportOnReadCellValue
    return base.Import(files);
}

public override WebResponseContent Export(PageDataOptions pageData)
{
    ExportColumns = x => new { x.Field1, x.Field2 };
    ExportOnExecuting = (List<{T}> rows, List<string> ignore) => new WebResponseContent().OK();
    Limit = 10000;
    return base.Export(pageData);
}

A.10 上传 Upload

public override WebResponseContent Upload(List<IFormFile> files)
{
    UploadFolder = "Upload/Tables/{表}/";
    IsRoot = true;
    return base.Upload(files);
}

A.11 打印 Print(Startup + PrintFilter)

// Startup: PrintContainer.Use<{T}, {TDetail}>("模板名", mainFields, detailFields);
public class {T}PrintFilter : PrintFilter<{T}> {
    public override IQueryable<{T}> Query(PrintQuery query) => base.Query(query);
}

A.12 自定义 API 三件套

见 §12.J.10(IService 声明 + Service 实现 + Controller [HttpPost("Action")])。


§12.B 后台 cs/dev 常用操作

B.0 数据库访问(EF,默认必用 · 重点)

在线文档: db.html — EF 操作数据库open in new window
非必要勿用: EFsql.html — EF 执行原生 SQLopen in new windowrepository.QueryList / Dapper 参数化 SQL)。AI 默认只用 EF LINQ,仅当 LINQ 无法表达复杂 SQL 时才考虑原生 SQL,且须说明理由。

B.0.1 写代码位置与访问方式

规则说明
写哪里Partial/{表}Service.cs(业务查询/写库);Controller 仅转发或简单组合
用什么当前表 repositoryServiceBase 注入);其他表 ctor 注入 I{其他表}Repository
禁止在 Controller 直接写复杂 LINQ(应放 Service);非 Partial Service;默认走 EFSql/Dapper
NoTrackingEF 默认 NoTracking;更新 MUST repository.Update(model, cols, true) + SaveChanges()
// 跨表:Partial Service 构造方法注入
public partial class Sys_WorkFlowTableService
{
    private readonly ISys_WorkFlowTableRepository _repository;
    private readonly ISys_WorkFlowTableStepRepository _stepRepository;
    [ActivatorUtilitiesConstructor]
    public Sys_WorkFlowTableService(
        ISys_WorkFlowTableRepository dbRepository,
        IHttpContextAccessor httpContextAccessor,
        ISys_WorkFlowTableStepRepository stepRepository)
        : base(dbRepository)
    {
        _repository = dbRepository;
        _stepRepository = stepRepository; // 操作其他表
    }
}

B.0.2 EF 查询(repository LINQ)

using Microsoft.EntityFrameworkCore;
using VolPro.Core.Extensions; // TakePage、WhereIF

// 单表
var list = repository.FindAsIQueryable(x => x.Enable == 1).ToList();
var one = await repository.FindAsIQueryable(x => x.Id == id).FirstOrDefaultAsync();
var data = repository.FindAsIQueryable(x => true)
    .Select(s => new { s.Field1, s.Field2 }).ToList();

// 模糊 / in
repository.FindAsIQueryable(x => x.Name.Contains(kw)).ToList();
repository.FindAsIQueryable(x => x.Name.StartsWith(kw)).ToList();
repository.FindAsIQueryable(x => x.Name.EndsWith(kw)).ToList();
var ids = new List<int> { 1, 2, 3 };
repository.FindAsIQueryable(x => ids.Contains(x.Id)).ToList();

// 动态条件 WhereIF
repository.WhereIF(!string.IsNullOrEmpty(kw), x => x.Name.Contains(kw))
    .WhereIF(status > 0, x => x.Status == status)
    .ToList();

// 主子表 Include(生成器已配置明细 navigation)
var list = repository.FindAsIQueryable(x => true).Include(x => x.明细表).ToList();

// 是否存在
var exists = repository.Exists(x => x.Code == code);
var existsAsync = await repository.ExistsAsync(x => x.Code == code);

B.0.3 分页 — 一律 TakePage

MUST: 所有 IQueryable 分页使用 TakePage(page, rows)(框架扩展,内部处理 Skip/Take),禁止手写 Skip((page-1)*rows).Take(rows)

// 普通分页
var page = 1; var rows = 30;
var list = repository.FindAsIQueryable(x => 条件).TakePage(page, rows).ToList();

// 带排序 + 匿名投影
var query = repository.FindAsIQueryable(x => x.Enable == 1);
var total = await query.CountAsync();
var data = await query.OrderByDescending(x => x.CreateDate)
    .TakePage(options.Page, options.Rows)
    .Select(s => new { s.Id, s.Name }).ToListAsync();

B.0.4 vol-table / selectTable 自定义分页接口(ConvertQueryFilter)

场景: 独立 vol-tableselectTableitem.url / url 指向自定义接口(非标准 getPageData)。前端 POST PageDataOptions(含 PageRowsWheres/FilterSortValue 等),与 view-grid 查询格式一致。

后台 MUST:

  1. 参数 [FromBody] PageDataOptions options
  2. options.ConvertQueryFilter<{实体}>() — 将前端 wheres 自动转为 LINQ(Sys_User 换为实际查询实体 {T}
  3. 业务过滤 .Where(x => ...)
  4. CountAsync() 得 total
  5. 排序 + TakePage(options.Page, options.Rows) + Select 投影
  6. 返回 { total, rows };Controller return JsonNormal(...)(字段与前端 columns.field 大小写一致)
// Partial/I{表}Service.cs
Task<object> SearchSysUser(PageDataOptions options);

// Partial/{表}Service.cs — Sys_User 换为要查询的实体
public async Task<object> SearchSysUser(PageDataOptions options)
{
    var query = options.ConvertQueryFilter<Sys_User>().Where(x => x.Enable == 1);
    var total = await query.CountAsync();
    var rows = await query.OrderByDescending(x => x.User_Id)
        .TakePage(options.Page, options.Rows)
        .Select(s => new
        {
            s.User_Id,
            s.UserName,
            s.UserTrueName,
            s.Gender,
            s.PhoneNo,
            s.Email
        }).ToListAsync();
    return new { total, rows };
}

// Partial/{表}Controller.cs
[HttpPost("SearchSysUser")]
public async Task<IActionResult> SearchSysUser([FromBody] PageDataOptions options)
{
    return JsonNormal(await _service.SearchSysUser(options));
}

前端 wheres 来源: vol-table loadBeforeparam.wheres;selectTable 列设 search: true 时框架自动写入 wheres;或 loadBeforeparam.value 由后台在 ConvertQueryFilter 之外再 Where 处理。

selectTable 推荐: 自定义 api/{表}/SearchXxx + 上模板,优于直接 getPageData(权限/字段可控)。见 §12.J.3。

B.0.5 新建 / 更新 / 删除

// 新建
var model = newModel();
model.SetCreateDefaultVal(); // 可选:创建人、创建时间
repository.Add(model);
repository.SaveChanges(); // 保存后主键自动回填

// 批量新建
repository.AddRange(list);
repository.SaveChanges();

// 更新(主键必须有值)
model.SetModifyDefaultVal();
repository.Update(model, x => new { x.Field1, x.Field2 });
repository.SaveChanges();

// 批量更新
repository.UpdateRange(list, x => new { x.Field1 }, false);
repository.SaveChanges();

// 按条件更新/删除(不立即提交时第三参数 false)
repository.Update(model, x => new { x.Status }, false);
repository.Delete(x => 条件, false);

// 删除单条 / 批量主键
repository.Delete(model);
repository.DeleteWithKeys(new object[] { id1, id2 });
repository.SaveChanges();

B.0.6 事务与其他

// EF 事务
repository.DbContextBeginTransaction(() =>
{
    repository.Add(main);
    _otherRepository.Add(detail);
    repository.SaveChanges();
    return webResponse.OK();
});

// 调试 SQL
Console.WriteLine(queryable.ToQueryString());

// 原生 EF Set(少用)
repository.DbContext.Set<Sys_User>();

B.0.7 与生成页 GetPageData 的分工

场景做法
标准生成页列表不重写 GetPageData;用 QueryRelativeExpression / QueryRelativeList(§12.A.1)
vol-table / selectTable / 弹框内嵌表自定义 url§12.B.0.4:ConvertQueryFilter + TakePage + { total, rows }
非分页列表 APIrepository.FindAsIQueryable(...).Take(N) 或 ToList
复杂 SQL 非 EF 可表达最后手段:EFsql / Dapper(文档 EFSql),须注释说明

B.1 其他 cs/dev 速查

操作实现要点
调试断点 Partial Service;Console.WriteLine(queryable.ToQueryString())
appsettingsAppSetting.GetSettingString(key) / 注入 IOptions
API 参数POST [FromBody] Dictionary<string,object>;GET 查询字符串
JSON 返回大小写自定义接口 return JsonNormal(data);全局 Json() 为小驼峰;标准 getPageData 已 JsonNormal
接口免登录[AllowAnonymous] on Partial Controller Action
重写接口权限Partial Controller 重写方法或框架权限过滤器配置
序列化Newtonsoft / System.Text.Json 按项目配置
字典DictionaryManager.GetDictionary("dicNo")
EF 查询/分页§12.B.0 · FindAsIQueryable / WhereIF / TakePage
EF 条件更新删除§12.B.0.5 · Update / Delete / SaveChanges
EF 事务repository.DbContextBeginTransaction(() => {})
EF 多表Include / Join in QueryRelativeExpression 或 ctor 注入多 Repository
EF 原生 SQL非默认;见 EFSql 文档,优先 LINQ
Dapper / SqlSugar非默认;跨库或特殊场景
HttpContext注入 IHttpContextAccessor
当前用户UserContext.Current.UserId / UserName
用户权限角色/菜单 API
单据号规则编码服务
定时任务后台 Task 接口配置
DI 注入ctor 注入 I{X}Repository / I{X}Service
Repository 实例{T}Repository.Instance 或 ctor 注入
视图生成代码代码生成器配置视图
缓存Memory / Redis 封装
Model 校验DataAnnotations / FluentValidation
日志Logger.Info(LoggerType.Info, message)
租户过滤QueryRelativeExpression 加租户字段
SqlBulkInsert批量扩展方法
工具类VolPro.Core.Utilities / 项目 util

§12.C ViewGrid 全部 Hook({表}.vue 绑定 :hookName="fn")

文档类型: docs/view-grid/view-grid 组件对标准生成页的属性/方法/Slots;不是 web 示例集。
适用文件: 标准模式 {表}.vue;新页面编辑的列表页仍用 view-grid,编辑页见 §12.K

通用规则: 返回 false 取消操作;async 钩子可返回 Promise;gridRef = $vm 在 onInit。

Hook参数返回值/作用代码模板
onInit$vmgridRef=$vm
onInited改 height/columns/detailOptions
showAdvancedSearchBeforeboolreturn true
dicIniteddic级联 initDicKeys
searchBeforeparamboolparam.wheres.push({name,value,displayType}); param.value={}; return true
searchAfterparam,resultbool后处理 result.rows
searchDetailBeforeparam,callback,table,itembool改 param.wheres
searchDetailAfterparam,databool
searchSubDetailBefore/Afterrows,table,itembool三级明细
resetSearchFormAfter重置后
modelOpenBeforerowbool打开弹框前
modelOpenBeforeAsyncrowPromise异步取数
modelOpenAfterrow,action,isCopy默认值/焦点 editFocus
saveConfirmcallback,formData,isAddproxy.$confirm().then(()=>callback(true))
addBeforeformData,isCopybool校验 return false 阻止
addBeforeAsyncformData,isCopyPromise&lt;bool&gt;
addAfterresult,isCopybool刷新 search()
updateBeforeformDatabool
updateBeforeAsyncformDataPromise&lt;bool&gt;
updateAfterresultbool
submitBeforeformData,isAdd,isCopyPromise&lt;bool&gt;统一提交前
submitAfterresult,formData,isAdd,isCopyPromise提交后
delBeforeids,rowsbool删除前
delBeforeAsyncids,rowsPromise&lt;bool&gt;
delAfterresultbool
getDelMessagerowsstring自定义提示
delRowBefore/Afterrows,table,item,indexbool弹框明细行
delDetailRowrows,tablebool旧明细删行
auditBeforeids,rows,callbackbool审批前
auditAfterresult,rowsbool
auditModelOpenBeforerows,isAnti,viewbool
boxAuditOptionOpenBeforerow
getAuditTablerows,isAnti,viewstring返回表名
flowLoadAfterform,result流程加载后
cancelAuditBefore/Afterrows,isCancel / result,rows,isCancelbool撤销
urgentAuditAfterresult,rowsbool催办后
antiAfterresult,param,rows,filebool反审后
importBeforeformData,callbackboolformData.append('k',v)
importAfterdataboolsearch()
importDetailBeforeformData,callbackbool明细导入
importDetailAfterrowsbool
exportBeforeparambool
exportAfterres,param
getFileNameisDetailstring'导出_'+Date.now()
reloadDicSource
onModelCloseiconClickboolreturn false 阻止关闭
printBeforerowsbool
printModelClose
selectablerow,indexbool能否勾选
rowClick/rowDbClick{row,column,event}
rowChange/selectionChangerows
beginEdit/endEditBeforerow,column,indexbool行内编辑
spanMethod/detailSpanMethodmerge args, rowsElement 合并
sortEnd/detailSortEndindices,rows拖拽排序
tableAddRowBefore/getDefaultRowrow,indexbool/object默认行
detailAddRowBeforetable,itemobject/arrayreturn {字段:默认值}
addDetailRowtable,item,index二三级默认行
detailRowClick/Change/Selectable...明细行事件
detailTabsClicktable一对多 tabs
checkEditrow,column,indexbool动态可编辑
tabClickname表单分组
copyDataBeforerowsbool复制
continueAddAfterformFields,formData,res连续新建
resetAdd/Update/EditForm Before/After...bool表单重置
headerDragend/detailHeaderDragendwidth event,table列宽
fullscreenfullbool全屏
mounted少用

saveConfirm 完整示例:

const saveConfirm = (callback, formData, isAdd) => {
  proxy.$confirm('确认保存?', '提示', { type: 'warning' })
    .then(() => callback(true))
}

importDetailBatch(大批量明细导入防卡顿): 分批 push 到 rowData,用 setTimeout 或 requestAnimationFrame 分帧。


§12.D ViewGrid 公共方法 / 属性 / Slots / 主从

D.1 公共方法(gridRef)

方法用法
search()/refresh()刷新主表
getSelectRows()/getSelected()选中行
getDetailSelectRows(table)明细选中
getTable(table)/getTableRef(table)明细 vol-table
getElementTableRef()Element table
getFormOption(field)/getSearchFormOption(field)字段配置
getSearchParameters()当前 wheres
add()/edit(row)/del(row)调框架 CRUD
clearSelection()/toggleRowSelection(row)选择
setFixedSearchForm(true)固定查询
updateDetailTableSummaryTotal(fields,table)明细合计
searchFocus(field)/editFocus(field,callback)焦点
initDicKeys()刷新字典
filterPermission(table,action)权限
rowData()主表全部行
getCurrentAction()Add/Update 等

D.2 常用属性(onInit/onInited)

gridRef.tableV2 = true          // 高性能表格
gridRef.single = true           // 单选
gridRef.ck = false              // 隐藏 checkbox
gridRef.fixedSearchForm = true  // 默认展开查询
gridRef.continueAdd = true      // 连续添加
gridRef.doubleEdit = true       // 双击编辑
gridRef.url = 'api/{表}/'       // 接口前缀
gridRef.pagination = { size: 30, sizes: [30,60,100] }
gridRef.boxOptions.width = 1000
gridRef.queryFields = ['Field1','Field2'] // 多快捷查询
gridRef.dyScript = `{ ... }`    // 动态脚本扩展

D.3 Slots

#gridHeader #gridBody #gridFooter #btnLeft #btnRight #modelHeader #modelBody #modelFooter #detailContent #modelBtn #modelRight #importContent #auditContent #auditButton #printContent

D.4 主从 / 一对多 / 三级

// 主从(单明细)
detail.value = { enabled: true, loadKey: '{主键}', url: 'api/{表}/getDetailPage' }

// 一对多
details.value = [
  { cnName: '明细1', table: '{Detail1}', columns: [...], url: '...' },
  { cnName: '明细2', table: '{Detail2}', ... }
]

// 三级 subDetails 在 onInit 配置 subDetails 数组

§12.E 字段级事件(view-grid 内嵌 vol-form / vol-table)

机制: view-grid 把 editFormOptionscolumnsdetailOptions.columns 传给内嵌 vol-form/vol-table;事件写在 options 项上,通过 getFormOption / 遍历 columns 绑定。
文档: http://v3.volcore.xyz/docs/view-grid/components.html · event/forminputevent/detailforminput 等。

E.0 绑定位置对照

UI 区域内嵌组件绑定方式典型事件
查询区vol-formgetSearchFormOption('字段')onChange
编辑弹框主表vol-formgetFormOption('字段')onKeyPress、onChange、blur、focus
主表列表vol-tablegridRef.columns 列配置formatter、click;行事件用 Hook rowClick
编辑弹框明细vol-tabledetailOptions.columnsonKeyPress、onChange、blur、formatter、checkEdit

{表}.vueonInit(主表表单/列)onInited(明细列,字典已加载) 中绑定。

E.1 主表编辑表单

const bindFormEvent = (field, events) => {
  const opt = gridRef.getFormOption(field)
  Object.assign(opt, events)
}
bindFormEvent('Qty', {
  onKeyPress: ($e) => { if ($e.keyCode === 13) { /*回车*/ } },
  onChange: (val) => { editFormFields.Amount = val * editFormFields.Price },
  blur: () => {},
  focus: () => {}
})

E.2 查询表单:getSearchFormOption('字段').onChange

E.3 明细 columns

c.onKeyPress / c.onChange / c.blur / c.formatter = (row) => {}
c.checkEdit = (row, column, index) => row.Status !== 1

E.4 事件对照

事件适用控件
onKeyPressinput/textarea/decimal
onChangeselect/date/cascader
blur/focus输入框
formatter明细实时计算显示

§12.F 前端 general 通用功能

文档类型: docs/web/general/常用功能示例(怎么做弹框选数、自定义按钮等);Hook 签名以 view-gridedit 为准。

功能实现
HTTP 请求proxy.http.post('api/{表}/{Action}', params, true) GET 同理
文件下载http.post 返回 blob;或 window.open(url)
自定义按钮§12.J.5
隐藏按钮gridRef.buttons.find(x=>x.name==='新建').hidden=true
权限按钮filterPermission(table,'Add')
vol-box 多弹框多个 vol-box + v-model
弹出框选主表数据§12.J.1
弹出框选明细§12.J.2
selectTable§12.J.3
事件处理§12.E
列表显示明细§12.J.8 gridFooter + rowClick
viewDetail 一对多显示onInited 配置 detail 在列表页展开
日期计算dayjs/moment 在 onChange
tabs 跳转proxy.$tabs.open(...) 或路由
页面多开 tables菜单多开配置
saveConfirm§12.C saveConfirm
openEdit 调其他页弹框引入 {其他表}.vue ref 调 $refs.grid.edit(row) / add()
pagecache 禁缓存路由 meta + keep-alive 配置
全局 cachelocalStorage / 框架 storage
tableV2gridRef.tableV2=true
slot 扩展§12.D.3
debugconsole + Vue DevTools

openEdit 模板:

<OtherTable ref="otherRef" style="display:none" />
<script>
import OtherTable from '@/views/{模块}/{其他表}/{其他表}.vue'
const otherRef = ref()
// 按钮:otherRef.value.$refs.grid.edit({ Id: row.Id })
</script>

§12.G 前端 web/table 查询与表格配置

统一位置: onInit / onInitedcolumnssearchFormOptionsdetailOptionsgridRef.*

功能代码要点
查询自定义按钮 btndetailOptions/buttons/boxButtons + columns.render
dySearch 动态查询searchBefore 动态 wheres;子查询 param.value
dropBtn 下拉按钮组buttons 配置 children
sort 默认排序searchBefore param.sort={Field:'CreateDate',Order:'desc'}
ck 禁止勾选selectable 返回 false
fixed 固定列columns fixed:'left'/'right'
showFilter 显示全部查询setFixedSearchForm(true)
defaultVal 查询默认值onInit searchFormFields.{字段}=值
singleDate 单日期searchFormOptions type/date 配置
card 卡片切换gridRef 卡片模式属性
searchSelectChange 查询联动getSearchFormOption onChange
removeQuery 移除快捷查询queryFields 过滤
queryFields 多快捷字段gridRef.queryFields=[...]
customCell 自定义显示columns.render
formatter 格式化columns.formatter
cellEvent 单元格点击columns.click
rowClick 行点击:rowClick
tips 超出换行columns showOverflowTooltip/textInline
customTips 自定义提示render + tooltip
treeTable 树形rowKey + parentId 数据
headFilter 表头筛选columns 筛选配置
headEvent 自定义表头columns title render
tagColor 数据源颜色columns bind 字典颜色
switch 单选gridRef.single=true
editTable 在线编辑doubleEdit/clickEdit + beginEdit
tableSwitch 显示 switchcolumns edit type switch
head 多级表头columns children 嵌套
summary 合计columns summary:true + 后端 SummaryExpress
hideColumn 动态隐藏列onInited columns.hidden=true
page/hidePage 分页pagination / paginationHide
rows 获取行getSelectRows/getTable().rowData
rowToggle 点击选中rowClick 内 toggleRowSelection
showRowNum 行号columnIndex
qrcode 二维码columns render QR
progress 进度条columns render el-progress
merge/mergeField 合并spanMethod
rowColor 行颜色columns cellStyle 或 rowStyle
fxColumn 固定对齐align/fixed
detailBtn 明细按钮detailOptions.buttons
order 明细序号column type index 自定义
detailSlot 明细 slotdetailContent slot
detailEvent 明细 selectcolumns onChange
detailUpload 明细上传columns edit type img/file
detailSelectChange 明细联动columns onChange 改其他列 data
detailCalc 明细计算formatter/onChange
dyEdit 动态可编辑checkEdit / beginEdit
detailFilter 明细查询searchDetailBefore

§12.H 前端 web/form 编辑表单

功能代码要点
onKeyPress 回车getFormOption onKeyPress
customValidator 自定义验证item.validator=(rule,val,callback)=>{}
customForm 多字段显示formRules 多列 + render
multipleUpload 上传type img/file + uploadBefore/After
editSelectChange 联动§12.J.4
cascaderLastSelect 级联末级cascader 配置 emitPath false
width/labelWidth/inputWidthformOptions colSize/width
cutomBtn/cutomLabel 自定义按钮标签item.extra / labelRender
tops 自定义提示item.placeholder / extra
dataCascader 级联数据源type cascader bind
right/group 左右/分组formRules 二维分组
subdetails 三级明细subDetails 左右结构
continueAdd 连续添加gridRef.continueAdd + continueAddAfter
focus 焦点editFocus / item.focus
formCalc 实时计算onChange 改其他字段
modelEvent 弹框关闭onModelClose
dateRange 日期范围min/max onInit 动态设置
hideSelectItem 隐藏下拉项onChange 过滤 item.data
hideFormItem 动态隐藏getFormOption('x').hidden=true
formReadonly 只读必填readonly/required 动态
formSelectData 弹框选§12.J.1
detailSelectData 明细弹框选§12.J.2
tree 数组转树工具函数转 cascader/tree
excelImport 导入参数importBefore append

§12.I 独立组件(非 ViewGrid 页面)

I.1 vol-form

<vol-form ref="formRef" :formFields="formFields" :formRules="formRules" :loadKey="true" />
<script setup>
const formRules = ref([[{ field:'Name', title:'名称', type:'input', required:true }]])
const submit = async () => {
  const valid = await formRef.value.validate()
  if (!valid) return
  proxy.http.post('api/...', formFields, true)
}
</script>

I.2 vol-table

标准 url: api/{表}/getPageData(框架标准,无需自定义后台)。

自定义 url(弹框选数、独立页表格): 后台 MUST §12.B.0.4 — PageDataOptions + ConvertQueryFilter<{T}>() + TakePage + return JsonNormal(new { total, rows })

<vol-table ref="tableRef" :columns="columns" url="api/{表}/SearchXxx"
  @loadBefore="loadBefore" :ck="true" :pagination="true" />
<script>
// param 即 PageDataOptions:Page, Rows, wheres, Sort, Value...
const loadBefore = (param, cb) => {
  param.wheres.push({ name: 'Enable', value: '1', displayType: 'equal' })
  cb(true)
}
tableRef.value.load(null, true)
tableRef.value.getSelected()
tableRef.value.addRow({ ... })
</script>

I.3 vol-box — 见 §12.J.11


§12.J 弹出框 / 按钮 / API(完整模板)

如何找实现: ① 本节 J.x 代码 → ② 不够则 v3.volcore.xyz(下表 URL,与离线 doc.web 同路径)。

J.x 对应关系总表

编号业务功能Skill 代码v3.volcore.xyz
J.1主表弹框选数据§12.J.1http://v3.volcore.xyz/docs/web/general/selectData.html
J.2明细弹框批量选§12.J.2http://v3.volcore.xyz/docs/web/general/selectDetailData.html
J.3selectTable§12.J.3http://v3.volcore.xyz/docs/web/general/selectTable.html
J.4select 联动§12.J.4http://v3.volcore.xyz/docs/web/form/editSelectChange.html
J.5自定义按钮§12.J.5http://v3.volcore.xyz/docs/web/general/customBtn.html
J.6查询前后端传参§12.J.6http://v3.volcore.xyz/docs/cs/service/search.html
J.7明细实时计算§12.J.7http://v3.volcore.xyz/docs/web/table/detailCalc.html
J.8gridFooter 子表§12.J.8http://v3.volcore.xyz/docs/web/general/viewDetail.html
J.9导入带参数§12.J.9http://v3.volcore.xyz/docs/web/form/excelImport.html
J.10自定义 API§12.J.10http://v3.volcore.xyz/docs/cs/dev/api.html
J.11vol-box§12.J.11http://v3.volcore.xyz/docs/web/general/box.html
J.12parentCall§12.J.12http://v3.volcore.xyz/docs/view-grid/common/com.html
J.13按钮权限§12.J.13http://v3.volcore.xyz/docs/web/general/authBtn.html

AI 查找顺序: 需求 → §12.Z → §12.J.x →(可选)上表 URL → 用户项目源码。


J.1 主表弹框选数据(嵌入 ViewGrid)

场景: 编辑表单字段旁「选择」,vol-box 内嵌另一张表的 {选择表}.vue

新建 {主表}/{主表}Select{选择表}.vue

<template>
  <vol-box :lazy="true" v-model="model" title="选择{标题}" :width="1200" :padding="0">
    <sourceGrid ref="sourceRef" />
    <template #footer>
      <el-button type="primary" @click="onSelect">确认</el-button>
    </template>
  </vol-box>
</template>
<script setup>
import sourceGrid from '@/views/{模块}/{选择表}/{选择表}.vue'
import { ref, getCurrentInstance, nextTick } from 'vue'
const emit = defineEmits(['onSelect'])
const { proxy } = getCurrentInstance()
const model = ref(false), sourceRef = ref()
const open = () => {
  model.value = true
  nextTick(() => sourceRef.value?.$refs?.grid?.search())
}
const onSelect = () => {
  const rows = sourceRef.value.$refs.grid.getSelectRows()
  if (!rows.length) return proxy.$message.error('请选择数据')
  model.value = false
  emit('onSelect', rows)
}
defineExpose({ open })
</script>

{主表}.vue

<SelectXxx ref="selectRef" @onSelect="onSelectXxx" />
<script setup lang="jsx">
import SelectXxx from './{主表}/{主表}Select{选择表}.vue'
const selectRef = ref()
const onInit = ($vm) => {
  gridRef = $vm
  const opt = gridRef.getFormOption('{字段}')
  opt.readonly = true
  opt.extra = { icon: 'el-icon-search', text: '选择', click: () => selectRef.value.open() }
}
const onSelectXxx = (rows) => {
  editFormFields.{字段A} = rows[0].{源字段A}
}
</script>

J.2 明细表弹框批量选数据

场景: 明细工具栏按钮,vol-box + vol-table 或多选嵌入页。

// onInited
gridRef.detailOptions.buttons.unshift({
  name: '选择{名称}', icon: 'el-icon-plus',
  onClick: () => selectRef.value.openDetail()
})
const onSelectDetail = (rows) => {
  const table = gridRef.getTable()
  rows.forEach(r => table.rowData.push({
    {明细字段}: r.{源字段}
  }))
}

Select 组件内: vol-table + url + loadBefore 设 wheres,确认时 getSelected() → emit。完整结构同 J.1,第二段 vol-box 见框架 SelectData.vueopenDetail 模式。


J.3 selectTable(字段下拉搜 table 选一条)

后台: 优先自定义接口(§12.B.0.4),ConvertQueryFilter<{实体}> + TakePageSys_User 换为实际表实体。

const onInit = ($vm) => {
  gridRef = $vm
  const item = gridRef.getFormOption('{字段}')
  item.type = 'selectTable'
  // 推荐自定义 Search 接口,不用 getPageData(权限/字段更可控)
  item.url = 'api/{当前表}/Search{实体}'
  item.columns = [
    { field: '{列1}', title: '列1', width: 100, search: true }, // search:true 才参与 wheres 过滤
    { field: '{列2}', title: '列2', width: 120 },
  ]
  item.onSelect = (rows) => {
    editFormFields.{字段} = rows[0].{源字段}
  }
  item.loadBefore = (param, callback) => {
    // 方式1:输入框关键字 → 后台用 options.Value 或 wheres 处理
    param.value = editFormFields.{字段}
    // 方式2:param.wheres.push({ name, value, displayType: 'like' })
    callback(true)
  }
}
// Partial/{表}Service.cs — 查询 Sys_User 时实体为 Sys_User,其他表同理替换
public async Task<object> SearchSysUser(PageDataOptions options)
{
    if (options.Page <= 0) options.Page = 1;
    if (options.Rows <= 0) options.Rows = 30;
    var query = options.ConvertQueryFilter<Sys_User>().Where(x => x.Enable == 1);
    var total = await query.CountAsync();
    var rows = await query.OrderByDescending(x => x.User_Id)
        .TakePage(options.Page, options.Rows)
        .Select(s => new { s.User_Id, s.UserName, s.UserTrueName }).ToListAsync();
    return new { total, rows };
}
// Controller: [HttpPost("SearchSysUser")] return JsonNormal(await _service.SearchSysUser(options));

J.4 表单 select 联动

gridRef.getFormOption('{主字段}').onChange = (val) => {
  editFormFields.{从字段} = null
  proxy.http.post('api/{表}/Get{联动}', { parentId: val }, true).then(res => {
    gridRef.getFormOption('{从字段}').data = res
  })
}

J.5 自定义按钮

位置代码
查询栏gridRef.buttons.splice(3,0,{ name, icon, type:'primary', onClick })
明细栏gridRef.detailOptions.buttons.unshift({ name, onClick })
弹框gridRef.boxButtons.push({ name, onClick })
表格列columns.find(...).render = (h,{row}) => el-button link ...
隐藏系统按钮gridRef.buttons.find(x=>x.name==='新建').hidden = true

J.6 前后端传参

// 前端 searchBefore
const searchBefore = (param) => {
  param.value = { status: 1, warehouseId: 'xxx' }
  param.wheres.push({ name: 'OrderDate', value: '2024-01-01', displayType: 'thanorequal' })
  return true
}
// 后端 GetPageData 内
object extra = options.Value;
QueryRelativeExpression = q => q.Where(x => x.Status == 1);

J.7 明细实时计算

const onInited = () => {
  gridRef.detailOptions.columns.forEach(c => {
    if (c.field === 'Amount') {
      c.formatter = (row) => {
        row.Amount = (row.Qty || 0) * (row.Price || 0)
        return row.Amount
      }
    }
  })
}

J.8 列表页 gridFooter 展示子表

<view-grid ... :rowClick="rowClick">
  <template #gridFooter><DetailPanel ref="footerRef" /></template>
</view-grid>
<script>
const rowClick = ({ row }) => footerRef.value.load(row.{主键})
</script>

J.9 导入带自定义参数

const importBefore = (formData, callback) => {
  formData.append('warehouseId', editFormFields.WarehouseId)
  return true
}

J.10 自定义 API 三件套

JSON: Partial Controller MUST return JsonNormal(...) 返回业务数据(列表/实体/ { total, rows }),保持与实体属性、前端 columns.field 相同大小写。勿用 return Json(...)
数据库: 默认 EF + repository(§12.B.0);禁止默认 EFSql/Dapper。

A. 非分页 / 下拉 / 联动(Dictionary 或简单参数):

// Partial/I{表}Service.cs
Task<WebResponseContent> GetItems(string keyword);
// Partial/{表}Service.cs
public Task<WebResponseContent> GetItems(string kw) {
  var data = repository.FindAsIQueryable(x => x.Name.Contains(kw)).Take(100).ToList();
  return Task.FromResult(new WebResponseContent().OK(null, data));
}
// Partial/{表}Controller.cs
[HttpPost("GetItems")]
public async Task<IActionResult> GetItems([FromBody] Dictionary<string,object> d)
{
    var res = await _service.GetItems(d["keyword"]?.ToString() ?? "");
    return JsonNormal(new { status = res.Status, message = res.Message, data = res.Data });
}

B. vol-table / selectTable 分页(PageDataOptions + ConvertQueryFilter + TakePage):

// Partial/I{表}Service.cs
Task<object> SearchForTable(PageDataOptions options);

// Partial/{表}Service.cs
public async Task<object> SearchForTable(PageDataOptions options)
{
    if (options.Page <= 0) options.Page = 1;
    if (options.Rows <= 0) options.Rows = 30;

    // {T} = 实际查询实体;前端 wheres 的 name 对应实体属性名
    var query = options.ConvertQueryFilter<{T}>().Where(x => x.Enable == 1);
    var total = await query.CountAsync();
    var rows = await query.OrderByDescending(x => x.{主键})
        .TakePage(options.Page, options.Rows)
        .Select(s => new { s.{字段1}, s.{字段2} }).ToListAsync();
    return new { total, rows };
}

// Partial/{表}Controller.cs
[HttpPost("SearchForTable")]
public async Task<IActionResult> SearchForTable([FromBody] PageDataOptions options)
{
    return JsonNormal(await _service.SearchForTable(options));
}
proxy.http.post('api/{表}/GetItems', { keyword: 'xx' }, true)

J.11 vol-box 通用弹窗

<vol-box v-model="visible" title="标题" :width="900" :height="500" :lazy="true" :padding="10">
  <!-- 内容 -->
  <template #footer>
    <el-button type="primary" @click="ok">确定</el-button>
    <el-button @click="visible = false">取消</el-button>
  </template>
</vol-box>

J.12 子组件 parentCall 通信

// 子组件内调用父 ViewGrid
proxy.$emit('parentCall', ($parent) => {
  $parent.search()
  $parent.getSelectRows()
})
// 父组件:proxy.$refs.gridFooter / gridHeader / modelBody

J.13 按钮权限

const onInit = ($vm) => {
  gridRef = $vm
  if (gridRef.filterPermission(gridRef.table.name, 'Add')) {
    gridRef.buttons.splice(1, 0, { name: '扩展', onClick: () => {} })
  }
}

§12.K 新页面编辑(Edit.vue + vol-edit)

生效条件: 代码生成器 编辑模式 = 新页面编辑
文档: http://v3.volcore.xyz/docs/edit/
与标准页区别: 列表仍在 {表}.vue(view-grid);新建/编辑跳转到 Edit.vue 独立路由页,不用弹框 modelOpen*

K.1 文件与路由

views/{类库}/{文件夹}/{表}/
├── {表}.vue          ← 列表页(view-grid,点新建/编辑 router 跳转)
└── Edit.vue          ← ★ 新页面编辑业务 Hook 写这里
  • 路由 query.id 有值 = 编辑;无值 = 新建
  • defineOptions({ name: "{表}_edit" }) 须与路由 name 一致
  • 仍从 ./options.jseditFormFieldsdetail 等;动态改配置在 Edit.vue 的 onInit,勿手改 options.js

K.2 最小结构

<template>
  <vol-edit
    ref="edit"
    :keyField="key"
    :tableName="tableName"
    :tableCNName="tableCNName"
    :formFields="editFormFields"
    :formOptions="editFormOptions"
    :detail="detail"
    :details="details"
    :onInit="onInit"
    :loadFormBefore="loadFormBefore"
    :addBefore="addBefore"
    :updateBefore="updateBefore"
    :loadTableBefore="loadTableBefore"
  />
</template>
<script setup lang="jsx">
defineOptions({ name: '{表}_edit' })
import editOptions from './options.js'
import { ref, reactive, getCurrentInstance } from 'vue'
import { useRoute } from 'vue-router'

const { proxy } = getCurrentInstance()
const route = useRoute()
const isAdd = !route.query.id
const edit = ref(null)
const { key, tableName, tableCNName, editFormFields, editFormOptions, detail, details } =
  reactive(editOptions())

const onInit = async ($vm) => {
  // 改按钮、明细列、分组 group 等(同 view-grid onInit 思路)
}

const loadFormBefore = (params, callback) => {
  // 拉主表行前;callback(false) 中止
  callback(true)
}

const addBefore = async (formData, _cb, isCopyClick) => {
  // 新建保存前;return false 中止
  return true
}

const updateBefore = async (formData) => {
  return true
}

const loadTableBefore = (params, callback, table, item) => {
  callback(true)
}
</script>

K.3 Hook 对照(Edit.vue vs view-grid)

场景view-grid {表}.vue新页面 Edit.vue
初始化onInit / onInited同左
加载主表searchBeforeloadFormBefore / loadFormAfter / searchBefore
保存总闸saveConfirmsubmitBefore
新建/修改保存addBefore / updateBefore同左(含 Async 并行版)
明细加载searchDetailBeforeloadTableBefore + searchDetailBefore
打开弹框默认值modelOpenAftermodelOpenAfter(无列表弹框时语义为数据就绪后)
列表查询searchBefore{表}.vue 列表页处理

ref 常用方法: edit.value.search() 刷新主从、edit.value.getTable() 操作明细表、edit.value.save() 主动保存。

K.4 后端

新页面编辑 仍调用同一套 api/{表}/getPageData|Add|Update|Del|Audit…;数据库前/后处理仍在 Partial/{表}Service.cs(§12.A),与前端模式无关。

K.5 在线文档

内容URL
新页面编辑总览http://v3.volcore.xyz/docs/edit/
各 Hook 说明同上 README 内 Methods 表

§12.Z 全量操作索引(开发者描述 → 章节)

AI:收到需求后先在本表检索关键词,再打开对应章节代码。

Z.1 后台 Service(ApplicationServiceBase Func)

开发者说法章节 / Func
Func 怎么写 / base.Add 之前赋值§12.A.0
主表查询/过滤/合计/SQL§12.A.1 · QueryRelativeList / QueryRelativeExpression
查询结果再加工GetPageDataOnExecuted
明细表查询/明细合计§12.A.2
OR 查询§12.A.3
新建/AddOnExecute/保存前/保存后/进流程§12.A.4 · AddOnExecute / AddOnExecuting / AddOnExecuted
修改/UpdateOnExecute/明细增删改§12.A.5 · UpdateOnExecuting / UpdateOnExecuted
删除/删除前校验§12.A.6 · DelOnExecuting / DelOnExecuted
审批/流程节点/审批后§12.A.7
反审/回滚§12.A.8
导入/导出/模板/Excel§12.A.9
文件上传§12.A.10
打印§12.A.11
自定义后台接口§12.A.12 / §12.J.10
数据库 EF 增删改查§12.B.0 · repository LINQ
分页 / TakePage§12.B.0.3
vol-table 自定义查询 / ConvertQueryFilter§12.B.0.4 · §12.J.10 B · §12.I.2
selectTable 下拉搜表§12.J.3 + §12.B.0.4

Z.1b 新页面编辑(Edit.vue)

开发者说法章节
新页面编辑 / 独立页新建编辑 / vol-edit§12.K
Edit 页保存前校验§12.K.3 addBefore / updateBefore / submitBefore
Edit 页加载主表前过滤§12.K.3 loadFormBefore
Edit 页明细加载§12.K.3 loadTableBefore / searchDetailBefore
Edit 页自定义按钮/插槽§12.K.2 + docs/edit slots

Z.2 后台 cs/dev 常用

数据库EF§12.B.0 | TakePage§12.B.0.3 | ConvertQueryFilter§12.B.0.4 | vol-table分页§12.B.0.4/J.10 | 调试§12.B.1 | 配置文件§12.B.1 | API参数§12.B.1 | 大小写§12.B.1 | JsonNormal§12.B.1/J.10 · case.md | 免登录§12.B.1 | 重写权限§12.B.1 | 序列化§12.B.1 | 字典§12.B.1 | EF增删改§12.B.0.5 | EF事务§12.B.0.6 | 多表Include§12.B.0 | EFsql非默认 | Dapper非默认§12.B.1 | HttpContext§12.B.1 | 当前用户§12.B.1 | 权限§12.B.1 | 单据号§12.B.1 | 定时任务§12.B.1 | DI§12.B.1 | Repository§12.B.0.1 | 视图代码§12.B.1 | 缓存§12.B.1 | 校验§12.B.1 | 日志§12.B.1 | 租户§12.B.1 | BulkInsert§12.B.1 | 工具§12.B.1

Z.3 ViewGrid Hook(按生命周期)

初始化 onInit/onInited/dicInited§12.C | 查询 searchBefore/After/searchDetail*§12.C | 弹框 modelOpen*/onModelClose§12.C | 保存 saveConfirm/add*/update*/submit*§12.C | 删除 del*§12.C | 审批 audit*/flow*/cancel*/anti*§12.C | 导入导出 import*/export*/getFileName§12.C | 表格 row*/selectable/beginEdit/span/sort§12.C | 明细 detail*§12.C | 打印 print*§12.C | 复制 copyDataBefore§12.C | 连续添加 continueAddAfter§12.C | 全屏 fullscreen§12.C

Z.4 ViewGrid 公共方法与属性

search/refresh/getSelectRows/getTable/getFormOption/add/edit/del§12.D.1 | tableV2/single/continueAdd/dyScript§12.D.2 | slots§12.D.3 | 主从/一对多/三级§12.D.4

Z.5 字段事件

表单 onKeyPress/onChange/blur/focus§12.E.1 | 查询表单§12.E.2 | 明细 columns§12.E.3

Z.6 general 通用

HTTP/下载§12.F | 自定义/隐藏按钮§12.F/J.5 | 权限§12.F/J.13 | 多 vol-box§12.F | 选主表/选明细§12.J.1/J.2 | selectTable§12.J.3 | 列表下明细§12.J.8 | tabs/多开§12.F | saveConfirm§12.C | openEdit§12.F | 缓存§12.F | tableV2§12.F | slot§12.D.3

Z.7 web/table 全部

自定义按钮 btn§12.G | 动态查询 dySearch§12.G | 下拉按钮 dropBtn§12.G | 排序 sort§12.G | 禁止勾选 ck§12.G | 固定列 fixed§12.G | 显示全部查询 showFilter§12.G | 查询默认值 defaultVal§12.G | 单日期 singleDate§12.G | 卡片 card§12.G | 查询 select 联动 searchSelectChange§12.G | 移除快捷 removeQuery§12.G | 多快捷 queryFields§12.G | 自定义单元格 customCell§12.G | 格式化 formatter§12.G | 单元格事件 cellEvent§12.G | 行点击 rowClick§12.G | 超出提示 tips/customTips§12.G | 树表 treeTable§12.G | 表头筛选 headFilter§12.G | 表头事件 headEvent§12.G | 颜色 tagColor/rowColor§12.G | 单选 switch§12.G | 在线编辑 editTable§12.G | tableSwitch§12.G | 多级表头 head§12.G | 合计 summary§12.G | 隐藏列 hideColumn§12.G | 分页 page/hidePage§12.G | 获取行 rows§12.G | 点击选中 rowToggle§12.G | 行号 showRowNum§12.G | 二维码 qrcode§12.G | 进度 progress§12.G | 合并 merge/mergeField§12.G | 对齐 fxColumn§12.G | 明细按钮 detailBtn§12.G | 明细序号 order§12.G | 明细 slot detailSlot§12.G | 明细事件 detailEvent§12.G | 明细上传 detailUpload§12.G | 明细联动 detailSelectChange§12.G | 明细计算 detailCalc§12.G | 动态编辑 dyEdit§12.G | 明细条件 detailFilter§12.G

Z.8 web/form 全部

回车 onKeyPress§12.H | 验证 customValidator§12.H | 自定义显示 customForm§12.H | 上传 multipleUpload§12.H | select 联动 editSelectChange§12.H | 级联末级 cascaderLastSelect§12.H | 宽度 width/labelWidth/inputWidth§12.H | 自定义按钮标签 cutomBtn/cutomLabel§12.H | 提示 tops§12.H | 级联 dataCascader§12.H | 左右/分组 right/group§12.H | 三级明细 subdetails§12.H | 连续添加 continueAdd§12.H | 焦点 focus§12.H | 计算 formCalc§12.H | 弹框关闭 modelEvent§12.H | 日期范围 dateRange§12.H | 隐藏选项 hideSelectItem§12.H | 隐藏字段 hideFormItem§12.H | 只读 formReadonly§12.H | 弹框选 formSelectData/detailSelectData§12.J | 树 tree§12.H | 导入 excelImport§12.H

Z.9 独立组件

vol-form validate/reset§12.I.1 | vol-table load/编辑/选择§12.I.2 | vol-box§12.I.3

Z.10 ViewGrid 文档 methods 文件名索引

onInit§12.C | onInited§12.C | searchBefore§12.C | searchAfter§12.C | searchDetailBefore/After§12.C | searchSubDetailBefore/After§12.C | modelOpenBefore/Async/After§12.C | addBefore/Async/After§12.C | updateBefore/Async/After§12.C | delBefore/Async/After§12.C | delRowBefore/After§12.C | saveConfirm§12.C | auditBefore/After§12.C | importBefore/After§12.C | importDetailBefore/After/Batch§12.C | exportBefore/After§12.C | printBefore/printModelClose§12.C | rowClick/rowDbClick§12.C | selectionChange/rowChange§12.C | beginEdit/endEditBefore§12.C | spanMethod/detailSpanMethod§12.C | sortEnd/detailSortEnd§12.C | detailAddRowBefore§12.C | detailTabsClick§12.C | flowLoadAfter§12.C | dicInited§12.C | onModelClose§12.C | continueAddAfter§12.C | copyDataBefore§12.C | getDelMessage/getFileName§12.C | checkEdit§12.C | tableAddRowBefore§12.C | resetSearchFormAfter§12.C | tabClick§12.C | selectable§12.C | auditModelOpenBefore§12.C | boxAuditOptionOpenBefore§12.C | getAuditTable§12.C

Z.11 ViewGrid common 索引

search§12.D.1 | refresh§12.D.1 | getSelectRows/getRowData§12.D.1 | getDetailRowData/detailRowData§12.D.1 | searchDetail§12.D.1 | add/edit/del§12.D.1 | clearSelection/toggleRowSelection§12.D.1 | getFormOption/searchParameters§12.D.1 | setFixedSearchForm§12.D.1 | initDicKeys§12.D.1 | updateSummary/updateDetailSummary§12.D.1 | tableRef§12.D.1 | editRowIndex§12.D.1 | com/parentCall§12.J.12 | dyScript§12.D.2

Z.12 ViewGrid event 索引

forminput/formfocus/formblur/formchange/formcalc§12.E | detailforminput/detailformblur/detailformchange/detailformcalc§12.E


§12 END — 与 §四–§七 互补:§四–§七 为能力总览,§十二 为可执行代码与全量索引。

十三、v3.volcore.xyz 在线文档(无本地 doc.web 时的官方补充)

与离线 doc.web/docs/ 同路径,仅域名不同。AI 在 Skill 模板不够时 SHOULD 访问 下列页面。

13.1 文档入口与四分法

分区性质URL
后台 cs/serviceService 数据库操作前/后(Partial Service 钩子)http://v3.volcore.xyz/docs/cs/service/guid.html
后台 cs/dev跨表基础设施(API、EF、用户、权限…)http://v3.volcore.xyz/docs/cs/dev/
view-grid生成页容器 + Hook + 内嵌 form/table 事件http://v3.volcore.xyz/docs/view-grid/
view-grid/componentsvol-form/vol-table 依赖关系与事件三层模型http://v3.volcore.xyz/docs/view-grid/components.html
web常用功能 示例(非 view-grid API 手册)http://v3.volcore.xyz/docs/web/
edit新页面编辑 Edit.vue / vol-edit Hookhttp://v3.volcore.xyz/docs/edit/
vol-form / vol-table / vol-box独立组件 APIhttp://v3.volcore.xyz/docs/form/ 等
其他URL
站点首页http://v3.volcore.xyz/
后台开发总览http://v3.volcore.xyz/docs/cs/

URL 拼接: http://v3.volcore.xyz/docs/ + {doc.web 相对路径},扩展名一般为 .html

AI 查文档顺序: 判断需求类型(见 §文档体系)→ Skill §十二 → 上表对应分区。

13.2 后台 Service 业务(§12.A / ApplicationServiceBase Func 对照)

业务v3.volcore.xyz
Func 机制总览(必读)http://v3.volcore.xyz/docs/cs/service/guid.html
JsonNormal 返回原大小写http://v3.volcore.xyz/docs/cs/dev/case.html
主表查询/合计/SQLhttp://v3.volcore.xyz/docs/cs/service/search.html
新建http://v3.volcore.xyz/docs/cs/service/add.html
修改http://v3.volcore.xyz/docs/cs/service/update.html
删除http://v3.volcore.xyz/docs/cs/service/del.html
审批http://v3.volcore.xyz/docs/cs/service/audit.html
反审http://v3.volcore.xyz/docs/cs/service/antiAudit.html
导入/模板http://v3.volcore.xyz/docs/cs/service/importData.html
导出http://v3.volcore.xyz/docs/cs/service/exportData.html
上传http://v3.volcore.xyz/docs/cs/service/upload.html
明细查询/合计http://v3.volcore.xyz/docs/cs/service/detail.html
OR 查询http://v3.volcore.xyz/docs/cs/service/or.html
流程信息http://v3.volcore.xyz/docs/cs/service/flow.html

13.3 ViewGrid 生成页(§12.C/D/E 对照)

类型URL
组件关系(vol-form/vol-table)http://v3.volcore.xyz/docs/view-grid/components.html
属性 propertieshttp://v3.volcore.xyz/docs/view-grid/properties.html
插槽 slotshttp://v3.volcore.xyz/docs/view-grid/slots.html
主从/一对多http://v3.volcore.xyz/docs/view-grid/detailTable.html
方法目录http://v3.volcore.xyz/docs/view-grid/ops.html
单方法http://v3.volcore.xyz/docs/view-grid/methods/{方法名}.html 如 searchBefore、addBefore
公共方法 commonhttp://v3.volcore.xyz/docs/view-grid/common/{方法名}.html
字段事件 eventhttp://v3.volcore.xyz/docs/view-grid/event/{事件名}.html

13.4 前端 web 示例(§12.F/G/H 对照)

类型URL
general 目录http://v3.volcore.xyz/docs/web/general/
table 目录http://v3.volcore.xyz/docs/web/table/
form 目录http://v3.volcore.xyz/docs/web/form/
单篇http://v3.volcore.xyz/docs/web/{general|table|form}/{文件名}.html

13.4b 新页面编辑(§12.K 对照)

类型URL
edit 总览与 Hook 表http://v3.volcore.xyz/docs/edit/

13.5 后台常用 dev(§12.B 对照)

类型URL
dev 目录http://v3.volcore.xyz/docs/cs/dev/
API 参数http://v3.volcore.xyz/docs/cs/dev/api.html
重写权限http://v3.volcore.xyz/docs/cs/dev/interfaceauth.html
EF/数据库(默认必用)http://v3.volcore.xyz/docs/cs/dev/db.html
EF 原生 SQL(非默认,勿优先)http://v3.volcore.xyz/docs/cs/dev/db/EFsql.html
selectTable 自定义查询http://v3.volcore.xyz/docs/web/general/selectTable.html
当前用户http://v3.volcore.xyz/docs/cs/dev/user.html

13.6 本 Skill 不覆盖

配置类任务(生成器操作、菜单、流程设计器、部署等)→ 不在本 Skill 范围;表级业务仍用 §12。

AI 勿混淆:

  • web/ = 效果示例,Hook 签名以 view-grid/edit 为准
  • view-grid/ = 生成页;字段事件见 components + event/
  • .jsx = MUST NOT 写业务

VolPro 业务逻辑 AI 开发技能 · 文档四分法(cs / view-grid / web / edit)· Skill + v3.volcore.xyz 双源 ·

Last Updated 2026/6/15 15:45:33
ON THIS PAGE