EdonSoft Development Framework

Access 开发框架帮助中心

当前版本v202610.01
更新日期2026-10-09
适用对象用户 / 开发者

快速开始

系统要求

操作系统Windows 7 / 10 / 11
Access 版本2010 / 2013 / 2016 / 2019 / 2021 / Microsoft 365
Office 位数32 位 ↔ 32 位,64 位 ↔ 64 位
后端数据库Access .accdb,专业版支持 SQL Server / MySQL
多用户网络建议稳定局域网共享后端

首次部署

  1. 打开 Access,进入“文件 → 账户 → 关于 Access”,确认 Office 位数。
  2. 下载对应位数版本,避免 32/64 位混用。
  3. 准备前端 .accdb / .accde、后端 Data.accdb、assets 与 Theme 目录。
  4. 首次打开前端时,如出现安全警告,请点击“启用内容”。
  5. 如提示找不到后端数据,请进入链接表设置重新选择 Data.accdb。
MyProject\
  MyProject.accdb
  Data.accdb
  assets\
  Theme\
初始账号:admin / admin。首次登录后请立即修改管理员密码。

日常使用

左侧菜单由数据库表驱动,只显示当前用户有权限的功能。
顶部栏系统名称、版本、当前用户、头像、消息、退出。
主工作区用于打开业务窗体、列表、报表或查询。

账号与头像

在登录界面输入用户名密码或按 Enter 登录。修改密码需输入当前密码、新密码并再次确认,两次输入必须一致。

头像默认存放于 assets/images/user/{UserName}.ext。未上传头像时使用 default.jpg。

版本更新

v202610.01 2026-10-09 当前 AccessTreeView、AccessDataTable、邮件中心和代码生成优化

新增

  • 新增 AccessTreeView(专业版):按父子关系展示树形数据,支持节点展开、选择、键值返回及节点新增、修改、删除。
  • 新增 AccessDataTable(专业版):将 Access 表或查询展示为 Web 数据表格,支持排序、分页及选中记录键值返回。
  • 新增 邮件中心:支持邮件账户配置、SMTP 发送、POP3 收取、草稿、附件、回复、回复全部、转发、搜索和邮件状态管理。

改进

  • 邮件工具栏使用嵌入式抗锯齿 PNG 图标,减少对系统图标字体的依赖。
  • 主子表编辑窗体采用普通字段双列、长文本独占整行的布局,精简独立标题和留白,明细区随窗体尺寸伸缩。
  • 生成代码采用 strSQL、rstRecordset、cnnConnection 等类型前缀命名,配置常量不再使用纯大写缩写,ADO 参数与游标使用具名枚举。
  • 生成列表增加刷新按钮;单表与主子表保存后刷新实际列表,支持自定义主窗体名及嵌套窗体定位,主子表仍在同一连接事务中保存。
  • 邮件账户按框架用户隔离,授权码使用 Windows DPAPI 加密保存;邮件、草稿及附件保存在当前电脑的本地目录。

修复

  • 调整邮件中心权限检查、本地邮件库查询及账户保存逻辑;编辑账户时授权码留空可保留已保存的授权码。
  • 修复生成模板头被误替换的问题,统一输出 Option Compare Database。
布局和模板更新需要重新生成窗体才能生效,请先备份手工修改过的对象。邮件本地库不会自动跨电脑同步,DPAPI 加密的授权码也不能直接在另一 Windows 用户环境中使用。
v202607.01 2026-07-14 历史 主页仪表盘、搜索组合框、授权安全、链接容错与交互优化

新增

  • 新增 主页仪表盘:支持 KPI 卡片、柱状图、折线图、面积图、堆叠柱状图、饼图、环形图、指标面积图和汇总饼图,可渲染到 Access WebBrowser 控件。
  • 新增 搜索组合框:通过 CreateSearchCombo 为普通 Access 组合框增加输入即筛选的模糊搜索能力。
  • 正式启用 RSA-SHA256 注册码签名:网站端私钥签发,Access 客户端只内置公钥验签,提升授权安全边界。
  • 链接表管理器和数据库升迁向导增加表列表 全选 / 全不选 操作,批量处理更方便。

改进

  • 数据库配置和登录流程支持非关键链接表容错:普通业务表或 USys* 链接缺失时仅提示,关键 TS_ 系统表缺失才阻断登录。
  • 单据选择弹窗增加多层专业版授权复核,公开入口、窗体加载、搜索、清除、确认和最终写入边界都会重新检查授权。
  • 菜单设置和权限设置列表增加下级菜单缩进显示,父子层级更容易识别。
  • 代码生成器输出进一步精简,减少重复事件过程,中文内容直接输出为可读字符。
  • 主页仪表盘未授权时不再弹出单独提示框,改为在白色 WebBrowser 页面内显示专业版提示。

修复

  • 修复部分链接表不存在时配置数据库或登录被过早阻断的问题。
  • 修复主界面 WebBrowser 菜单在资源路径、FontAwesome 字体和脚本加载方面的兼容问题。
  • 移除不再需要的菜单建表、建窗体代码,避免运行时误重建已有对象。
v202606.01 2026-06-19 历史 后端连接、链接表管理、数据库升迁、待办提醒与单据选择

新增

  • 新增 后端连接抽象:分别支持 Access、SQL Server、MySQL 的 ADO 与 ODBC 链接字符串生成和连接测试。
  • 新增 链接表管理器:可测试后端连接、读取远程对象并批量重新链接表。
  • 新增 数据库升迁向导:支持将 Access 后端表结构和数据迁移到 SQL Server,并自动处理主键、索引和链接表刷新。
  • 新增 提醒 / 待办中心:业务模块可投递提醒,顶部栏显示未读数量,用户可从提醒跳转到对应业务窗体。
  • 新增 单据选择弹窗:用于从子窗体或业务窗体中选择来源单据并回填数据。

改进

  • 初始化设置支持从 appsettings.json 读取和同步后端配置,兼容旧版本窗体属性保存方式。
  • 后端类型模型扩展为 Access、SQL Server、MySQL,并支持 MySQL 端口和 ODBC 驱动名配置。
  • ADO 通用执行模块可根据当前后端类型生成默认连接字符串,便于业务代码连接 SQL Server 或 MySQL。
  • 主界面顶部栏增加待办未读数刷新,用户关闭待办窗口后可自动更新徽标。

修复

  • 修复重新生成设置窗体后部分数据库属性为空导致初始化异常的问题。
  • 修复链接表切换后端类型时直接刷新链接可能失败的问题,改为必要时使用临时链接表重建本地链接。
  • 修复部分中文配置在跨模块校验时可能不一致的问题。
v202605.01 2026-05-13 历史 代码生成器全面升级、ADO 通用模块、密码哈希

新增

  • 全新升级 代码生成器:支持单表 / 主子表自动生成,可视化勾选按钮、列表字段、编辑字段、必填字段。
  • 代码生成器增加 自动编号 配置:前缀、日期格式、流水位数一次设置完成。
  • 新增 ADO 通用执行模块:ADOExecuteSQL / ADOExecuteScalar / ADOOpenRecordset,统一支持 SQL Server。
  • 新增 存储过程执行系列:ADOExecuteProcedure / ADOExecuteProcedureScalar / ADOOpenProcedureSnapshot。
  • 新增 参数包装函数:ADOParams / ADOInputParam / ADOOutputParam。
  • 新增 密码安全函数:HashPassword / VerifyPassword / NeedsPasswordRehash。

改进

  • 统一按钮样式 StyleButton 增加多种 outline 风格。
  • 主界面菜单样式
  • 参数设置增加菜单的字体

修复

  • 数据库配置已知问题
  • 修正主子表代码生成器在非自增主键时按钮事件挂载失败。
  • 修正登录时自动引用edonsoftCore问题
  • 修正第一次登录数据库配置的问题
  • 修正测试版本中已知的一些问题

代码生成

代码生成器用于根据数据表快速生成 F_ 风格业务窗体套件,适合搭建标准的单表或主子表业务模块。

打开代码生成器

在框架主界面进入开发或系统工具相关菜单,打开代码生成器。生成前建议先确认数据表结构、主键字段和命名规则,避免后续重复调整。

支持生成内容

内容 说明
主窗体 业务入口,包含工具栏和列表子窗体
列表窗体 Datasheet 方式展示数据
编辑窗体 新增和编辑记录
主子表窗体 支持主表和明细表组合维护
菜单项 可选,生成后自动写入菜单表

生成流程

  1. 选择主表。
  2. 选择窗体类型:单表或主子表。
  3. 确认主键字段。
  4. 确认模块名和窗体名称。
  5. 选择要生成的按钮。
  6. 选择列表字段、编辑字段和必填字段。
  7. 如需自动编号,填写编号字段、前缀、位数和日期格式。
  8. 如需菜单,勾选创建导航菜单并填写菜单文本。
  9. 点击“创建”。
生成后建议在 Access VBA 编辑器中执行“调试 → 编译”,并手动测试新增、编辑、删除、查询、导出等按钮。

