一、引言
模块开发是通达OA二次开发的核心内容。本文将通过一个完整的实战案例,详细介绍如何创建一个功能完善的OA模块,包括目录结构、数据库设计、代码编写、菜单注册和权限分配等环节。
二、开发前准备
2.1 需求分析
假设我们需要开发一个"项目管理"模块,包含以下功能:

2.2 数据库设计
项目表(project) :
| 字段名 | 类型 | 说明 |
|---|---|---|
| project_id | int | 项目ID(主键,自增) |
| project_name | varchar(100) | 项目名称 |
| description | text | 项目描述 |
| start_date | date | 开始日期 |
| end_date | date | 结束日期 |
| status | tinyint | 状态(0=进行中,1=已完成) |
| creator_uid | int | 创建人ID |
| create_time | datetime | 创建时间 |
| update_time | datetime | 更新时间 |
数据库关系图:

三、模块开发步骤
3.1 步骤一:建立模块目录
在 MYOA\webroot\general 目录下创建模块目录:
php
New-Item -ItemType Directory -Path "MYOA\webroot\general\project"
目录结构规划:
csharp
project/
├── index.php # 项目列表页
├── add.php # 添加项目页
├── edit.php # 编辑项目页
├── detail.php # 项目详情页
├── delete.php # 删除项目接口
├── ajax.php # AJAX接口
└── style.css # 模块样式文件
3.2 步骤二:创建数据库表
使用phpMyAdmin或SQL语句创建表:
sql
CREATE TABLE IF NOT EXISTS `project` (
`project_id` int(11) NOT NULL AUTO_INCREMENT,
`project_name` varchar(100) NOT NULL,
`description` text,
`start_date` date DEFAULT NULL,
`end_date` date DEFAULT NULL,
`status` tinyint(1) DEFAULT '0',
`creator_uid` int(11) NOT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`project_id`),
KEY `creator_uid` (`creator_uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.3 步骤三:编写核心代码
3.3.1 项目列表页(index.php)
php
<?php
$HTML_PAGE_TITLE = _("项目管理");
include_once("inc/auth.inc.php");
include_once("inc/header.inc.php");
$td = new TD();
// 分页参数
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$page_size = 10;
$offset = ($page - 1) * $page_size;
// 查询项目列表
$sql = "SELECT p.*, u.name AS creator_name
FROM project p
LEFT JOIN user u ON p.creator_uid = u.uid
ORDER BY p.create_time DESC
LIMIT $offset, $page_size";
$result = $td->query($sql);
// 查询总数
$count_sql = "SELECT COUNT(*) AS total FROM project";
$count_result = $td->query($count_sql);
$count_row = $td->fetch_array($count_result);
$total = $count_row['total'];
$total_pages = ceil($total / $page_size);
?>
<div class="main-content">
<h2><?php echo _("项目列表"); ?></h2>
<a href="add.php" class="btn btn-primary">
<?php echo _("添加项目"); ?>
</a>
<table class="table">
<thead>
<tr>
<th><?php echo _("项目名称"); ?></th>
<th><?php echo _("描述"); ?></th>
<th><?php echo _("时间范围"); ?></th>
<th><?php echo _("状态"); ?></th>
<th><?php echo _("创建人"); ?></th>
<th><?php echo _("操作"); ?></th>
</tr>
</thead>
<tbody>
<?php while ($row = $td->fetch_array($result)) { ?>
<tr>
<td><?php echo $row['project_name']; ?></td>
<td><?php echo csubstr($row['description'], 0, 50); ?></td>
<td><?php echo $row['start_date'] . ' ~ ' . $row['end_date']; ?></td>
<td><?php echo $row['status'] ? _('已完成') : _('进行中'); ?></td>
<td><?php echo $row['creator_name']; ?></td>
<td>
<a href="detail.php?id=<?php echo $row['project_id']; ?>">
<?php echo _("查看"); ?>
</a>
<a href="edit.php?id=<?php echo $row['project_id']; ?>">
<?php echo _("编辑"); ?>
</a>
<a href="delete.php?id=<?php echo $row['project_id']; ?>"
onclick="return confirm('<?php echo _('确定删除?'); ?>')">
<?php echo _("删除"); ?>
</a>
</td>
</tr>
<?php } ?>
</tbody>
</table>
<?php // 分页代码省略 ?>
</div>
<?php
include_once("inc/footer.inc.php");
?>
3.3.2 添加项目页(add.php)
ini
<?php
$HTML_PAGE_TITLE = _("添加项目");
include_once("inc/auth.inc.php");
include_once("inc/header.inc.php");
if ($_POST) {
$td = new TD();
$project_name = $td->escape($_POST['project_name']);
$description = $td->escape($_POST['description']);
$start_date = $_POST['start_date'];
$end_date = $_POST['end_date'];
$creator_uid = $_SESSION['uid'];
$sql = "INSERT INTO project (project_name, description, start_date, end_date, creator_uid)
VALUES ('$project_name', '$description', '$start_date', '$end_date', $creator_uid)";
if ($td->query($sql)) {
Message(_("添加成功"), "index.php");
exit;
} else {
Message(_("添加失败"), "add.php");
exit;
}
}
?>
<div class="main-content">
<h2><?php echo _("添加项目"); ?></h2>
<form method="post" action="add.php">
<div class="form-group">
<label><?php echo _("项目名称"); ?>:</label>
<input type="text" name="project_name" required class="form-control">
</div>
<div class="form-group">
<label><?php echo _("项目描述"); ?>:</label>
<textarea name="description" class="form-control"></textarea>
</div>
<div class="form-group">
<label><?php echo _("开始日期"); ?>:</label>
<input type="date" name="start_date" required class="form-control">
</div>
<div class="form-group">
<label><?php echo _("结束日期"); ?>:</label>
<input type="date" name="end_date" required class="form-control">
</div>
<button type="submit" class="btn btn-primary">
<?php echo _("提交"); ?>
</button>
<a href="index.php" class="btn btn-default">
<?php echo _("返回"); ?>
</a>
</form>
</div>
<?php
include_once("inc/footer.inc.php");
?>
3.3.3 删除项目(delete.php)
php
<?php
include_once("inc/auth.inc.php");
$td = new TD();
$project_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
if ($project_id > 0) {
$sql = "DELETE FROM project WHERE project_id = $project_id";
if ($td->query($sql)) {
Message(_("删除成功"), "index.php");
} else {
Message(_("删除失败"), "index.php");
}
} else {
Message(_("参数错误"), "index.php");
}
?>
3.4 步骤四:注册菜单
在通达OA系统管理中注册菜单:
lua
Mermaid 渲染失败: Lexical error on line 9. Unrecognized text.
...al/project/index.php] E -->|父菜单| E3[
-----------------------^
菜单注册信息:
| 字段 | 值 |
|---|---|
| 菜单名称 | 项目管理 |
| 菜单地址 | /general/project/index.php |
| 父菜单 | 工作台(或其他合适的父菜单) |
| 菜单图标 | 选择合适的图标 |
| 排序号 | 根据需要设置 |
3.5 步骤五:分配权限

权限分配步骤:
- 进入系统管理 → 角色管理
- 选择需要分配权限的角色
- 点击"编辑权限"
- 在权限列表中找到"项目管理"并勾选
- 保存权限设置
四、高级功能开发
4.1 附件上传功能
使用OA内置的文件上传函数:
php
<?php
include_once("inc/utility_file.php");
// 处理文件上传
if ($_FILES['attachment']['name']) {
$attach = upload('attachment', 0, 0, 'project');
if ($attach['error'] == 0) {
$attach_id = $attach['id'];
// 保存附件ID到数据库
}
}
?>
4.2 人员选择组件
使用OA的人员选择组件:
xml
<?php
include_once("inc/header.inc.php");
?>
<!-- 人员多选组件 -->
<input type="hidden" name="user_ids" id="user_ids">
<script type="text/javascript">
// 调用人员选择组件
selectUser('user_ids', 'multi');
</script>
4.3 AJAX交互
php
<?php
// ajax.php
include_once("inc/auth.inc.php");
$action = isset($_GET['action']) ? $_GET['action'] : '';
if ($action == 'search') {
$keyword = $_GET['keyword'];
$td = new TD();
$keyword = $td->escape($keyword);
$sql = "SELECT * FROM project WHERE project_name LIKE '%$keyword%'";
$result = $td->query($sql);
$data = array();
while ($row = $td->fetch_array($result)) {
$data[] = $row;
}
echo json_encode($data);
exit;
}
?>
五、调试与测试
5.1 调试方法

调试工具:
| 工具 | 用途 |
|---|---|
| php_errors.log | PHP错误日志 |
| 浏览器开发者工具 | 前端调试 |
| phpMyAdmin | 数据库查询 |
| var_dump() | 变量输出 |
5.2 测试用例
功能测试:
| 测试项 | 测试步骤 | 预期结果 |
|---|---|---|
| 添加项目 | 填写表单提交 | 项目成功添加 |
| 编辑项目 | 修改信息提交 | 项目信息更新 |
| 删除项目 | 点击删除确认 | 项目成功删除 |
| 列表显示 | 访问列表页 | 显示所有项目 |
| 权限控制 | 无权限用户访问 | 提示无权限 |
六、模块部署与发布
6.1 部署流程

6.2 发布清单
| 项目 | 说明 |
|---|---|
| 代码文件 | 所有PHP文件 |
| 样式文件 | CSS文件 |
| 数据库脚本 | SQL文件 |
| 配置文件 | 菜单配置 |
| 权限配置 | 角色权限 |
七、最佳实践总结
7.1 代码规范

编码规范:
| 规范项 | 要求 |
|---|---|
| 变量命名 | 驼峰式命名 |
| 文件命名 | 小写字母,下划线分隔 |
| 数据库表 | 小写字母,下划线分隔 |
| SQL语句 | 使用TD类的escape方法转义 |
7.2 安全注意事项
- 输入验证:对所有用户输入进行验证
- SQL注入:使用参数化查询或escape方法
- 文件上传:验证文件类型和大小
- 权限控制:使用auth.inc.php进行权限验证
- 敏感信息:不输出敏感信息到页面