From e297a4871fc2e2d787d2d2641d2f342fe9106f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=BF=97=E5=BC=BA?= <357099073@qq.com> Date: Wed, 26 Aug 2026 18:37:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=90=8E=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/api/organization.js | 285 +++++++ backend/src/router/index.js | 7 + .../components/employeeEditDialog.vue | 383 ++++++++++ .../organization/components/orgEditDialog.vue | 301 ++++++++ .../components/orgSettingsDialog.vue | 259 +++++++ .../src/views/apps/oa/organization/index.vue | 696 ++++++++++++++++++ go/controllers/backend_erp.go | 350 +++++++++ go/routers/backend/backend.go | 11 + 8 files changed, 2292 insertions(+) create mode 100644 backend/src/api/organization.js create mode 100644 backend/src/views/apps/oa/organization/components/employeeEditDialog.vue create mode 100644 backend/src/views/apps/oa/organization/components/orgEditDialog.vue create mode 100644 backend/src/views/apps/oa/organization/components/orgSettingsDialog.vue create mode 100644 backend/src/views/apps/oa/organization/index.vue diff --git a/backend/src/api/organization.js b/backend/src/api/organization.js new file mode 100644 index 0000000..b6b72ef --- /dev/null +++ b/backend/src/api/organization.js @@ -0,0 +1,285 @@ +import request from '@/utils/request' + +// 组织架构相关接口 + +// 获取组织架构列表 +export function getOrganizationList(params) { + return request({ + url: '/backend/erp/getOrganization', + method: 'get', + params + }) +} + +// 获取组织架构详情 +export function getOrganizationDetail(id) { + return request({ + url: `/backend/erp/getOrganizationDetail/${id}`, + method: 'get' + }) +} + +// 创建组织架构 +export function createOrganization(data) { + return request({ + url: '/backend/erp/createOrganization', + method: 'post', + data + }) +} + +// 更新组织架构 +export function updateOrganization(id, data) { + return request({ + url: `/backend/erp/editOrganization/${id}`, + method: 'post', + data + }) +} + +// 删除组织架构 +export function deleteOrganization(id) { + return request({ + url: `/backend/erp/deleteOrganization/${id}`, + method: 'delete' + }) +} + +// 获取企业单位列表 +export function getCompanyList(params) { + return request({ + url: '/backend/erp/getCompanys', + method: 'get', + params + }) +} + +// 获取部门列表 +export function getDepartmentList(params) { + return request({ + url: '/backend/erp/getDepartments', + method: 'get', + params + }) +} + +// 员工相关接口 + +// 获取员工列表 +export function getEmployeeList(params) { + return request({ + url: '/backend/erp/getEmployee', + method: 'get', + params + }) +} + +// 获取员工详情 +export function getEmployeeDetail(id) { + return request({ + url: `/backend/erp/getEmployeeDetail/${id}`, + method: 'get' + }) +} + +// 创建员工 +export function createEmployee(data) { + return request({ + url: '/backend/erp/createEmployee', + method: 'post', + data + }) +} + +// 更新员工 +export function updateEmployee(id, data) { + return request({ + url: `/backend/erp/editEmployee/${id}`, + method: 'post', + data + }) +} + +// 删除员工 +export function deleteEmployee(id) { + return request({ + url: `/backend/erp/deleteEmployee/${id}`, + method: 'delete' + }) +} + +// 职位相关接口 + +// 获取职位列表 +export function getPositionList(params) { + return request({ + url: '/backend/erp/getPosition', + method: 'get', + params + }) +} + +// 获取职位详情 +export function getPositionDetail(id) { + return request({ + url: `/backend/erp/getPositionDetail/${id}`, + method: 'get' + }) +} + +// 创建职位 +export function createPosition(data) { + return request({ + url: '/backend/erp/createPosition', + method: 'post', + data + }) +} + +// 更新职位 +export function updatePosition(id, data) { + return request({ + url: `/backend/erp/editPosition/${id}`, + method: 'post', + data + }) +} + +// 删除职位 +export function deletePosition(id) { + return request({ + url: `/backend/erp/deletePosition/${id}`, + method: 'delete' + }) +} + +// 组织设置相关接口 + +// 获取组织设置 +export function getOrgSettings() { + return request({ + url: '/backend/erp/getOrgSettings', + method: 'get' + }) +} + +// 保存组织设置 +export function saveOrgSettings(data) { + return request({ + url: '/backend/erp/saveOrgSettings', + method: 'post', + data + }) +} + +// 组织架构导出 +export function exportOrganization(params) { + return request({ + url: '/backend/erp/exportOrganization', + method: 'get', + params, + responseType: 'blob' + }) +} + +// 组织架构导入 +export function importOrganization(data) { + return request({ + url: '/backend/erp/importOrganization', + method: 'post', + data, + headers: { + 'Content-Type': 'multipart/form-data' + } + }) +} + +// 批量操作组织 +export function batchOrganizeOrganizations(ids, action) { + return request({ + url: '/backend/erp/batchOrganizeOrganizations', + method: 'post', + data: { + ids, + action + } + }) +} + +// 获取组织架构统计信息 +export function getOrganizationStats() { + return request({ + url: '/backend/erp/getOrganizationStats', + method: 'get' + }) +} + +// 获取组织架构树形结构 +export function getOrganizationTree(params) { + return request({ + url: '/backend/erp/getOrganizationTree', + method: 'get', + params + }) +} + +// 搜索组织架构 +export function searchOrganizations(keyword) { + return request({ + url: '/backend/erp/searchOrganizations', + method: 'get', + params: { keyword } + }) +} + +// 获取组织架构下的所有员工 +export function getOrganizationEmployees(orgId) { + return request({ + url: `/backend/erp/getOrganizationEmployees/${orgId}`, + method: 'get' + }) +} + +// 移动员工到其他组织 +export function moveEmployeeToOrg(employeeId, orgId) { + return request({ + url: '/backend/erp/moveEmployeeToOrg', + method: 'post', + data: { + employee_id: employeeId, + org_id: orgId + } + }) +} + +// 获取组织架构层级关系 +export function getOrganizationHierarchy(orgId) { + return request({ + url: `/backend/erp/getOrganizationHierarchy/${orgId}`, + method: 'get' + }) +} + +// 验证组织编码唯一性 +export function checkOrgCodeUnique(orgCode, excludeId) { + return request({ + url: '/backend/erp/checkOrgCodeUnique', + method: 'get', + params: { + org_code: orgCode, + exclude_id: excludeId || '' + } + }) +} + +// 验证员工账号唯一性 +export function checkEmployeeAccountUnique(account, excludeId) { + return request({ + url: '/backend/erp/checkEmployeeAccountUnique', + method: 'get', + params: { + account, + exclude_id: excludeId || '' + } + }) +} diff --git a/backend/src/router/index.js b/backend/src/router/index.js index 1e23ac2..e0de6e3 100644 --- a/backend/src/router/index.js +++ b/backend/src/router/index.js @@ -69,6 +69,13 @@ const staticMainChildren = [ component: () => import("@/views/user/userProfile.vue"), meta: { requiresAuth: true, title: "用户中心" } }, + // OA 组织架构 + { + path: "/apps/oa/organization", + name: "Organization", + component: () => import("@/views/apps/oa/organization/index.vue"), + meta: { requiresAuth: true, title: "组织架构", modulePath: "/apps/oa" } + }, // 兼容拼写错误的路径重定向 { path: "/apps/erp/dashborad", diff --git a/backend/src/views/apps/oa/organization/components/employeeEditDialog.vue b/backend/src/views/apps/oa/organization/components/employeeEditDialog.vue new file mode 100644 index 0000000..17e8219 --- /dev/null +++ b/backend/src/views/apps/oa/organization/components/employeeEditDialog.vue @@ -0,0 +1,383 @@ + + + + + diff --git a/backend/src/views/apps/oa/organization/components/orgEditDialog.vue b/backend/src/views/apps/oa/organization/components/orgEditDialog.vue new file mode 100644 index 0000000..5bb7b49 --- /dev/null +++ b/backend/src/views/apps/oa/organization/components/orgEditDialog.vue @@ -0,0 +1,301 @@ + + + + + diff --git a/backend/src/views/apps/oa/organization/components/orgSettingsDialog.vue b/backend/src/views/apps/oa/organization/components/orgSettingsDialog.vue new file mode 100644 index 0000000..9984529 --- /dev/null +++ b/backend/src/views/apps/oa/organization/components/orgSettingsDialog.vue @@ -0,0 +1,259 @@ + + + + + diff --git a/backend/src/views/apps/oa/organization/index.vue b/backend/src/views/apps/oa/organization/index.vue new file mode 100644 index 0000000..91e5542 --- /dev/null +++ b/backend/src/views/apps/oa/organization/index.vue @@ -0,0 +1,696 @@ + + + + + diff --git a/go/controllers/backend_erp.go b/go/controllers/backend_erp.go index 0c8a1c5..bc582c6 100644 --- a/go/controllers/backend_erp.go +++ b/go/controllers/backend_erp.go @@ -1076,3 +1076,353 @@ func hashEmployeePassword(plain string) string { sum := sha256.Sum256([]byte(plain)) return hex.EncodeToString(sum[:]) } + +// GetOrgSettings 获取组织架构设置 +// GET /backend/erp/getOrgSettings +func (c *BackendErpController) GetOrgSettings() { + tid, _ := c.GetInt("tid") + + settings := map[string]interface{}{ + "org_code_prefix": "ORG", + "employee_code_prefix": "EMP", + "position_code_prefix": "POS", + "auto_generate_codes": true, + "allow_duplicate_codes": false, + "max_org_levels": 10, + "max_org_children": 50, + "tid": tid, + } + + c.jsonOK(settings) +} + +// SaveOrgSettings 保存组织架构设置 +// POST /backend/erp/saveOrgSettings +func (c *BackendErpController) SaveOrgSettings() { + _ = c.parseJSONBody() + + settings := map[string]interface{}{ + "org_code_prefix": "ORG", + "employee_code_prefix": "EMP", + "position_code_prefix": "POS", + "auto_generate_codes": true, + "allow_duplicate_codes": false, + "max_org_levels": 10, + "max_org_children": 50, + } + + // 这里可以添加保存到数据库的逻辑 + // 目前只是返回成功响应 + + c.jsonOK(settings) +} + +// GetOrganizationTree 获取组织架构树形结构 +// GET /backend/erp/getOrganizationTree +func (c *BackendErpController) GetOrganizationTree() { + tid, _ := c.GetInt("tid") + + qs := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("delete_time__isnull", true). + Exclude("status", 0) + if tid > 0 { + qs = qs.Filter("tid", tid) + } + + var rows []models.BackendErpOrganization + _, err := qs.OrderBy("sort", "id").All(&rows) + if err != nil { + c.jsonError(500, "查询组织机构失败: "+err.Error()) + return + } + + // 构建树形结构 + tree := buildOrganizationTree(rows) + c.jsonOK(tree) +} + +// SearchOrganizations 搜索组织机构 +// GET /backend/erp/searchOrganizations +func (c *BackendErpController) SearchOrganizations() { + tid, _ := c.GetInt("tid") + keyword := c.GetString("keyword") + + qs := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("delete_time__isnull", true). + Exclude("status", 0) + if tid > 0 { + qs = qs.Filter("tid", tid) + } + + if keyword != "" { + qs = qs.Filter("org_name__icontains", keyword) + } + + var rows []models.BackendErpOrganization + _, err := qs.OrderBy("sort", "id").All(&rows) + if err != nil { + c.jsonError(500, "查询组织机构失败: "+err.Error()) + return + } + + list := make([]erpOrganizationDTO, 0, len(rows)) + for _, row := range rows { + list = append(list, c.organizationDTO(row)) + } + + c.jsonOK(list) +} + +// GetOrganizationEmployees 获取指定组织下的员工列表 +// GET /backend/erp/getOrganizationEmployees/:org_id +func (c *BackendErpController) GetOrganizationEmployees() { + orgID, ok := c.pathUint64(":org_id") + if !ok { + c.jsonError(400, "无效组织ID") + return + } + + tid, _ := c.GetInt("tid") + + // 获取该组织及其所有子组织的ID + var orgIDs []uint64 + orgIDs = append(orgIDs, orgID) + + // 递归获取子组织ID + getChildOrgIDs(orgID, &orgIDs) + + qs := models.Orm.QueryTable(new(models.BackendErpEmployee)). + Filter("delete_time__isnull", true) + if tid > 0 { + qs = qs.Filter("tid", tid) + } + + // 查询属于这些组织的员工 + if len(orgIDs) > 0 { + qs = qs.Filter("department__in", orgIDs) + } + + var rows []models.BackendErpEmployee + _, err := qs.OrderBy("-id").All(&rows) + if err != nil { + c.jsonError(500, "查询员工失败: "+err.Error()) + return + } + + list := make([]erpEmployeeDTO, 0, len(rows)) + for _, row := range rows { + list = append(list, c.employeeDTO(row)) + } + + c.jsonOK(list) +} + +// MoveEmployeeToOrg 移动员工到指定组织 +// POST /backend/erp/moveEmployeeToOrg +func (c *BackendErpController) MoveEmployeeToOrg() { + body := c.parseJSONBody() + + employeeID, hasEmployee := c.getUintValue(body, "employee_id") + orgID, hasOrg := c.getUint64Value(body, "org_id") + + if !hasEmployee || !hasOrg { + c.jsonError(400, "员工ID和组织ID不能为空") + return + } + + // 更新员工的部门信息 + update := orm.Params{"department": orgID} + num, err := models.Orm.QueryTable(new(models.BackendErpEmployee)). + Filter("id", employeeID). + Filter("delete_time__isnull", true). + Update(update) + if err != nil { + c.jsonError(500, "移动员工失败: "+err.Error()) + return + } + if num == 0 { + c.jsonError(404, "员工不存在") + return + } + + c.jsonOK(nil) +} + +// GetOrganizationHierarchy 获取组织层级结构 +// GET /backend/erp/getOrganizationHierarchy/:org_id +func (c *BackendErpController) GetOrganizationHierarchy() { + orgID, ok := c.pathUint64(":org_id") + if !ok { + c.jsonError(400, "无效组织ID") + return + } + + // 获取指定组织的信息 + var org models.BackendErpOrganization + err := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("id", orgID). + Filter("delete_time__isnull", true). + One(&org) + if err != nil { + c.jsonError(404, "组织不存在") + return + } + + // 获取所有子组织 + var children []models.BackendErpOrganization + _, err = models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("parent_id", orgID). + Filter("delete_time__isnull", true). + OrderBy("sort", "id"). + All(&children) + if err != nil { + c.jsonError(500, "查询子组织失败: "+err.Error()) + return + } + + hierarchy := map[string]interface{}{ + "current": c.organizationDTO(org), + "children": make([]erpOrganizationDTO, 0, len(children)), + } + + childList := make([]erpOrganizationDTO, len(children)) + for i, child := range children { + childList[i] = c.organizationDTO(child) + } + hierarchy["children"] = childList + + c.jsonOK(hierarchy) +} + +// CheckOrgCodeUnique 检查组织编码唯一性 +// GET /backend/erp/checkOrgCodeUnique +func (c *BackendErpController) CheckOrgCodeUnique() { + tid, _ := c.GetInt("tid") + orgCode := c.GetString("org_code") + orgID, _ := c.GetUint64("org_id") + + if orgCode == "" { + c.jsonError(400, "组织编码不能为空") + return + } + + qs := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("org_code", orgCode). + Filter("delete_time__isnull", true) + if tid > 0 { + qs = qs.Filter("tid", tid) + } + if orgID > 0 { + qs = qs.Exclude("id", orgID) + } + + count, err := qs.Count() + if err != nil { + c.jsonError(500, "检查编码唯一性失败: "+err.Error()) + return + } + + c.jsonOK(map[string]interface{}{ + "unique": count == 0, + "message": func() string { + if count == 0 { + return "编码可用" + } + return "编码已存在" + }(), + }) +} + +// CheckEmployeeAccountUnique 检查员工账号唯一性 +// GET /backend/erp/checkEmployeeAccountUnique +func (c *BackendErpController) CheckEmployeeAccountUnique() { + tid, _ := c.GetInt("tid") + account := c.GetString("account") + employeeID, _ := c.GetInt("employee_id") + + if account == "" { + c.jsonError(400, "员工账号不能为空") + return + } + + qs := models.Orm.QueryTable(new(models.BackendErpEmployee)). + Filter("account", account). + Filter("delete_time__isnull", true) + if tid > 0 { + qs = qs.Filter("tid", tid) + } + if employeeID > 0 { + qs = qs.Exclude("id", employeeID) + } + + count, err := qs.Count() + if err != nil { + c.jsonError(500, "检查账号唯一性失败: "+err.Error()) + return + } + + c.jsonOK(map[string]interface{}{ + "unique": count == 0, + "message": func() string { + if count == 0 { + return "账号可用" + } + return "账号已存在" + }(), + }) +} + +// buildOrganizationTree 构建组织树形结构 +func buildOrganizationTree(rows []models.BackendErpOrganization) []map[string]interface{} { + tree := make([]map[string]interface{}, 0) + nodeMap := make(map[uint64]map[string]interface{}) + + // 创建节点映射 + for _, row := range rows { + node := map[string]interface{}{ + "id": row.ID, + "tid": row.Tid, + "org_name": row.OrgName, + "org_code": row.OrgCode, + "parent_id": row.ParentID, + "sort": row.Sort, + "is_company": row.IsCompany, + "status": row.Status, + "leader_id": row.LeaderID, + "remark": row.Remark, + "children": make([]map[string]interface{}, 0), + } + nodeMap[row.ID] = node + } + + // 构建树形结构 + for _, row := range rows { + node := nodeMap[row.ID] + if row.ParentID == 0 { + tree = append(tree, node) + } else { + if parent, exists := nodeMap[row.ParentID]; exists { + parent["children"] = append(parent["children"].([]map[string]interface{}), node) + } + } + } + + return tree +} + +// getChildOrgIDs 递归获取子组织ID +func getChildOrgIDs(parentID uint64, orgIDs *[]uint64) { + var children []models.BackendErpOrganization + _, err := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("parent_id", parentID). + Filter("delete_time__isnull", true). + All(&children) + if err != nil { + return + } + + for _, child := range children { + *orgIDs = append(*orgIDs, child.ID) + getChildOrgIDs(child.ID, orgIDs) + } +} diff --git a/go/routers/backend/backend.go b/go/routers/backend/backend.go index a05ffb9..24192ef 100644 --- a/go/routers/backend/backend.go +++ b/go/routers/backend/backend.go @@ -114,6 +114,17 @@ func RegisterAuthRoutes() { beego.Router("/backend/erp/createPosition", &controllers.BackendErpController{}, "post:CreatePosition") beego.Router("/backend/erp/editPosition/:id", &controllers.BackendErpController{}, "post:EditPosition") beego.Router("/backend/erp/deletePosition/:id", &controllers.BackendErpController{}, "delete:DeletePosition") + + // 新增组织架构接口 + beego.Router("/backend/erp/getOrgSettings", &controllers.BackendErpController{}, "get:GetOrgSettings") + beego.Router("/backend/erp/saveOrgSettings", &controllers.BackendErpController{}, "post:SaveOrgSettings") + beego.Router("/backend/erp/getOrganizationTree", &controllers.BackendErpController{}, "get:GetOrganizationTree") + beego.Router("/backend/erp/searchOrganizations", &controllers.BackendErpController{}, "get:SearchOrganizations") + beego.Router("/backend/erp/getOrganizationEmployees/:org_id", &controllers.BackendErpController{}, "get:GetOrganizationEmployees") + beego.Router("/backend/erp/moveEmployeeToOrg", &controllers.BackendErpController{}, "post:MoveEmployeeToOrg") + beego.Router("/backend/erp/getOrganizationHierarchy/:org_id", &controllers.BackendErpController{}, "get:GetOrganizationHierarchy") + beego.Router("/backend/erp/checkOrgCodeUnique", &controllers.BackendErpController{}, "get:CheckOrgCodeUnique") + beego.Router("/backend/erp/checkEmployeeAccountUnique", &controllers.BackendErpController{}, "get:CheckEmployeeAccountUnique") // 文章管理 beego.Router("/backend/articlesList", &controllers.BackendArticleController{}, "get:List")