布局、命名与刷新

  • 主子表编辑窗体的普通字段按双列排列,长文本独占整行;保留窗口标题,不额外增加独立大标题,明细区支持随窗体尺寸伸缩。
  • 生成代码使用类型前缀命名,例如 strSQL、rstRecordset、cnnConnection、blnTransactionStarted,配置常量采用 strTableName 等可读名称。VBA 关键字和库枚举保持原名。
  • 列表工具栏提供“刷新”按钮,重新查询实际列表子窗体,并尽量恢复原来的选中记录;刷新保留当前筛选条件,不会自动清除查询条件。
  • 单表与主子表保存后可刷新对应列表,按生成时确定的主窗体名定位,兼容自定义名称及嵌套打开方式。主子表在同一连接事务提交后刷新。
升级生成器模块不会自动修改已有窗体。重新生成前请备份手工修改过的窗体和事件代码;生成后编译 VBA,并测试新增、编辑、明细保存和刷新。

AI 助手

功能介绍

框架已内置作者开源项目 Access LLM Toolkit 的 AI 对话能力,可以在 Access 中向大模型提问、分析业务数据并查看 Markdown 回答,无需另行创建对话窗体。

  • 多模型对话:支持 DeepSeek、通义千问、文心一言、Kimi 等提供商,具体可选模型以框架内置窗体为准。
  • 上下文与系统提示词:连续对话保留上下文,通过系统提示词设定角色和回答要求。
  • 流式回答与打字机效果:可用 curl 时逐步显示回答,不具备流式条件时使用同步请求与打字机显示。
  • 数据库对象分析:选择当前数据库中的表或查询,将字段结构、记录数及最多前 30 行样例交给 AI 分析。
  • 历史会话与 Markdown:保存、加载和删除历史会话,并支持 Access 富文本与 WebBrowser 气泡对话两种显示方式。

使用前确认所选提供商的 API 地址、模型及凭据已配置,网络可访问对应服务;调用费用与限制由提供商决定。分析数据会把选定对象的结构和样例发送到所选 AI 服务,请先脱敏并确认允许发送;AI 建议与生成的 SQL 应人工核验。

打开与使用

在菜单动作或业务按钮中打开框架已内置的 AI 窗体。frmAI 使用 Access 富文本对话,frmAIWeb 使用 WebBrowser HTML 气泡对话:

DoCmd.OpenForm "frmAI", acNormal
DoCmd.OpenForm "frmAIWeb", acNormal

选择模型、按需填写系统提示词,输入问题后点击“提问”。分析当前数据库时,选择表或查询并点击数据分析按钮;参数查询、操作查询或不可访问对象不适合直接用于样例读取。

历史会话

通过内置历史窗体浏览、加载或删除已保存会话;“新对话”用于重置当前对话上下文。业务代码可使用以下入口:

ShowChatHistory
ClearHistory

ClearHistory 清除当前内存中的对话上下文,不等同于删除 tblChatHistory 中保存的会话。需要从界面开始全新对话时,使用窗体上的“新对话”按钮。

Markdown 调用

除 AI 对话外,也可用内置 Markdown 查看器显示自己的说明、分析结果或报告:

ShowMarkdown "# 分析结果" & vbCrLf & "**请复核业务数据**", "结果预览"

将 Markdown 写入业务窗体上的文本框时,使用 SetTextBoxMarkdown;只需要转换后的 HTML 字符串时,使用 MarkdownToRichText:

SetTextBoxMarkdown Me!txtResult, "**本月销售汇总**"
Debug.Print MarkdownToRichText("# 标题")

显示能力以 Access 富文本控件或所选对话模式为准,不等同于完整网页浏览器的 Markdown 支持。原开源项目介绍见 Access LLM Toolkit。

日期与年月选择

功能介绍

框架已内置作者开源的 Access DatePicker 日期及年月选择窗体,使用纯 VBA 与 Access 窗体实现,不依赖 ActiveX、WebBrowser 或外部日历库。

日期选择器提供日历网格、上月 / 下月导航及可选的小时、分钟列表;年月选择器提供年份导航与月份网格,适用于单据日期、统计月份等输入。可以模态返回选中值,也可将年月选择器作为文本框旁的非模态下拉使用。

日期时间选择

ShowDatePicker([dtDefault], [bShowTime=True]) As Variant:默认使用当前日期时间;第二个参数设为 False 时隐藏时间选择。确认时返回 Date,取消或清除时返回 Null。

Dim pickedDate As Variant
pickedDate = ShowDatePicker()
If Not IsNull(pickedDate) Then
    Debug.Print pickedDate
End If

仅选择日期,并把确认后的结果写回业务文本框:

Private Sub cmdPickDate_Click()
    Dim pickedDate As Variant
    pickedDate = ShowDatePicker(Me!txtDate.Value, False)
    If Not IsNull(pickedDate) Then
        Me!txtDate.Value = pickedDate
    End If
End Sub

默认值为空时按当前日期初始化。使用 Variant 接收结果并先判断 IsNull,可以在用户取消时保留文本框原值。

年月选择

ShowYearMonthPicker([dtDefault]) As Variant 以模态方式选择年月,确认时返回所选月份第 1 天的 Date,取消时返回 Null,不是直接返回“yyyy-mm”字符串。

Dim pickedMonth As Variant
pickedMonth = ShowYearMonthPicker(Date)
If Not IsNull(pickedMonth) Then
    Debug.Print Format(pickedMonth, "yyyy-mm")
End If

保存到日期字段时保留 Date 值,只在控件显示格式中设置 yyyy-mm;需要文本形式的年月时再调用 Format。

文本框绑定

PickYearMonthFor 打开非模态年月下拉,确认后自动写回目标控件,不以函数返回值接收所选年月。以下示例将格式化文本写入 txtMonth:

Private Sub txtMonth_Click()
    PickYearMonthFor Me!txtMonth, "yyyy-mm"
End Sub

也可以在窗体加载时自动绑定目标文本框的双击事件:

Private Sub Form_Load()
    AttachYearMonthPicker Me, "txtMonth", "yyyy-mm"
End Sub

AttachYearMonthPicker 会设置控件的 OnDblClick 表达式;如果已有双击逻辑,请在原事件中调用选择器,避免覆盖。无需编写事件过程时,可在文本框“双击”属性中填写:

=PickYearMonthForCtl("frmOrder", "txtMonth", "yyyy-mm")

将示例中的窗体、按钮和文本框名称替换为自己的对象名;需要存储 Date 值而非格式化文本时,使用上面的模态返回值方式。

AccessTreeView

功能与环境

AccessTreeView 是 Web 树控件,用于展示部门、商品分类、目录等父子层级数据。支持展开、折叠、节点选择,以及向 VBA 返回选中节点的 Key、Value、显示文本和父节点 Key。

可使用 Access 的 Edge 浏览器控件,也提供旧版 Microsoft Web Browser 控件兼容路径。Edge 方案要求当前 Access 版本实际支持该控件;不支持时,可使用 WebBrowser 方案。

部署时保留前台数据库旁的完整 assets/treeview 目录,包括页面、脚本和样式。读取、刷新与节点维护接口均检查专业版授权。

字段映射

  • SourceName:现有表或可读取的查询名称,不是 SQL 语句。
  • KeyField:节点唯一键,不可为空、重复或带首尾空格。
  • ParentField:父节点键;Null 或空字符串表示根节点,非空时必须指向存在的节点。0 不是特殊的根节点标记。
  • TextField:显示文本;ValueField:可选业务值字段,留空时使用节点 Key。

Key 与 Parent 字段的数据类型须一致,支持文本、GUID 或精确数值类型;Key、Parent、Text 必须映射到不同字段。指定 Value 时,它也须为独立字段。加载时会检查缺失父节点和循环父子关系。

接入方式

开发阶段可在前台 .accdb 的 VBA 立即窗口生成示例窗体;默认使用 Edge,第二个参数为 True 时使用 WebBrowser。强制重建会覆盖原窗体,执行前请备份。

BuildAccessTreeViewForm

' 使用旧版 WebBrowser 控件,重建示例窗体
BuildAccessTreeViewForm True, True

打开生成的 USysFrmAccessTreeView,填写数据源和字段映射后点击刷新。正式前台应包含已生成的窗体,运行时不需要执行生成器。

自行接入 Edge 控件时,以下示例假设控件名为 edgeTreeView、数据表为 T_Categories;请替换为自己的对象名,并把对应控件事件设置为“[事件过程]”。

Private Sub Form_Load()
    AccessTreeView_Load Me!edgeTreeView, _
        "T_Categories", "CategoryID", "ParentID", "CategoryName"
End Sub

Private Sub edgeTreeView_DocumentComplete(URL As Variant)
    AccessTreeView_DocumentComplete Me!edgeTreeView
End Sub

Private Sub edgeTreeView_Click()
    Debug.Print AccessTreeView_SelectedKey(Me!edgeTreeView)
    Debug.Print AccessTreeView_SelectedValue(Me!edgeTreeView)
End Sub

首次导航后由 DocumentComplete 注入数据,不要紧接 Load 就读取节点。可用 AccessTreeView_IsReady 检查页面初始化、AccessTreeView_IsLoaded 检查数据加载。生成的示例窗体包含首次加载重试;WebBrowser 方案通过窗体定时器同步选择状态,不应照搬 Edge 的 Click 事件。

节点维护

示例窗体提供新增根节点、新增子节点、保存和删除操作,对应 AccessTreeView_AddNode、AccessTreeView_UpdateNode、AccessTreeView_DeleteNode。成功写入后会刷新树。

维护接口只接受表数据源,能否写入仍取决于表的可更新性及数据库权限;查询数据源用于浏览。新增接口不支持手工写入自动编号 Key,应由业务层新增记录后刷新。修改时不能把节点移动到自身或自己的后代下,存在子节点时须先删除或移动子节点再删除父节点。

专业版授权检查不代替业务权限控制,接入业务窗体时仍应按项目规则检查操作权限。

AccessTreeView 为专业版专属功能。

AccessDataTable

功能与环境

AccessDataTable 是 Web 数据表格,将现有 Access 表或查询结果展示在 Edge 浏览器控件中,支持列排序、分页、横向滚动及单行选择。列标题优先使用字段 Caption,未设置时使用字段名;布尔值显示“是 / 否”,货币显示两位小数。

当前模块要求 Access 支持 Edge 浏览器控件,不提供旧版 WebBrowser 兼容接口。默认每页 25 条,可切换为 10、25、50 或 100 条;当前未开启内置搜索框,也没有直接编辑、删除或 Excel 导出接口。筛选条件应在来源查询中处理。

接入方式

开发阶段执行 BuildAccessDataTableForm 生成 USysFrmAccessDataTable 示例窗体,填写现有数据源名称、键字段和标题后刷新。BuildAccessDataTableForm True 会强制重建,须先备份手工改动。

也可在自己的窗体放置名为 edgeDataTable 的 Edge 控件,按以下示例接入,并设置窗体加载和控件 DocumentComplete 事件为“[事件过程]”。

Private Sub Form_Load()
    AccessDataTable_Load Me!edgeDataTable, _
        "Q_ProductList", "ProductID", "商品列表"
End Sub

Private Sub edgeDataTable_DocumentComplete(URL As Variant)
    AccessDataTable_DocumentComplete Me!edgeDataTable
End Sub

SourceName 是已存在的表或查询名称,不是 SQL 字符串。键字段留空时使用第一个字段,标题留空时使用数据源名称;建议明确指定唯一且非空的业务主键。

选中值与刷新

点击行后,通过 AccessDataTable_SelectedKey 获取所配置键字段的值。页面将选中键转换为字符串;业务层需要数值时自行校验转换。将 Edge 控件 Click 事件设为“[事件过程]”后,可这样读取:

Private Sub edgeDataTable_Click()
    Debug.Print AccessDataTable_SelectedKey(Me!edgeDataTable)
End Sub

' 页面已完成加载后重新读取数据
AccessDataTable_Refresh Me!edgeDataTable, _
    "Q_ProductList", "ProductID", "商品列表"

刷新会重新生成表格并移除行高亮,业务层应清除先前保存的选中键,待用户重新选择后再处理业务。此控件负责浏览和返回键值,不会自动把所选数据写入其他窗体。

部署与数据量

将完整 assets/datatable 目录随前台发布,包括 vendor 中的 jQuery、DataTables 脚本和样式,并保留 assets/css/bootstrap.min.css。前台正式运行时使用已生成窗体,无需调用生成器。

模块以 DAO 快照读取数据源全部字段和记录,转换为 JSON 后一次传给页面;这里的分页是浏览器端分页,不是数据库分页。数据量较大时,先通过保存的查询限制字段、条件和记录数,避免长时间加载或占用过多内存。

所展示查询应无需弹出参数输入,并返回可转换为普通值的字段;附件、多值字段或 OLE 对象应在来源查询中排除或转换。

AccessDataTable 为专业版专属功能。

邮件中心

功能说明

在 Access 中集中管理收件箱、草稿、已发送邮件和已删除邮件,支持 SMTP 发送、POP3 收取、回复、回复全部、转发、关键字搜索、已读状态及附件操作。

可从配置好的菜单打开,开发者也可调用 Mail_OpenCenter()。已有“邮件中心”菜单时检查当前框架用户的查看权限;没有菜单项时,菜单配置不是调用入口的前置条件。

账户配置

  1. 打开邮件中心的账户设置,新增或选择当前用户的邮件账户。
  2. 填写发件地址、登录名、SMTP 主机与端口、POP3 主机与端口,并启用账户。
  3. 按邮箱服务商要求启用 SMTP / POP3 服务,填写密码或专用授权码,不要将框架登录密码当作邮箱授权码。
  4. 保存后选择该账户,先进行少量邮件收取和测试发送。

配置可以先保存而暂不填写授权码,但收发前需要满足邮箱服务器的认证要求。编辑已有账户时,授权码留空会保留原来保存的密文。

收发与附件

选择账户后点击收取,使用文件夹切换邮件范围,按主题或发件人关键字搜索。撰写邮件时填写收件人、主题与正文,可添加附件、保存草稿后继续编辑,或直接发送。

选中已有邮件可回复、回复全部或转发,并可标记已读、删除或恢复邮件。收发过程中可使用取消操作;是否成功以界面提示和邮件状态为准。

存储与安全

邮件数据保存在当前电脑的 %LOCALAPPDATA%\EdonSoft\Mail\{项目标识} 中,Mail.accdb 保存账户、邮件与草稿信息,附件存放在同一项目目录的子目录中。实际路径可通过 Mail_StoreRoot() 查询。

账户及邮件按框架用户隔离;授权码使用 Windows DPAPI 加密,绑定当前 Windows 用户环境。这不代表整份邮件库都已加密,服务器地址、端口及邮件正文仍需通过 Windows 文件权限妥善保护。

本地邮件库不存放在共享业务后端,也不会自动跨电脑同步。备份时应保留整个项目邮件目录;换电脑或 Windows 用户后不要直接复用授权码密文,应重新配置凭据。

发布与维护

在引用开发版 Core 的前台 .accdb 中执行 BuildMailForms,生成邮件中心、列表、撰写和账户窗体,再编译前台 VBA。正式运行时打开已生成的窗体,不需要调用生成器。

BuildMailForms
? Mail_OpenCenter()

BuildMailCenter True 只重建邮件中心和列表;BuildMailForms True 重建整套邮件窗体。执行前请结束收发操作、关闭邮件窗体并备份手工修改过的对象。

邮件中心工具按钮使用嵌入式抗锯齿 PNG 图标,运行电脑无需额外安装图标字体。已有邮件中心可以在关闭后执行 UpdateMailCenterIcons 更新图标,不必重建整个窗体;执行前仍建议备份。

通用查询

通用查询功能用于给业务窗体快速接入统一的查询弹窗,不需要每个窗体都单独设计一套查询区域。

功能说明

通用查询适合列表窗体、主窗体中的子窗体,以及需要在基础 SQL 上动态追加查询条件的业务页面。

列表筛选按关键字、日期、状态等条件快速筛选记录。
子窗体筛选给主窗体中的列表子窗体增加统一查询入口。
复杂 RecordSource在基础 SQL 上追加查询条件和排序。
查询记忆系统可记住上一次实际执行过的查询值。
模块 作用
basCommonQuery 负责拼接查询条件、应用窗体筛选、应用记录源。
basCommonQueryDialog 负责弹出通用查询窗体、解析配置、保存历史条件。

用户看到的效果

通用查询窗体默认标题为“通用查询”,支持最多 8 行条件。

  • 留空的条件会自动忽略。
  • 多个条件默认按 AND 组合。
  • 布尔类型显示为“是 / 否”下拉框。
  • 日期类型按日期格式输入。
  • 系统会自动记住上次实际执行过的查询条件。
  • 查询窗体不存在或版本过旧时,系统可自动重建 USysFrmCommonQuery。

直接用于当前窗体或子窗体

函数 用途
CommonQuery_ShowForForm() 对当前窗体直接应用 Filter。
CommonQuery_ShowForSubform() 对当前主窗体中的子窗体应用 Filter。
CommonQuery_ShowForRecordSourceForm() 基于基础 SQL 重建当前窗体 RecordSource。
CommonQuery_ShowForRecordSourceSubform() 基于基础 SQL 重建子窗体 RecordSource。

当前窗体筛选示例:

Private Sub btnQuery_Click()
    Dim strConfig As String

    strConfig = Join(Array( _
        "关键字|ANYLIKE|FUserCode,FUserName,FRealName", _
        "状态|BOOLEAN|FEnabled", _
        "开始日期|DATEFROM|FCreateTime", _
        "结束日期|DATETO|FCreateTime" _
    ), vbCrLf)

    Call CommonQuery_ShowForForm(Me, strConfig, "用户查询")
End Sub

子窗体筛选示例:

Private Sub btnQueryDetail_Click()
    Dim strConfig As String

    strConfig = Join(Array( _
        "产品名称|TEXTCONTAINS|FProductName", _
        "最小数量|NUMBERMIN|FQty", _
        "最大数量|NUMBERMAX|FQty" _
    ), vbCrLf)

    Call CommonQuery_ShowForSubform(Me, "sfrDetail", strConfig, "明细查询")
End Sub

基于基础 SQL 重建记录源

如果列表窗体本身是较长 SQL,或需要在基础 SQL 上附加排序和查询条件,建议使用 RecordSource 模式。

Private Sub btnQuery_Click()
    Dim strBaseSql As String
    Dim strConfig As String

    strBaseSql = "SELECT FID, FOrderNo, FCustomerName, FCreateTime FROM TS_SaleOrder"
    strConfig = Join(Array( _
        "单号|TEXTCONTAINS|FOrderNo", _
        "客户|TEXTCONTAINS|FCustomerName", _
        "开始日期|DATEFROM|FCreateTime", _
        "结束日期|DATETO|FCreateTime" _
    ), vbCrLf)

    Call CommonQuery_ShowForRecordSourceForm(Me, strBaseSql, strConfig, "FID DESC", "销售订单查询")
End Sub

查询配置格式

配置字符串按“每行一个条件”编写,行与行之间用换行分隔。

简写格式:

显示名称|字段名

这种写法默认按 TEXTCONTAINS 处理,也就是模糊包含查询。

完整格式:

显示名称|查询模式|字段名|默认值

示例:

关键字|ANYLIKE|FUserCode,FUserName,FRealName
状态|BOOLEAN|FEnabled
开始日期|DATEFROM|FCreateTime
结束日期|DATETO|FCreateTime

支持的查询模式

模式 说明
TEXTCONTAINS文本包含,等价于 LIKE '*值*'。
TEXTEQUALS文本精确匹配。
ANYLIKE多字段模糊匹配,字段之间按 OR 组合。
NUMBER数字等于。
NUMBERMIN数字大于等于。
NUMBERMAX数字小于等于。
DATEFROM日期起始。
DATETO日期截止。
BOOLEAN布尔条件,界面显示为“是 / 否”。
ANYLIKE 的字段名可以写成 FCode,FName,FRemark 这种逗号分隔形式;DATETO 内部按“小于次日”处理,适合整天范围查询。

不弹窗时,直接在代码中拼条件

如果不想弹出统一查询窗体,也可以直接使用 basCommonQuery 中的条件函数和应用函数。

Private Sub btnQuickQuery_Click()
    Dim strWhere As String

    strWhere = Query_Join( _
        Query_AnyLike(Array("FUserCode", "FUserName"), Me!txtKeyword), _
        Query_NumberEquals("FDeptID", Me!cboDeptID), _
        Query_DateRange("FCreateTime", Me!txtDateFrom, Me!txtDateTo), _
        Query_BooleanEquals("FEnabled", Me!chkEnabled) _
    )

    Call Query_ApplySubformFilter(Me, "sfrList", strWhere)
End Sub
函数 用途
Query_TextContains()文本包含条件。
Query_AnyLike()多字段模糊匹配。
Query_NumberRange()数字范围。
Query_DateRange()日期范围。
Query_Join()按 AND 拼接多个条件。
Query_JoinOr()按 OR 拼接多个条件。
Query_ApplyFilter()对当前窗体应用 Filter。
Query_ApplySubformFilter()对子窗体应用 Filter。
Query_ApplyRecordSource()重建窗体记录源。
简单列表筛选优先用 CommonQuery_ShowForForm() / CommonQuery_ShowForSubform();基础 SQL 较复杂时优先用 CommonQuery_ShowForRecordSourceForm();需要完全自定义查询界面时再直接使用 Query_* 函数。

搜索组合框

搜索组合框用于给普通 Access 组合框增加输入即筛选的模糊搜索体验,适合客户、产品、供应商、项目等大数据量下拉选择。

功能说明

clsSearchCombo 通过 WithEvents 监听组合框的输入、方向键、更新和失焦事件,并在用户输入关键字时重建组合框 RowSource。

模糊搜索按指定字段生成 LIKE '*关键字*' 条件。
附加条件可传入固定 WHERE 条件限制候选数据范围。
排序支持可传入 ORDER BY 字段,让候选项稳定显示。
使用范围单一窗体、连续窗体和数据表视图中的组合框均可接入。

用户看到的效果

  • 用户在组合框中输入关键字时,下拉列表会立即缩小到匹配记录。
  • 支持中文名称、编码、拼音字段或调用方拼接出的搜索表达式。
  • 方向键和回车仍按 Access 组合框原有习惯操作。
  • 选择完成后组合框保存实际绑定列值,不改变原业务表结构。

接入方式

在窗体模块顶部声明一个 WithEvents 变量,并在 Form_Load 中调用 CreateSearchCombo。

Private WithEvents mCustomerCombo As clsSearchCombo

Private Sub Form_Load()
    Set mCustomerCombo = CreateSearchCombo( _
        Me.cboCustomer, _
        "[公司名称]", _
        "客户代码,公司名称", _
        "客户信息表", _
        "地区='华北'", _
        "公司名称")
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Set mCustomerCombo = Nothing
End Sub

注意事项

  • SearchField 可以是字段名,也可以是表达式,例如 [公司名称] & PinYin([公司名称])。
  • SelectClause 和 FromClause 不需要写 SELECT / FROM 关键字。
  • 窗体关闭时建议释放对象变量,避免事件引用仍然保留。
搜索组合框是界面增强能力,社区版和专业版均可使用。

后端部署

后端部署用于说明 Access、SQL Server、MySQL 三类后端的适用场景和连接方式。需要专业版的后端能力会在对应功能块末尾单独说明。

Access 后端

Access .accdb 是框架默认后端,适合中小型局域网应用、单机应用和快速交付项目。前端数据库通过链接表访问 Data.accdb 中的数据表。

部署方式前端放在用户本机,后端 Data.accdb 放在共享目录。
路径支持NormalizeBackendPath 可处理相对路径和当前项目目录下的后端文件。
连接字符串BuildAccessAdoConnectionString 和 BuildAccessTableConnectString 用于生成 ADO / 链接表连接字符串。
适用版本社区版和专业版均支持。

SQL Server 后端

SQL Server 后端适合多人并发、集中备份、统一权限管理和数据量较大的业务系统。框架可生成 SQL Server ADO 连接字符串和 ODBC 链接表连接字符串,供链接表管理器和 ADO 通用执行模块使用。

连接测试TestAdoConnection 可在保存配置前验证服务器、数据库和账号是否可用。
ADO 调用ADOExecuteSQL、ADOExecuteScalar、ADOOpenRecordset 可执行 SQL Server 查询和操作语句。
存储过程ADOExecuteProcedure、ADOExecuteProcedureScalar、ADOOpenProcedureSnapshot 可调用 SQL Server 存储过程。
适用版本SQL Server 完整后端支持为专业版功能。

MySQL 后端

MySQL 后端适合已有 MySQL 数据库环境、跨系统共享数据或需要使用 MySQL 统一维护数据的项目。框架支持 MySQL 服务器、端口、数据库、用户名、密码和 ODBC 驱动名配置。

驱动要求需要安装 MySQL ODBC 驱动,默认驱动名为 MySQL ODBC 8.0 Unicode Driver。
连接字符串BuildMySqlAdoConnectionString 和 BuildMySqlOdbcConnectString 用于生成 ADO / ODBC 连接字符串。
驱动安装FindMySqlOdbcInstaller 和 InstallMySqlOdbcDriverFromLocalPackage 可用于检测或安装本地驱动包。
适用版本MySQL 后端为专业版专属功能。

链接表管理器

链接表管理器用于维护前端 Access 与后端数据库之间的表链接,适合后端文件迁移、服务器地址变更、远程表重新链接等场景。

功能说明

链接表管理器把连接测试、远程对象读取、本地表名设置和链接创建集中在一个界面中,减少手工维护链接表时的路径错误和连接字符串错误。

Access 后端用于重新选择或修复 Data.accdb 链接。
SQL Server用于读取远程表或视图,并创建 ODBC 链接表。
MySQL用于读取 MySQL 表对象,并通过 MySQL ODBC 驱动创建链接表。
批量处理支持选择单个对象链接,也支持一次链接全部对象。

用户看到的效果

  • 先选择后端类型,再填写后端文件路径或服务器连接信息。
  • 点击“测试连接”后,系统会提示连接是否成功。
  • 点击“读取对象”后,列表显示可链接的数据表或视图。
  • 表列表支持全选和全不选,便于一次处理多张表。
  • 用户可修改本地链接表名称,再执行单个链接或批量链接。
  • 后端类型切换时,界面会显示对应的服务器、数据库、端口、驱动等字段。

操作流程

  1. 打开链接表管理器。
  2. 选择后端类型:Access、SQL Server 或 MySQL。
  3. 填写后端路径或服务器连接信息。
  4. 点击“测试连接”,确认账号、驱动和网络正常。
  5. 点击“读取对象”,加载可链接的数据表或视图。
  6. 使用“全选 / 全不选”快速调整需要处理的对象范围。
  7. 选择对象并确认本地表名。
  8. 点击“链接选中”或“链接全部”。
切换后端类型时,如果直接刷新链接失败,系统会在必要时使用临时链接表重建本地链接。

开发者入口

函数用途
LinkedTableMgr_Open打开链接表管理器。
LinkedTableMgr_ListRemoteObjects读取远程数据库中可链接的表或视图。
LinkedTableMgr_LinkObject按指定连接信息链接单个远程对象。
LinkedTableMgr_IsAvailable判断当前版本是否可使用对应链接能力。
Access 后端链接维护可用于社区版;SQL Server / MySQL 链接表管理为专业版功能。

数据库升迁

数据库升迁向导用于把本地 Access 后端表结构和数据迁移到 SQL Server,适合项目从单机或小型局域网部署升级到企业级数据库部署。

功能说明

升迁向导会扫描 Access 后端表结构,检查字段类型、主键和索引,再把可迁移的数据表创建到目标 SQL Server 数据库中。

结构扫描读取表、字段、主键、普通索引和唯一索引。
类型映射把 Access 字段类型转换为 SQL Server 可用字段类型。
数据迁移按表写入数据,并保留主键和索引。
链接刷新升迁后可把前端链接表切换到 SQL Server 后端。

用户看到的效果

  • 向导界面显示目标服务器、数据库、账号和连接测试按钮。
  • 表列表中显示可升迁的 Access 后端数据表。
  • 表列表支持全选和全不选,适合批量升迁或快速排除少量表。
  • 检查阶段提示字段类型、主键、索引和不适合自动迁移的字段。
  • 执行阶段显示迁移进度和结果信息。
  • 完成后可重新链接到目标 SQL Server 数据库。

升迁流程

步骤说明
测试连接输入目标服务器、数据库、账号和密码,确认 ADO 连接可用。
扫描表结构读取 Access 后端中的业务表、字段、主键和索引信息。
检查兼容性预览字段类型映射,提前发现附件、多值字段、OLE 等不建议自动迁移的字段。
执行升迁创建远程表、迁移数据、创建主键和索引。
重新链接升迁完成后可将前端链接表切换到新的 SQL Server 后端。

注意事项

  • 正式升迁前请备份前端、后端和目标数据库。
  • 附件字段、多值字段、复杂查阅字段和 OLE 对象不建议自动迁移。
  • 升迁完成后,需要在测试环境验证新增、编辑、删除、查询、导出等关键流程。
  • 开发者入口包括 Upsize_Open、Upsize_CheckTable、Upsize_MigrateTable 和 Upsize_BuildConnectionFromForm。
数据库升迁向导为专业版专属功能。

提醒与待办

提醒与待办用于把业务消息投递给指定用户或所有用户,并在主界面顶部栏显示未读数量。

功能说明

业务模块可以通过公共 API 投递提醒,提醒中可保存标题、内容、类别、目标用户、优先级、过期时间和跳转参数。

目标用户可投递给指定用户名,也可以留空表示所有用户可见。
优先级支持普通、重要、紧急等优先级。
未读数量顶部栏可显示当前用户未读提醒数量。
业务跳转提醒可保存菜单同款 FArguments,用于打开对应业务窗体。

用户看到的效果

  • 主界面顶部栏显示当前用户未读提醒数量。
  • 点击提醒入口后打开待办列表窗口。
  • 用户可查看提醒标题、内容、类别、优先级和创建时间。
  • 用户可标记单条已读、全部已读或删除提醒。
  • 带跳转参数的提醒可打开对应业务窗体。

公共 API

函数用途
AddReminder投递一条提醒,可设置标题、内容、类别、目标用户、优先级、过期时间和跳转参数。
GetUnreadCount读取当前用户未读数量,供主界面顶部栏徽标显示。
MarkReminderRead标记单条提醒为已读。
MarkAllRead将当前用户可见提醒全部标记为已读。
DeleteReminder删除指定提醒。

跳转参数

Call AddReminder( _
    "销售订单待处理", _
    "订单 SO20260619001 需要确认", _
    "销售", _
    "OpenForm|编辑|F_SaleOrder_Edit|||FID=123|||", _
    "admin", _
    1)
FArguments 与菜单表 TS_Menus.FArguments 使用同一套管道分隔格式,便于从提醒直接跳到业务窗体。

主页仪表盘

主页仪表盘用于把 KPI 指标和 ECharts 图表渲染到 Access WebBrowser 控件中,适合制作系统首页、经营看板和统计分析页。

功能说明

basDashboardConfig 是纯内存渲染引擎,不创建数据表,也不自动生成窗体。开发者自行创建窗体和 WebBrowser 控件,然后通过公共 API 添加 KPI 和图表数据。

KPI 卡片支持标题、数值、趋势、FontAwesome 图标、颜色和格式化掩码。
图表类型支持柱状图、横向柱状图、折线图、面积图、堆叠柱状图、饼图、环形图、指标面积图和汇总饼图。
数据来源可直接传数组,也可传 SQL,由框架读取分类和系列数据。
授权行为未授权时不弹 MsgBox,只在白色页面内显示专业版提示。

用户看到的效果

  • 主页中显示一组 KPI 卡片,支持数字、趋势文字和图标。
  • 下方图表区域按 12 栅格宽度排列,可混排不同图表。
  • 窗体调整大小后可调用 Dashboard_ResizeCharts 让图表自适应。
  • 未开通专业版时,WebBrowser 区域显示“主页仪表盘为专业版功能”,不会弹出单独提示框。

接入方式

先在 Access 窗体上放置一个 Microsoft Web Browser ActiveX 控件,例如 wbHome,再在窗体加载时添加数据并渲染。

Private Sub Form_Load()
    Dashboard_Reset

    Dashboard_AddKPI "订单总数", 4805, "+2.5%", "fa fa-shopping-cart", "#1e9ff2", "#,##0"

    Dashboard_AddChartFromSQL "销售趋势", "line", _
        "SELECT Format(FDate,'yyyy-mm') AS 月份, Sum(FAmount) AS 销售, Count(*) AS 单数 " & _
        "FROM T_SaleOrder GROUP BY Format(FDate,'yyyy-mm') ORDER BY Format(FDate,'yyyy-mm')", _
        "销售|单数", 8, 340

    Dashboard_Render Me!wbHome
End Sub

Private Sub Form_Resize()
    Dashboard_ResizeCharts Me!wbHome
End Sub

公共 API

函数用途
Dashboard_Reset清空当前缓存的 KPI 和图表。
Dashboard_AddKPI直接添加一个 KPI 卡片。
Dashboard_AddKPIFromSQL用 SQL 查询 KPI 数值和趋势。
Dashboard_AddChart直接传分类、系列名和二维数组添加图表。
Dashboard_AddChartFromSQL用 SQL 添加图表,第 1 列为分类,其余列为系列。
Dashboard_Render把完整仪表盘 HTML 写入 WebBrowser 控件。
Dashboard_ResizeCharts窗体尺寸变化后重绘图表。
Dashboard_GetHtml返回完整 HTML,供高级场景自行写入页面。
主页仪表盘为专业版专属功能。模块内部会在添加数据和生成 HTML 时复核授权,未授权仅显示页面内提示。

单据选择弹窗

单据选择弹窗用于在主子表业务窗体中,从来源表、查询或 SQL 中选择一行或多行记录,并把选中的字段写入目标明细子窗体。

功能说明

单据选择弹窗适合销售订单选商品、采购入库选采购明细、费用单选项目等场景。业务窗体只需要调用 BillSelect_ShowForSubform,传入来源数据、字段映射和目标明细子窗体控件名,即可完成选择和写入。

来源数据支持表名、查询名或 SELECT SQL。
多选写入列表支持多选,可一次追加多条明细。
字段映射支持 目标字段|来源字段 或 目标字段=来源字段。
发布准备运行时直接打开宿主窗体 USysFrmBillSelect。

用户看到的效果

  • 弹窗标题默认显示“选择信息”,调用方也可以指定为“选择商品”“选择销售订单”等业务标题。
  • 顶部可选择过滤字段、过滤方式并输入关键字;过滤方式支持“包含”“开头是”“等于”。
  • 列表区域显示调用方指定的字段,未指定时默认显示来源数据前 8 个字段。
  • 用户可勾选多行后点击“确定”;如果没有勾选但当前行已定位,也会写入当前行。
  • 双击当前行等同于确定;点击“取消”关闭弹窗,不写入明细。

调用入口

Call BillSelect_ShowForSubform( _
    Me, _
    "sfrDetail", _
    "SELECT FID, FProductCode, FProductName, FSpec, FUnit FROM TS_Product WHERE FEnabled=True", _
    "FProductID|FID;FProductCode|FProductCode;FProductName|FProductName;FSpec|FSpec;FUnit|FUnit", _
    "FProductCode,FProductName,FSpec,FUnit", _
    "FProductCode,FProductName,FSpec", _
    "选择商品", _
    "FQty", _
    1)
参数说明
frmCaller当前主窗体,通常传 Me。
strSubformControlName目标明细子窗体控件名,不是子窗体对象名。
strSource来源表名、查询名或 SELECT SQL。
strFieldMap字段映射,决定来源字段如何写入目标明细字段。
strDisplayFields可选,列表显示字段,逗号分隔。
strSearchFields可选,未指定过滤字段时参与搜索的字段,逗号分隔。
strDialogTitle可选,弹窗标题。
strDefaultQtyField可选,默认数量字段,默认 FQty;传空字符串可关闭默认数量写入。
dblDefaultQty可选,数量字段为空时写入的默认值,默认 1。
strTargetAppendSource可选,目标明细记录集不可更新时,指定实际写入表或可更新查询。

字段映射

字段映射支持分号或换行分隔,也兼容 | 和 = 两种写法。左侧是目标明细字段,右侧是来源字段。

FProductID|FID
FProductCode|FProductCode
FProductName|FProductName
FQty||1
  • 第 3 段可作为默认值,例如 FQty||1 表示来源中没有数量时写入 1。
  • 来源字段为空但存在默认值时,会按默认值写入。
  • 默认数量字段会在映射写入后再检查,如果该字段为空,会写入 dblDefaultQty。

查询与显示字段

strDisplayFields 控制列表中哪些字段可见;未指定时默认显示来源数据前 8 个字段。列宽会根据字段名自动估算,例如名称字段更宽、规格字段次之。

strSearchFields 控制没有指定过滤字段时参与关键字搜索的字段。弹窗加载后会把可显示字段放入过滤字段下拉框,用户也可以在界面中选择单个字段进行查询。

如果来源是复杂 SQL,关键字查询会在外层包装查询后追加 WHERE 条件;如果来源是表名或查询名,则直接按该对象生成筛选 SQL。

发布与维护

当前版本运行时只检查并打开宿主窗体 USysFrmBillSelect,不会在业务运行过程中自动调用窗体生成器。发布前请确认前端 .accdb 或 .accde 中已经包含该窗体。

BuildBillSelectForm True
  • 开发环境或发布准备阶段,可在 VBA 立即窗口执行上面的命令创建或重建宿主窗体。
  • basBillSelectFormBuilder.bas 只用于创建或重建宿主窗体,不应作为业务运行时依赖。
  • 目标明细子窗体应绑定到可新增记录源;如果不可更新,可在 BillSelect_ShowForSubform 最后一个参数指定实际写入表或可更新查询。
  • 专业版授权会在公开入口、窗体加载、查询、确认和最终写入时重复检查,避免绕过界面直接调用写入逻辑。
单据选择弹窗为专业版专属功能。

函数与接口

编号与数值

AutoNumStr按规则生成下一个编号

语法:AutoNumStr(TableName, FieldName, Digit, [Prefixal], [DateFormat])

返回:编号字符串,例如 SO202605120001。

Me.txtOrderNo = AutoNumStr("TS_SaleOrder", "FOrderNo", 4, "SO", "yyyymmdd")

按"当前最大值 + 1"生成,非严格并发安全,高并发场景需要额外防重。

RoundDown向下取值

语法:RoundDown(Number, [NumDigitsAfterDecimal = 0])

Debug.Print RoundDown(12.987, 2)  ' 12.98
RoundUp向上取值

语法:RoundUp(Number, [NumDigitsAfterDecimal = 0])

Debug.Print RoundUp(12.011, 2)  ' 12.02
RoundOff四舍五入

语法:RoundOff(Number, [NumDigitsAfterDecimal = 0])

Debug.Print RoundOff(12.555, 2)  ' 12.56

SQL 与数据读取

SQLText构造安全 SQL 字符串字面量

语法:SQLText(Expression)

返回:带单引号的 SQL 文本;空值返回 Null。

strSQL = "SELECT * FROM TS_Users WHERE FUserName=" & SQLText(Me.txtUserName)
SQLDate构造 Access SQL 日期字面量

语法:SQLDate(Expression)

返回:#2026-05-12# 形式;空值返回 Null。

strSQL = "SELECT * FROM TS_Log WHERE FLogDate>=" & SQLDate(Me.txtDateFrom)
DoRunSQL执行操作型 SQL

语法:DoRunSQL(SQLStatement)

Call DoRunSQL("UPDATE TS_Users SET FIsEnabled=0 WHERE FID=" & Nz(Me.FID, 0))

内部会临时关闭系统警告,不负责事务控制。

AdoScalar读取第一行第一列

语法:AdoScalar(strSQL, [varDefault])

lngCount = Nz(AdoScalar("SELECT Count(*) FROM TS_Users", 0), 0)
AdoExists判断查询是否有记录

语法:AdoExists(strSQL)

If AdoExists("SELECT 1 FROM TS_Users WHERE FUserName=" & SQLText(Me.txtUserName)) Then ...
AdoOpenSnapshot打开只读快照记录集

语法:AdoOpenSnapshot(strSQL)

Set rst = AdoOpenSnapshot("SELECT FUserName FROM TS_Users")
Do Until rst.EOF
    Debug.Print rst!FUserName
    rst.MoveNext
Loop
rst.Close : Set rst = Nothing
ADOExecuteSQLADODB 执行操作型 SQL

语法:ADOExecuteSQL(sql, [UseTransaction=True], [UseProjectConnection=True], [ConnectionString], [CommandTimeout=30])

返回:受影响记录数。

lngRows = ADOExecuteSQL("UPDATE TS_Users SET FOnline=0 WHERE FID=1")

适合连接 SQL Server 或需要超时控制的场景。

ADOExecuteScalarADODB 读取单个值

语法:ADOExecuteScalar(sql, [varDefault], [UseProjectConnection=True], [ConnectionString], [CommandTimeout=30])

lngCount = Nz(ADOExecuteScalar("SELECT Count(*) FROM TS_Users", 0), 0)
ADOOpenRecordsetADODB 打开断开式记录集

语法:ADOOpenRecordset(sql, [UseProjectConnection=True], [ConnectionString], [CommandTimeout=30])

Set rst = ADOOpenRecordset("SELECT FID, FUserName FROM TS_Users ORDER BY FID")
ADOExecuteProcedure执行 SQL Server 存储过程

语法:ADOExecuteProcedure(ProcedureName, [Parameters], [UseTransaction=True], [UseProjectConnection=False], [ConnectionString], [CommandTimeout=30])

Call ADOExecuteProcedure("usp_UpdateUser", ADOParams( _
    ADOInputParam("@UserID", adInteger, 1), _
    ADOInputParam("@UserName", adVarChar, "张三", 50) _
))
ADOExecuteProcedureScalar执行存储过程并取单个值

语法:ADOExecuteProcedureScalar(ProcedureName, [Parameters], [varDefault], ...)

lngCount = ADOExecuteProcedureScalar("usp_GetUserCount", ADOParams(ADOInputParam("@UserGroupID", adInteger, 1)), 0)
ADOOpenProcedureSnapshot执行存储过程并返回记录集

语法:ADOOpenProcedureSnapshot(ProcedureName, [Parameters], ...)

Set rst = ADOOpenProcedureSnapshot("usp_GetUsers", ADOParams(ADOInputParam("@UserGroupID", adInteger, 1)))
ADOParams包装多个 ADO 参数

语法:ADOParams(ParamArray Parameters())

无参数存储过程不需要写 ADOParams()。

ADOInputParam创建输入参数

语法:ADOInputParam(Name, DataType, Value, [Size=0])

ADOInputParam "@UserName", adVarChar, "张三", 50
ADOOutputParam创建输出参数

语法:ADOOutputParam(Name, DataType, [Size=0])

ADOOutputParam "@NewID", adInteger
DMerge把查询结果某字段拼接成字符串

语法:DMerge(Exps, Domain, [Criteria], [Separator=";"])

strRoles = DMerge("FRoleName", "TS_UserRole", "FUserID=" & Nz(Me.FUserID, 0), ",")

表单与记录

CheckRequired检查必填控件

语法:CheckRequired(DataForm, [RequiredMark="<Required>"])

If Not CheckRequired(Me) Then Exit Sub

把必填控件的 Tag 属性设为 <Required>。

ReadRecord把记录集当前行读取到窗体控件

语法:ReadRecord(DataForm, rst, [ReadMark="<Read>"])

Set rst = CurrentDb.OpenRecordset("SELECT * FROM TS_Users WHERE FID=" & Nz(Me.FID, 0), dbOpenSnapshot)
If Not rst.EOF Then
    Call ReadRecord(Me, rst)
End If

把需要从记录集读取的控件 Tag 设为 <Read>。

WriteRecord把窗体控件值写入记录集当前行

语法:WriteRecord(DataForm, rst, [WriteMark="<Write>"])

Set rst = CurrentDb.OpenRecordset("TS_Users", dbOpenDynaset)
rst.AddNew
Call WriteRecord(Me, rst)
rst.Update

把需要写入记录集的控件 Tag 设为 <Write>。

ClearInput清空带标记的输入控件

语法:ClearInput(DataForm, [WriteMark="<Clear>"])

Call ClearInput(Me)
Call ClearInput(Me, "<Write>")

默认清空 Tag 包含 <Clear> 的控件,也可传 <Write> 复用可写字段标记。

DeleteRecord删除当前记录

语法:DeleteRecord(DataForm, SQLArray)

Call DeleteRecord(Me, "DELETE FROM TS_Customer WHERE FID=" & Nz(Me.FID, 0))
RequerySnapshot刷新快照窗体并保留定位

语法:RequerySnapshot(DataForm, [FindWhereCondition])

Call RequerySnapshot(Me, "FID=" & Nz(Me.FID, 0))
AuditData写入审核人、审核时间和审核状态

语法:AuditData(sTable, sUser, lngID, sStatus)

strUser = Nz(AdoScalar("SELECT FValues FROM TS_ParametersLocal WHERE FItem='UserName'", ""), "")
If AuditData("TS_SaleOrder", strUser, Nz(Me.FID, 0), "已审核") Then
    ShowMsg "审核完成", vbInformation, "提示"
End If

目标表需要存在 Auditor、AuditTime、AuditStatus、id 等字段。

UpdateCreatorModifier回写创建人、修改人和修改时间

语法:UpdateCreatorModifier(strTable, strUser, clngID, IsEdit)

strUser = Nz(AdoScalar("SELECT FValues FROM TS_ParametersLocal WHERE FItem='UserName'", ""), "")
Call UpdateCreatorModifier("TS_Customer", strUser, Nz(Me.FID, 0), True)

IsEdit=True 时会写 FCreator、FModifier、FModifyTime;False 时只写创建人。

ChildFormClose按菜单配置关闭子窗体

语法:ChildFormClose(strMode, strFrm)

Private Sub btnClose_Click()
    Call ChildFormClose("客户管理", Me)
End Sub

strMode 传的是菜单文本,不是窗体名。找不到菜单配置时会直接关闭当前窗体。

WriteLog向日志表追加操作日志

语法:WriteLog(sModuleCode, sDes, sUser)

strUser = Nz(AdoScalar("SELECT FValues FROM TS_ParametersLocal WHERE FItem='UserCode'", ""), "")
Call WriteLog("F_Customer", "新增客户:" & Nz(Me.txtCustomerName, ""), strUser)

函数会直接向 Ts_Log 表写入 FModuleCode、FLogDes、FUserCode 字段。

SwitchRecord用上下方向键切换记录

语法:SwitchRecord(KeyCode, Shift)

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
    Call SwitchRecord(KeyCode, Shift)
End Sub

日期 / 对象 / 设置

FirstOfMonth取月初日期
Me.txtDateFrom = FirstOfMonth(Date)
LastOfMonth取月末日期
Me.txtDateTo = LastOfMonth(Date)
FirstOfWeek取本周第一天
Me.txtWeekFrom = FirstOfWeek(Date, vbMonday)
LastOfWeek取本周最后一天
Me.txtWeekTo = LastOfWeek(Date, vbMonday)
ObjectIsExists判断对象是否存在

语法:ObjectIsExists(ObjectType, ObjectName)

If ObjectIsExists(acForm, "F_Customer") Then DoCmd.OpenForm "F_Customer"
ObjectIsLoaded判断窗体或报表是否已打开
If ObjectIsLoaded(acForm, "USysFrmLogin") Then Forms("USysFrmLogin").Visible = True
SaveDbSetting保存数据库属性

语法:SaveDbSetting([DatabaseName], [PropertyName], DataType, Value)

Call SaveDbSetting("", "SoftwareName", DB_TEXT, "报价管理系统")
GetDbSetting读取数据库属性

语法:GetDbSetting(SettingName, DefaultValue)

Debug.Print GetDbSetting("SoftwareName", "未设置")
LoadString读取多语言字符串

语法:LoadString(Key, [FindText], [ReplaceText])

ShowMsg LoadString("MsgNoPermission"), vbExclamation, LoadString("MsgTitle_Notes")

导出 / 文件 / 系统

ExportToExcel导出数据到 Excel

语法:ExportToExcel(WorkbookName, [SheetName], [FirstRange="A1"])

Call ExportToExcel("销售订单列表", "订单")
GetSaveAsFileName弹出另存为对话框
strFile = GetSaveAsFileName("销售订单.xlsx")
GetLocalIPAddress获取本机 IP 地址
Debug.Print GetLocalIPAddress()
ShellExecute打开文件 / 目录 / 网址
Call ShellExecute("https://www.edonsoft.com")
Call ShellExecute(CurrentProject.Path & "\帮助文档.pdf")
PS_Zip压缩文件或文件夹

语法:PS_Zip(sSrc, sDest, [sCompressionLvl="Optimal"])

Call PS_Zip("C:\Temp\MonthlyStats.xlsx", "C:\Temp\MonthlyStats.zip")
PS_UnZip解压 ZIP 文件
Call PS_UnZip("C:\Temp\MonthlyStats.zip", "C:\Temp\Export")

AI 与 Markdown

ShowChatHistory打开历史会话
ShowChatHistory

浏览、加载和删除已保存的 AI 会话。AI 主窗体入口见 打开与使用。

ClearHistory清除当前对话上下文
ClearHistory

重置当前内存上下文,不删除已保存的历史会话;界面中的“新对话”按钮会一并处理当前对话显示。

ShowMarkdown打开 Markdown 内容预览

语法:ShowMarkdown(sMd, [sTitle="Markdown"])。

ShowMarkdown "**业务分析结果**", "结果预览"
SetTextBoxMarkdown将 Markdown 写入文本框

语法:SetTextBoxMarkdown(txt As TextBox, sMd)。

SetTextBoxMarkdown Me!txtResult, "**销售汇总**"

只需转换 HTML 时使用 MarkdownToRichText(sMd) As String。

日期与年月选择

ShowDatePicker选择日期及可选时间

语法:ShowDatePicker([dtDefault], [bShowTime=True]) As Variant。

pickedDate = ShowDatePicker(Date, False)

返回 Date 或 Null,使用前声明 Variant 变量;完整取消处理见 日期时间选择。

ShowYearMonthPicker模态选择年月

语法:ShowYearMonthPicker([dtDefault]) As Variant。

返回所选月份第一天的 Date,取消时返回 Null;显示年月时再使用 Format。

PickYearMonthFor年月下拉并写回控件

语法:PickYearMonthFor(ctl As Control, [sFormat=""])。

PickYearMonthFor Me!txtMonth, "yyyy-mm"

非模态调用,不应把它作为返回日期的函数使用。

AttachYearMonthPicker绑定文本框双击事件

语法:AttachYearMonthPicker(frm As Form, sCtlName, [sFormat=""])。

AttachYearMonthPicker Me, "txtMonth", "yyyy-mm"

会覆盖该控件的双击事件表达式;属性表直接绑定示例见 文本框绑定。

Web 数据控件(专业版)

AccessTreeView_Load加载树页面及字段配置

语法:AccessTreeView_Load(EdgeControl, SourceName, KeyField, ParentField, TextField, [ValueField=""])。

支持 Edge 与 WebBrowser 控件。导航完成后,在控件事件中调用 AccessTreeView_DocumentComplete(EdgeControl) 注入数据。

完整事件示例及字段要求见 AccessTreeView 接入方式。

AccessTreeView_Refresh重新读取树数据

语法:AccessTreeView_Refresh(EdgeControl, SourceName, KeyField, ParentField, TextField, [ValueField=""])。

AccessTreeView_Refresh Me!edgeTreeView, _
    "T_Categories", "CategoryID", "ParentID", "CategoryName"
AccessTreeView_SelectedKey读取选中节点 Key

语法:AccessTreeView_SelectedKey(EdgeControl) As Variant。

Debug.Print AccessTreeView_SelectedKey(Me!edgeTreeView)

先确认数据已加载且用户已选择节点;键值通过页面状态返回,不等同于完整记录。

AccessTreeView_SelectedValue读取选中节点业务值

语法:AccessTreeView_SelectedValue(EdgeControl) As Variant。

未指定 Value 字段时返回节点 Key。另有 AccessTreeView_SelectedText 和 AccessTreeView_SelectedParentKey 可读取文本及父节点键。

AccessTreeView_AddNode节点维护接口

新增:AccessTreeView_AddNode(EdgeControl, SourceName, KeyField, ParentField, TextField, ValueField, NodeKey, ParentKey, NodeText, [NodeValue])。

修改:AccessTreeView_UpdateNode 使用相同参数;删除:AccessTreeView_DeleteNode(EdgeControl, SourceName, KeyField, ParentField, TextField, ValueField, NodeKey) As Boolean。

不使用业务值字段时,ValueField 仍须传入空字符串。仅支持表数据源;自动编号新增及父子关系限制见 节点维护。

AccessDataTable_Load加载数据表格页面

语法:AccessDataTable_Load(EdgeControl, SourceName, [KeyField=""], [Caption=""])。

仅支持 Edge 控件;在导航完成事件中调用 AccessDataTable_DocumentComplete(EdgeControl) 注入数据。

数据源须是现有表或查询名称。完整示例见 AccessDataTable 接入方式。

AccessDataTable_Refresh重新读取表格数据

语法:AccessDataTable_Refresh(EdgeControl, SourceName, [KeyField=""], [Caption=""])。

AccessDataTable_Refresh Me!edgeDataTable, _
    "Q_ProductList", "ProductID", "商品列表"

页面加载完成后调用;重新读取全部结果并重建表格,不是数据库分页请求。

AccessDataTable_SelectedKey读取所选记录键值

语法:AccessDataTable_SelectedKey(EdgeControl) As Variant。

Debug.Print AccessDataTable_SelectedKey(Me!edgeDataTable)

用户点击行后读取,页面返回字符串形式的键值;不要把刷新前保存的键值当作新的选择。

邮件服务

Mail_OpenCenter打开邮件中心

语法:Mail_OpenCenter() As Boolean,成功打开时返回 True。

Call Mail_OpenCenter()

前台必须已存在 USysFrmMailCenter;开发或发布准备时使用 BuildMailForms 生成。

Mail_Receive收取指定账户邮件

语法:Mail_Receive(AccountID, [MaximumMessages=20]) As Long,返回本次保存的邮件数量。

lngReceived = Mail_Receive(lngAccountID, 20)

账户必须属于当前框架用户,并已配置可用的 POP3 服务器与认证信息。

Mail_Send发送已保存的邮件

语法:Mail_Send(MessageID) As Boolean,发送成功时返回 True。

blnSent = Mail_Send(lngMessageID)

参数是本地邮件记录 ID,不是邮件正文;使用邮件中心保存草稿或撰写流程准备记录和附件。

Mail_StoreRoot查询当前项目邮件目录

语法:Mail_StoreRoot() As String。

Debug.Print Mail_StoreRoot()

返回当前 Windows 用户本地目录,不是共享业务数据库的路径。

UI 与交互

ShowMsg统一消息提示框

语法:ShowMsg(Prompt, [Buttons=vbOKOnly], [Title], [lngSeconds])

If ShowMsg("确认删除当前记录?", vbExclamation + vbYesNo + vbDefaultButton2, "删除确认") = vbYes Then ...

支持倒计时自动关闭与字符串表。

StyleButton统一按钮样式

语法:StyleButton(btn, sStyle)

样式名:primary / secondary / add / delete / warning / info / light / dark / outline-primary / outline-secondary / outline-success / outline-danger / outline-warning / outline-info / outline-dark

StyleButton Me.btnSave, "primary"
StyleButton Me.btnDelete, "delete"
StyleButton Me.btnClose, "outline-secondary"
CreateSearchCombo为组合框启用输入即筛选

语法:CreateSearchCombo(Combo, SearchField, SelectClause, FromClause, [WhereClause], [OrderByClause])

返回:clsSearchCombo 事件对象。调用方应使用模块级 WithEvents 变量保存。

Private WithEvents mCombo As clsSearchCombo

Private Sub Form_Load()
    Set mCombo = CreateSearchCombo(Me.cboCustomer, "[公司名称]", "客户代码,公司名称", "客户信息表", , "公司名称")
End Sub

窗体卸载时建议 Set mCombo = Nothing。如果需要拼音首字母搜索,可把 SearchField 写成包含拼音函数的表达式。

BillSelect_ShowForSubform选择来源数据并追加到明细子窗体

语法:BillSelect_ShowForSubform(frmCaller, strSubformControlName, strSource, strFieldMap, [strDisplayFields], [strSearchFields], [strDialogTitle], [strDefaultQtyField], [dblDefaultQty], [strTargetAppendSource])

返回:成功写入明细时返回 True;取消或未写入时返回 False。

If BillSelect_ShowForSubform(Me, "sfrDetail", "TS_Product", "FProductID|FID", "FProductCode,FProductName", "FProductCode,FProductName", "选择商品") Then
    ShowMsg "明细已添加", vbInformation, "提示"
End If

运行时必须已存在宿主窗体 USysFrmBillSelect。开发或发布准备阶段可执行 BuildBillSelectForm True 创建或重建。

Dashboard_AddKPI添加主页仪表盘 KPI 卡片

语法:Dashboard_AddKPI(Title, Value, [Trend], [Icon], [Color], [Format])

Dashboard_AddKPI "本月销售", 125800, "+8.2%", "fa fa-money", "#0d6efd", "#,##0"

也可使用 Dashboard_AddKPIFromSQL 通过 SQL 读取数值和趋势。

Dashboard_AddChart添加主页仪表盘图表

语法:Dashboard_AddChart(Title, ChartType, Categories, SeriesNames, SeriesData, [ColSpan], [Height])

常用类型:bar / horizontalBar / line / area / stackedBar / pie / donut / metricArea / summaryPie

Dashboard_AddChartFromSQL "销售趋势", "line", strSql, "销售|单数", 8, 340

Dashboard_AddChartFromSQL 要求 SQL 第 1 列为分类,其余列为系列数据。

Dashboard_Render渲染主页仪表盘到 WebBrowser

语法:Dashboard_Render(wb)

Dashboard_Reset
Dashboard_AddKPI "订单总数", 4805
Dashboard_Render Me!wbHome

参数可以传 Access WebBrowser 控件,也可以传控件的 .Object。未授权时会写入白底专业版提示页,不弹 MsgBox。

GetUserAvatarPath获取当前用户头像路径
Me.imgAvatar.Picture = GetUserAvatarPath()
ChangeUserAvatar更换当前用户头像
If ChangeUserAvatar() Then Me.imgAvatar.Picture = GetUserAvatarPath()

安全与权限

HashPassword生成密码哈希

语法:HashPassword(PlainText, [Salt=""])

strHash = HashPassword(Me.txtPassword)
VerifyPassword校验密码

语法:VerifyPassword(PlainText, StoredHash)

If VerifyPassword(Me.txtPassword, strStoredHash) Then ...
NeedsPasswordRehash判断旧密码是否需要升级哈希
If NeedsPasswordRehash(strStoredHash) Then strStoredHash = HashPassword(Me.txtPassword)
GetPermissions读取当前模块权限集合

语法:GetPermissions(Module, CltPermissions)

Dim cltPermissions As New Collection
If Not GetPermissions("F_Customer", cltPermissions) Then Exit Sub
If cltPermissions("Open/Run") Then DoCmd.OpenForm "F_Customer"
LogOff关闭业务窗体并返回登录界面
Call LogOff

常见问题

打开程序后提示找不到链接表怎么办?

通常是后端 Data.accdb 路径改变。请在登录界面进入链接表设置,重新选择后端文件。新版登录会区分关键系统表和普通业务表:普通业务表或 USys* 链接缺失时只提示并继续登录,TS_Users、TS_Parameters、TS_UserGroups 等关键系统表不可访问时才会阻断登录。

切换 SQL Server 或 MySQL 后端后链接表刷新失败怎么办?

请先在链接表管理器中测试连接,再执行批量重新链接。SQL Server / MySQL 链接表管理属于专业版功能,请确认当前版本已开通专业版。

登录失败怎么办?

先确认用户名和密码,再检查后端链接。如果数据库服务器不可连接,或登录必需的 TS_ 系统表不可访问,登录会失败;如果只是普通业务链接表缺失,系统会提示但仍允许进入。

权限 / 菜单

为什么某个用户看不到菜单?

该用户所属角色没有对应菜单的"查看"权限。请管理员检查角色权限和菜单权限绑定。

权限改了为什么没有立即生效?

建议用户退出并重新登录。权限缓存和菜单显示通常在登录或主界面加载时刷新。

为什么点击某些功能会提示需要专业版?

MySQL 后端、数据库升迁向导、SQL Server / MySQL 完整链接管理、单据选择弹窗、主页仪表盘、AccessTreeView、AccessDataTable 等功能为专业版专属。请确认当前版本已开通专业版后再使用。主页仪表盘未授权时会在页面内显示提示,不再弹出单独提示框。

点击选单按钮提示通用单据选择窗体不存在怎么办?

请确认前端中已经包含 USysFrmBillSelect。开发环境可在 VBA 立即窗口执行 BuildBillSelectForm True 重建该窗体,确认编译通过后再重新发布前端。

选单确定后没有写入明细怎么办?

先检查字段映射中的目标字段和来源字段是否拼写正确,再确认目标明细子窗体记录源是否可新增。如果子窗体基于复杂查询导致记录集不可更新,可在 BillSelect_ShowForSubform 最后一个参数指定实际写入表或可更新查询。

环境 / 资源

AccessTreeView 或 AccessDataTable 页面空白、一直加载怎么办?

先确认已激活专业版、控件类型可用、相关 assets 目录完整且表或查询可读取,再检查 DocumentComplete 事件已设置并调用对应模块入口。DataTable 还需要 vendor 文件和 Bootstrap 样式;TreeView 可通过 IsReady、IsLoaded 区分页面初始化和数据加载问题。

旧版 Access 能否使用这两个 Web 数据控件?

以当前安装的 Access 是否实际支持 Edge 控件为准,不能只根据 Office 位数判断。TreeView 提供 WebBrowser 兼容方案,可用 BuildAccessTreeViewForm True, True 生成;DataTable 当前仅支持 Edge 控件。

32 位和 64 位版本有什么区别?

功能一致,仅适配不同位数的 Office。32 位 Office 必须使用 32 位版本,64 位 Office 必须使用 64 位版本。

菜单图标不显示怎么办?

检查 assets/Theme/font-awesome.css 和字体文件是否存在,资源路径应相对数据库目录可访问。

主题或菜单字体大小保存后没变化?

确认系统设置保存成功。如果仍未变化,请关闭主界面重新登录。

开发相关

升级后旧窗体为什么没有变成新布局?

模块升级不会自动重写旧窗体。新布局和事件模板需要重新生成,执行前备份手工修改过的对象。

列表刷新后为什么没有显示所有记录?

生成的刷新按钮重新查询列表子窗体,但保留当前筛选条件。需要全部记录时先清除查询条件;若出现刷新错误,请按提示检查实际列表控件名并确认已使用新模板生成。

邮件中心打不开,或换电脑后看不到原来的邮件?

打不开时检查前台是否已生成邮件窗体,以及已有邮件中心菜单的查看权限。邮件数据保存在当前电脑的本地目录,不会自动同步到其他电脑;DPAPI 授权码与 Windows 用户环境绑定,迁移后需要重新配置凭据。

代码生成器生成的窗体打不开怎么办?

在 VBA 编辑器中执行"调试 → 编译",根据编译错误定位字段名、主键字段或窗体名冲突等问题。

进度条显示后程序像卡住了怎么办?

在长循环中加入 DoEvents,并确保任务结束后调用进度条对象的 Close 方法。