gatsby hace 4 meses
padre
commit
fe75310706
Se han modificado 31 ficheros con 1288 adiciones y 827 borrados
  1. 13 0
      backend/Dockerfile
  2. 49 0
      backend/app/config/.env.dev
  3. 2 1
      backend/app/plugin/module_payment/__init__.py
  4. 97 112
      backend/app/plugin/module_payment/department/controller.py
  5. 9 52
      backend/app/plugin/module_payment/department/crud.py
  6. 21 24
      backend/app/plugin/module_payment/department/model.py
  7. 85 38
      backend/app/plugin/module_payment/department/schema.py
  8. 97 220
      backend/app/plugin/module_payment/department/service.py
  9. 2 2
      backend/app/plugin/module_payment/employee/__init__.py
  10. 15 12
      backend/app/plugin/module_payment/employee/controller.py
  11. 26 17
      backend/app/plugin/module_payment/employee/crud.py
  12. 8 1
      backend/app/plugin/module_payment/employee/model.py
  13. 7 3
      backend/app/plugin/module_payment/employee/schema.py
  14. 65 12
      backend/app/plugin/module_payment/employee/service.py
  15. 3 3
      backend/app/plugin/module_payment/notification/enums.py
  16. 27 2
      backend/app/plugin/module_payment/notification/handlers/employee_handler.py
  17. 15 9
      backend/app/plugin/module_payment/notification/schemas.py
  18. 11 11
      backend/app/scripts/data/sys_param.json
  19. 52 0
      docker-compose.yml
  20. 21 0
      frontend/Dockerfile
  21. 182 105
      frontend/src/api/module_payment/department.ts
  22. 48 22
      frontend/src/api/module_payment/employee.ts
  23. 19 10
      frontend/src/layouts/components/NavBar/components/EnterpriseSwitcher.vue
  24. 9 9
      frontend/src/layouts/components/NavBar/components/NavbarActions.vue
  25. 13 5
      frontend/src/store/modules/enterprise.store.ts
  26. 22 2
      frontend/src/views/module_payment/employee/components/DepartmentDetail.vue
  27. 35 11
      frontend/src/views/module_payment/employee/components/DepartmentForm.vue
  28. 73 21
      frontend/src/views/module_payment/employee/components/EmployeeDetail.vue
  29. 65 62
      frontend/src/views/module_payment/employee/components/EmployeeForm.vue
  30. 197 60
      frontend/src/views/module_payment/employee/index.vue
  31. 0 1
      frontend/src/views/module_payment/enterprise/index.vue

+ 13 - 0
backend/Dockerfile

@@ -0,0 +1,13 @@
+FROM python:3.9-slim
+
+WORKDIR /app
+
+COPY requirements.txt .
+
+RUN pip install --no-cache-dir -r requirements.txt
+
+COPY . .
+
+EXPOSE 8000
+
+CMD ["uvicorn", "app.api.v1:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

+ 49 - 0
backend/app/config/.env.dev

@@ -0,0 +1,49 @@
+# 项目环境
+ENVIRONMENT=DEV
+
+# 服务器配置
+SERVER_HOST=0.0.0.0
+SERVER_PORT=8000
+
+# 数据库配置
+DATABASE_TYPE=postgres
+DATABASE_HOST=database
+DATABASE_PORT=5432
+DATABASE_USER=admin
+DATABASE_PASSWORD=password
+DATABASE_NAME=example_db
+
+# Redis配置
+REDIS_ENABLE=True
+REDIS_HOST=redis
+REDIS_PORT=6379
+REDIS_DB_NAME=0
+REDIS_USER=
+REDIS_PASSWORD=
+
+# 日志配置
+LOGGER_LEVEL=DEBUG
+
+# 跨域配置
+CORS_ORIGIN_ENABLE=True
+ALLOW_ORIGINS=*
+ALLOW_METHODS=*
+ALLOW_HEADERS=*
+ALLOW_CREDENTIALS=True
+
+# 登录认证配置
+SECRET_KEY=vgb0tnl9d58+6n-6h-ea&u^1#s0ccp!794=krylxcjq75vzps$
+ALGORITHM=HS256
+ACCESS_TOKEN_EXPIRE_MINUTES=3600
+REFRESH_TOKEN_EXPIRE_MINUTES=7200
+
+# API文档配置
+DEBUG=True
+TITLE=🎉 Payment Platform 🎉
+VERSION=1.0.0
+DESCRIPTION=Payment Platform API文档
+SUMMARY=接口汇总
+DOCS_URL=/docs
+REDOC_URL=/redoc
+LJDOC_URL=/ljdoc
+ROOT_PATH=/api/v1

+ 2 - 1
backend/app/plugin/module_payment/__init__.py

@@ -3,5 +3,6 @@ from .enterprise import *
 from .expense import *
 from .notification import *
 from .points import *
+from .department import *
 
-__all__ = ["employee", "enterprise", "expense", "notification", "points"]
+__all__ = ["employee", "enterprise", "expense", "notification", "points", "department"]

+ 97 - 112
backend/app/plugin/module_payment/department/controller.py

@@ -1,4 +1,4 @@
-from typing import Annotated
+from typing import Annotated, Optional
 
 from fastapi import APIRouter, Depends, Path, Query
 from fastapi.responses import JSONResponse
@@ -11,10 +11,9 @@ from app.core.router_class import OperationLogRoute
 
 from .schema import (
     DepartmentCreateSchema,
-    DepartmentMoveSchema,
-    DepartmentOutSchema,
-    DepartmentTreeSchema,
     DepartmentUpdateSchema,
+    DepartmentDetailOutSchema,
+    DepartmentOperationOutSchema,
 )
 from .service import DepartmentService
 
@@ -28,156 +27,142 @@ DepartmentRouter = APIRouter(
 @DepartmentRouter.post(
     "",
     summary="创建部门",
-    description="创建一个新的部门",
-    response_model=ResponseSchema[DepartmentOutSchema],
+    description="创建部门 (alipay.commerce.ec.department.create)",
+    response_model=ResponseSchema[DepartmentOperationOutSchema],
 )
 async def create_department_controller(
     data: DepartmentCreateSchema,
     auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:create"]))],
 ) -> JSONResponse:
     """创建部门"""
-    result = await DepartmentService.create_service(auth=auth, data=data)
-    log.info(f"创建部门成功: {data.name}, department_id={result.department_id}")
+    result = await DepartmentService.create_department_service(auth=auth, data=data)
+    log.info(f"创建部门成功: {data.department_name}, department_id={result.department_id}")
     return SuccessResponse(data=result, msg="创建部门成功")
 
 
-@DepartmentRouter.get(
-    "",
-    summary="查询部门列表",
-    description="分页查询部门列表(平铺)",
-    response_model=ResponseSchema[DepartmentOutSchema],
+@DepartmentRouter.delete(
+    "/{department_id}",
+    summary="删除部门",
+    description="删除部门 (alipay.commerce.ec.department.delete)",
+    response_model=ResponseSchema[DepartmentOperationOutSchema],
 )
-async def list_department_controller(
-    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:list"]))],
-    page_no: Annotated[int, Query(description="页码")] = 1,
-    page_size: Annotated[int, Query(description="每页数量")] = 100,
-    name: Annotated[str | None, Query(description="部门名称")] = None,
-    code: Annotated[str | None, Query(description="部门编码")] = None,
-    status: Annotated[str | None, Query(description="状态")] = None,
+async def delete_department_controller(
+    department_id: Annotated[str, Path(description="部门ID")],
+    enterprise_id: Annotated[str, Query(description="企业ID")],
+    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:delete"]))],
 ) -> JSONResponse:
-    """查询部门列表"""
-    search = {}
-    if name:
-        search["name"] = name
-    if code:
-        search["code"] = code
-    if status:
-        search["status"] = status
-
-    result = await DepartmentService.list_service(
-        auth=auth, page_no=page_no, page_size=page_size, search=search
+    """删除部门"""
+    result = await DepartmentService.delete_department_service(
+        auth=auth, department_id=department_id, enterprise_id=enterprise_id
     )
-    return SuccessResponse(data=result, msg="查询部门列表成功")
+    log.info(f"删除部门成功: department_id={department_id}")
+    return SuccessResponse(data=result, msg="删除部门成功")
 
 
-@DepartmentRouter.get(
-    "/tree",
-    summary="获取部门树",
-    description="获取部门树形结构",
-    response_model=ResponseSchema[DepartmentTreeSchema],
+@DepartmentRouter.put(
+    "/{department_id}",
+    summary="更新部门",
+    description="更新部门 (alipay.commerce.ec.department.info.modify)",
+    response_model=ResponseSchema[DepartmentOperationOutSchema],
 )
-async def tree_department_controller(
-    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:tree"]))],
+async def update_department_controller(
+    department_id: Annotated[str, Path(description="部门ID")],
+    enterprise_id: Annotated[str, Query(description="企业ID")],
+    data: DepartmentUpdateSchema,
+    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:update"]))],
 ) -> JSONResponse:
-    """获取部门树形结构"""
-    result = await DepartmentService.tree_service(auth=auth)
-    return SuccessResponse(data=result, msg="获取部门树成功")
+    """更新部门"""
+    result = await DepartmentService.update_department_service(
+        auth=auth, department_id=department_id, enterprise_id=enterprise_id, data=data
+    )
+    log.info(f"更新部门成功: department_id={department_id}")
+    return SuccessResponse(data=result, msg="更新部门成功")
 
 
 @DepartmentRouter.get(
     "/{department_id}",
     summary="查询部门详情",
-    description="根据 department_id 查询部门详情",
-    response_model=ResponseSchema[DepartmentOutSchema],
+    description="查询部门详情 (alipay.commerce.ec.department.info.query)",
+    response_model=ResponseSchema[DepartmentDetailOutSchema],
 )
-async def get_detail_controller(
+async def get_department_controller(
     department_id: Annotated[str, Path(description="部门ID")],
+    enterprise_id: Annotated[str, Query(description="企业ID")],
     auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:detail"]))],
 ) -> JSONResponse:
     """查询部门详情"""
-    result = await DepartmentService.detail_service(auth=auth, department_id=department_id)
-    log.info(f"查询部门详情成功: {department_id}")
+    result = await DepartmentService.get_department_service(
+        auth=auth, department_id=department_id, enterprise_id=enterprise_id
+    )
+    log.info(f"查询部门详情成功: department_id={department_id}")
     return SuccessResponse(data=result, msg="查询部门详情成功")
 
 
 @DepartmentRouter.get(
-    "/{department_id}/children",
-    summary="获取子部门",
-    description="获取指定部门的子部门列表",
-    response_model=ResponseSchema[DepartmentOutSchema],
+    "/{department_id}/sub",
+    summary="查询子部门列表",
+    description="查询子部门列表 (alipay.commerce.ec.department.sublist.query)",
 )
-async def get_children_controller(
+async def get_sub_departments_controller(
     department_id: Annotated[str, Path(description="部门ID")],
-    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:children"]))],
+    enterprise_id: Annotated[str, Query(description="企业ID")],
+    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:list"]))],
 ) -> JSONResponse:
-    """获取子部门列表"""
-    result = await DepartmentService.children_service(auth=auth, department_id=department_id)
-    return SuccessResponse(data=result, msg="获取子部门成功")
+    """查询子部门列表"""
+    result = await DepartmentService.get_sub_departments_service(
+        auth=auth, parent_department_id=department_id, enterprise_id=enterprise_id
+    )
+    log.info(f"查询子部门列表成功: department_id={department_id}")
+    return SuccessResponse(data=result, msg="查询子部门列表成功")
 
 
 @DepartmentRouter.get(
-    "/options",
-    summary="获取部门选项",
-    description="获取部门选择选项,用于上级部门选择",
-    response_model=ResponseSchema[list],
+    "",
+    summary="查询部门列表",
+    description="分页查询部门列表",
 )
-async def get_department_options_controller(
-    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:options"]))],
+async def list_department_controller(
+    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:list"]))],
+    page_no: Annotated[int, Query(description="页码")] = 1,
+    page_size: Annotated[int, Query(description="每页数量")] = 20,
+    enterprise_id: Annotated[str | None, Query(description="企业ID")] = None,
+    department_name: Annotated[str | None, Query(description="部门名称")] = None,
+    department_code: Annotated[str | None, Query(description="部门编码")] = None,
+    parent_department_id: Annotated[str | None, Query(description="上级部门ID")] = None,
+    status: Annotated[str | None, Query(description="状态")] = None,
 ) -> JSONResponse:
-    """获取部门选择选项"""
-    result = await DepartmentService.options_service(auth=auth)
-    return SuccessResponse(data=result, msg="获取部门选项成功")
-
+    """查询部门列表"""
+    search = {}
+    if enterprise_id:
+        search["enterprise_id"] = enterprise_id
+    if department_name:
+        search["department_name"] = department_name
+    if department_code:
+        search["department_code"] = department_code
+    if parent_department_id is not None:
+        search["parent_department_id"] = parent_department_id
+    if status:
+        search["status"] = status
 
-@DepartmentRouter.put(
-    "/{department_id}",
-    summary="更新部门",
-    description="更新部门信息",
-    response_model=ResponseSchema[DepartmentOutSchema],
-)
-async def update_department_controller(
-    department_id: Annotated[str, Path(description="部门ID")],
-    data: DepartmentUpdateSchema,
-    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:update"]))],
-) -> JSONResponse:
-    """更新部门"""
-    result = await DepartmentService.update_service(
-        auth=auth, department_id=department_id, data=data
+    result = await DepartmentService.list_service(
+        auth=auth, page_no=page_no, page_size=page_size, search=search
     )
-    log.info(f"更新部门成功: {department_id}")
-    return SuccessResponse(data=result, msg="更新部门成功")
+    log.info(f"查询部门列表成功")
+    return SuccessResponse(data=result, msg="查询部门列表成功")
 
 
-@DepartmentRouter.put(
-    "/{department_id}/move",
-    summary="移动部门",
-    description="移动部门(变更父级)",
-    response_model=ResponseSchema[DepartmentOutSchema],
+@DepartmentRouter.get(
+    "/tree/list",
+    summary="获取部门树形结构",
+    description="获取部门树形结构",
 )
-async def move_department_controller(
-    department_id: Annotated[str, Path(description="部门ID")],
-    data: DepartmentMoveSchema,
-    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:move"]))],
+async def get_department_tree_controller(
+    enterprise_id: Annotated[str, Query(description="企业ID")],
+    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:list"]))],
 ) -> JSONResponse:
-    """移动部门"""
-    result = await DepartmentService.move_service(
-        auth=auth, department_id=department_id, parent_id=data.parent_id
+    """获取部门树形结构"""
+    result = await DepartmentService.get_department_tree_service(
+        auth=auth, enterprise_id=enterprise_id
     )
-    log.info(f"移动部门成功: {department_id} -> {data.parent_id}")
-    return SuccessResponse(data=result, msg="移动部门成功")
-
-
-@DepartmentRouter.delete(
-    "/{department_id}",
-    summary="删除部门",
-    description="删除部门",
-    response_model=ResponseSchema[None],
-)
-async def delete_department_controller(
-    department_id: Annotated[str, Path(description="部门ID")],
-    auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:department:delete"]))],
-) -> JSONResponse:
-    """删除部门"""
-    await DepartmentService.delete_service(auth=auth, department_id=department_id)
-    log.info(f"删除部门成功: {department_id}")
-    return SuccessResponse(data=None, msg="删除部门成功")
+    log.info(f"获取部门树形结构成功: enterprise_id={enterprise_id}")
+    return SuccessResponse(data=result, msg="获取部门树形结构成功")

+ 9 - 52
backend/app/plugin/module_payment/department/crud.py

@@ -1,65 +1,22 @@
+from typing import Optional, List, Dict, Any
+
 from app.api.v1.module_system.auth.schema import AuthSchema
 from app.core.base_crud import CRUDBase
-from app.core.exceptions import CustomException
+from app.core.logger import log
 
 from .model import DepartmentModel
-from .schema import DepartmentCreateSchema
+from .schema import DepartmentCreateSchema, DepartmentUpdateSchema
 
 
-class DepartmentCRUD(CRUDBase[DepartmentModel, DepartmentCreateSchema, DepartmentCreateSchema]):
-    """部门 CRUD 操作"""
+class DepartmentCRUD(CRUDBase[DepartmentModel, DepartmentCreateSchema, DepartmentUpdateSchema]):
+    """部门CRUD操作"""
 
-    def __init__(self, auth: AuthSchema) -> None:
+    def __init__(self, auth: AuthSchema):
         self.auth = auth
         super().__init__(model=DepartmentModel, auth=auth)
 
     async def get_by_department_id(
-        self, department_id: str
+        self, department_id: str, enterprise_id: str
     ) -> DepartmentModel | None:
         """根据部门ID查询部门(业务主键)"""
-        return await self.get(department_id=department_id)
-
-    async def get_by_parent_id(
-        self, parent_id: str | None
-    ) -> list[DepartmentModel]:
-        """根据父部门ID查询子部门列表"""
-        if parent_id is None:
-            return await self.get_all(parent_id=None)
-        return await self.get_all(parent_id=parent_id)
-
-    async def get_root_departments(self) -> list[DepartmentModel]:
-        """获取根部门列表(parent_id为空的部门)"""
-        return await self.get_all(parent_id=None)
-
-    async def get_children(self, department_id: str) -> list[DepartmentModel]:
-        """获取子部门列表"""
-        return await self.get_all(parent_id=department_id)
-
-    async def has_children(self, department_id: str) -> bool:
-        """检查部门是否有子部门"""
-        children = await self.get_children(department_id)
-        return len(children) > 0
-
-    async def update_by_department_id(
-        self, department_id: str, data: dict
-    ) -> DepartmentModel | None:
-        """根据部门ID更新部门"""
-        obj = await self.get(department_id=department_id, preload=[])
-        if not obj:
-            raise CustomException(msg="更新失败!对象不存在")
-
-        if self.auth.user and hasattr(obj, "updated_id"):
-            setattr(obj, "updated_id", self.auth.user.id)
-
-        for key, value in data.items():
-            if hasattr(obj, key):
-                setattr(obj, key, value)
-
-        await self.auth.db.flush()
-        await self.auth.db.refresh(obj)
-
-        verify_obj = await self.get(department_id=department_id, preload=[])
-        if not verify_obj:
-            raise CustomException(msg="更新失败!对象不存在或无权限访问")
-
-        return obj
+        return await self.get(department_id=department_id, enterprise_id=enterprise_id)

+ 21 - 24
backend/app/plugin/module_payment/department/model.py

@@ -1,12 +1,11 @@
 from datetime import datetime
+from typing import List, Optional
 
-from sqlalchemy import DateTime, String
-from sqlalchemy.orm import Mapped, mapped_column
+from sqlalchemy import Column, String, Integer, ForeignKey, DateTime, JSON
+from sqlalchemy.orm import Mapped, mapped_column, relationship
 
 from app.common.enums import PermissionFilterStrategy
-from app.core.base_model import PaymentModelMixin, TenantMixin, EnterpriseMixin
-
-from .enums import DepartmentStatusEnum
+from app.core.base_model import EnterpriseMixin, PaymentModelMixin, TenantMixin
 
 
 class DepartmentModel(PaymentModelMixin, TenantMixin, EnterpriseMixin):
@@ -16,22 +15,20 @@ class DepartmentModel(PaymentModelMixin, TenantMixin, EnterpriseMixin):
     __table_args__ = {"comment": "部门表"}
     __permission_strategy__ = PermissionFilterStrategy.ENTERPRISE_BASED
 
-    department_id: Mapped[str | None] = mapped_column(
-        String(64), unique=True, index=True, comment="部门ID(业务主键)"
-    )
-    name: Mapped[str] = mapped_column(String(128), comment="部门名称")
-    code: Mapped[str | None] = mapped_column(String(64), comment="部门编码")
-    parent_id: Mapped[str | None] = mapped_column(
-        String(64), index=True, comment="上级部门ID"
-    )
-    leader_employee_id: Mapped[str | None] = mapped_column(
-        String(64), comment="部门负责人员工ID"
-    )
-    sort_order: Mapped[int] = mapped_column(
-        default=0, comment="排序值(越大越靠前)"
-    )
-    status: Mapped[str] = mapped_column(
-        String(32),
-        default=DepartmentStatusEnum.DEPARTMENT_ACTIVE.value,
-        comment="状态",
-    )
+    id: Mapped[int] = mapped_column(primary_key=True, index=True, comment="主键ID")
+    department_id: Mapped[str] = mapped_column(String(64), unique=True, nullable=False, comment="部门ID")
+    department_name: Mapped[str] = mapped_column(String(128), nullable=False, comment="部门名称")
+    department_code: Mapped[Optional[str]] = mapped_column(String(64), comment="部门编码")
+    parent_department_id: Mapped[Optional[str]] = mapped_column(String(64), comment="上级部门ID")
+    status: Mapped[str] = mapped_column(String(32), default="NORMAL", comment="状态")
+    sort_order: Mapped[Optional[int]] = mapped_column(Integer, comment="排序值")
+    leader_employee_id: Mapped[Optional[str]] = mapped_column(String(64), comment="部门负责人ID")
+    leader_employee_name: Mapped[Optional[str]] = mapped_column(String(128), comment="部门负责人姓名")
+    created_time: Mapped[datetime] = mapped_column(DateTime, default=datetime.now, comment="创建时间")
+    updated_time: Mapped[datetime] = mapped_column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
+
+    # 关联关系
+    parent_department = relationship("DepartmentModel", remote_side=[department_id], backref="sub_departments", primaryjoin="DepartmentModel.parent_department_id == DepartmentModel.department_id")
+
+    def __repr__(self):
+        return f"<DepartmentModel(department_id={self.department_id}, department_name={self.department_name})>"

+ 85 - 38
backend/app/plugin/module_payment/department/schema.py

@@ -1,64 +1,111 @@
 from datetime import datetime
-from typing import Optional
+from typing import Optional, List
 
 from pydantic import BaseModel, ConfigDict, Field
 
 
 class DepartmentCreateSchema(BaseModel):
     """创建部门请求"""
-
-    name: str = Field(max_length=128, description="部门名称")
-    code: Optional[str] = Field(default=None, max_length=64, description="部门编码")
-    parent_id: Optional[str] = Field(default=None, description="上级部门ID")
-    leader_employee_id: Optional[str] = Field(default=None, description="部门负责人员工ID")
-    sort_order: Optional[int] = Field(default=0, description="排序值(越大越靠前)")
+    
+    enterprise_id: str = Field(..., description="企业ID")
+    department_name: str = Field(..., description="部门名称")
+    department_code: Optional[str] = Field(default=None, description="部门编码")
+    parent_department_id: Optional[str] = Field(default=None, description="上级部门ID")
+    sort_order: Optional[int] = Field(default=None, description="排序值")
+    leader_employee_id: Optional[str] = Field(default=None, description="部门负责人ID")
+    leader_employee_name: Optional[str] = Field(default=None, description="部门负责人姓名")
 
 
 class DepartmentUpdateSchema(BaseModel):
     """更新部门请求"""
-
-    name: Optional[str] = Field(default=None, max_length=128, description="部门名称")
-    code: Optional[str] = Field(default=None, max_length=64, description="部门编码")
-    parent_id: Optional[str] = Field(default=None, description="上级部门ID")
-    leader_employee_id: Optional[str] = Field(default=None, description="部门负责人员工ID")
-    sort_order: Optional[int] = Field(default=None, description="排序值(越大越靠前)")
-    status: Optional[str] = Field(default=None, description="状态")
+    
+    department_name: Optional[str] = Field(default=None, description="部门名称")
+    department_code: Optional[str] = Field(default=None, description="部门编码")
+    parent_department_id: Optional[str] = Field(default=None, description="上级部门ID")
+    sort_order: Optional[int] = Field(default=None, description="排序值")
+    leader_employee_id: Optional[str] = Field(default=None, description="部门负责人ID")
+    leader_employee_name: Optional[str] = Field(default=None, description="部门负责人姓名")
 
 
 class DepartmentOutSchema(BaseModel):
     """部门响应模型"""
-
+    
     model_config = ConfigDict(from_attributes=True)
-
+    
     id: int = Field(description="主键ID")
-    department_id: Optional[str] = Field(default=None, description="部门ID(业务主键)")
-    enterprise_id: Optional[str] = Field(default=None, description="企业ID")
-    name: str = Field(description="部门名称")
-    code: Optional[str] = Field(default=None, description="部门编码")
-    parent_id: Optional[str] = Field(default=None, description="上级部门ID")
-    leader_employee_id: Optional[str] = Field(default=None, description="部门负责人员工ID")
-    sort_order: int = Field(description="排序值")
+    department_id: str = Field(description="部门ID")
+    department_name: str = Field(description="部门名称")
+    department_code: Optional[str] = Field(default=None, description="部门编码")
+    parent_department_id: Optional[str] = Field(default=None, description="上级部门ID")
+    enterprise_id: str = Field(description="企业ID")
     status: str = Field(description="状态")
-    created_time: Optional[datetime] = Field(default=None, description="创建时间")
-    updated_time: Optional[datetime] = Field(default=None, description="更新时间")
+    sort_order: Optional[int] = Field(default=None, description="排序值")
+    leader_employee_id: Optional[str] = Field(default=None, description="部门负责人ID")
+    leader_employee_name: Optional[str] = Field(default=None, description="部门负责人姓名")
+    created_time: datetime = Field(description="创建时间")
+    updated_time: datetime = Field(description="更新时间")
+    tenant_id: int = Field(description="租户ID")
 
 
-class DepartmentTreeSchema(BaseModel):
-    """部门树形结构响应"""
+class DepartmentListOutSchema(BaseModel):
+    """部门列表响应"""
+    
+    model_config = ConfigDict(from_attributes=True)
+    
+    id: int = Field(description="主键ID")
+    department_id: str = Field(description="部门ID")
+    department_name: str = Field(description="部门名称")
+    department_code: Optional[str] = Field(default=None, description="部门编码")
+    parent_department_id: Optional[str] = Field(default=None, description="上级部门ID")
+    enterprise_id: str = Field(description="企业ID")
+    status: str = Field(description="状态")
+    sort_order: Optional[int] = Field(default=None, description="排序值")
+    leader_employee_name: Optional[str] = Field(default=None, description="部门负责人姓名")
+    created_time: datetime = Field(description="创建时间")
 
+
+class DepartmentDetailOutSchema(BaseModel):
+    """部门详情响应"""
+    
     model_config = ConfigDict(from_attributes=True)
+    
+    department: DepartmentOutSchema = Field(description="部门信息")
+    sub_departments: List[DepartmentOutSchema] = Field(default_factory=list, description="子部门列表")
+
+
+class DepartmentPageQuery(BaseModel):
+    """部门分页查询参数"""
+    
+    page_no: int = Field(default=1, description="页码")
+    page_size: int = Field(default=20, description="每页数量")
+    enterprise_id: Optional[str] = Field(default=None, description="企业ID")
+    department_name: Optional[str] = Field(default=None, description="部门名称")
+    department_code: Optional[str] = Field(default=None, description="部门编码")
+    parent_department_id: Optional[str] = Field(default=None, description="上级部门ID")
+    status: Optional[str] = Field(default=None, description="状态")
+
+
+class DepartmentOperationOutSchema(BaseModel):
+    """部门操作响应"""
+    
+    department_id: str = Field(description="部门ID")
+    department_name: str = Field(description="部门名称")
 
-    department_id: Optional[str] = Field(default=None, description="部门ID")
-    name: str = Field(description="部门名称")
-    code: Optional[str] = Field(default=None, description="部门编码")
-    parent_id: Optional[str] = Field(default=None, description="上级部门ID")
-    leader_employee_id: Optional[str] = Field(default=None, description="部门负责人员工ID")
-    sort_order: int = Field(description="排序值")
-    status: str = Field(description="状态")
-    children: list["DepartmentTreeSchema"] = Field(default_factory=list, description="子部门")
 
+# 部门状态常量
+DEPARTMENT_STATUS = {
+    "NORMAL": "正常",
+    "DISABLED": "禁用"
+}
 
-class DepartmentMoveSchema(BaseModel):
-    """移动部门请求"""
+# 部门状态标签类型
+DEPARTMENT_STATUS_TAG_TYPE = {
+    "NORMAL": "success",
+    "DISABLED": "warning"
+}
 
-    parent_id: Optional[str] = Field(default=None, description="新的上级部门ID")
+# 部门状态标签
+DEPARTMENT_STATUS_LABEL = {
+    "NORMAL": "正常",
+    "DISABLED": "禁用"
+}

+ 97 - 220
backend/app/plugin/module_payment/department/service.py

@@ -1,257 +1,134 @@
-from typing import Any
+from typing import Optional, List, Dict, Any
+
+from alipay.aop.api.domain.DepartmentInfoDTO import DepartmentInfoDTO
+from alipay.aop.api.domain.AlipayCommerceEcDepartmentCreateModel import AlipayCommerceEcDepartmentCreateModel
+from alipay.aop.api.domain.AlipayCommerceEcDepartmentDeleteModel import AlipayCommerceEcDepartmentDeleteModel
+
+from alipay.aop.api.response.AlipayCommerceEcDepartmentCreateResponse import AlipayCommerceEcDepartmentCreateResponse
+from alipay.aop.api.response.AlipayCommerceEcDepartmentDeleteResponse import AlipayCommerceEcDepartmentDeleteResponse
+
+
+from alipay.aop.api.request.AlipayCommerceEcDepartmentCreateRequest import AlipayCommerceEcDepartmentCreateRequest
+from alipay.aop.api.request.AlipayCommerceEcDepartmentDeleteRequest import AlipayCommerceEcDepartmentDeleteRequest
+from alipay.aop.api.request.AlipayCommerceEcDepartmentInfoModifyRequest import AlipayCommerceEcDepartmentInfoModifyRequest
+from alipay.aop.api.request.AlipayCommerceEcDepartmentInfoQueryRequest import AlipayCommerceEcDepartmentInfoQueryRequest
+from alipay.aop.api.request.AlipayCommerceEcDepartmentSublistQueryRequest import AlipayCommerceEcDepartmentSublistQueryRequest
+
 
 from app.api.v1.module_system.auth.schema import AuthSchema
-from app.core.exceptions import CustomException
+from app.core.alipay import AlipayClient
 from app.core.logger import log
-from app.utils.snowflake import get_snowflake_id_str
+from app.core.exceptions import CustomException
 
 from .crud import DepartmentCRUD
-from .enums import DepartmentStatusEnum
 from .schema import (
     DepartmentCreateSchema,
-    DepartmentOutSchema,
-    DepartmentTreeSchema,
     DepartmentUpdateSchema,
+    DepartmentDetailOutSchema,
+    DepartmentOperationOutSchema
 )
 
 
 class DepartmentService:
-    """部门服务"""
+    """部门服务"""
 
     @classmethod
-    async def create_service(
+    async def create_department_service(
         cls, auth: AuthSchema, data: DepartmentCreateSchema
-    ) -> DepartmentOutSchema:
+    ) -> DepartmentOperationOutSchema:
         """创建部门"""
-        crud = DepartmentCRUD(auth)
+    # 调用支付宝接口创建部门
+        department_create_model = AlipayCommerceEcDepartmentCreateModel()
+        department_create_model.enterprise_id = data.enterprise_id
+        department_create_model.department_name = data.department_name
+        if data.department_code:
+            department_create_model.department_code = data.department_code
+        if data.parent_department_id:
+            department_create_model.parent_department_id = data.parent_department_id or "-1"
+                  
+        client = AlipayClient.get_client()
 
-        department_data = data.model_dump(exclude_none=True)
-        department_data["department_id"] = get_snowflake_id_str(auth.tenant_id)
-        department_data["status"] = DepartmentStatusEnum.DEPARTMENT_ACTIVE.value
-
-        if department_data.get("parent_id"):
-            parent = await crud.get_by_department_id(department_data["parent_id"])
-            if not parent:
-                raise CustomException(msg="父部门不存在")
-            department_data["enterprise_id"] = parent.enterprise_id
-        else:
-            if hasattr(auth, "enterprise_id") and auth.enterprise_id:
-                department_data["enterprise_id"] = auth.enterprise_id
-
-        department = await crud.create(department_data)
-        if not department:
-            raise CustomException(msg="创建部门失败")
-
-        return DepartmentOutSchema.model_validate(department)
-
-    @classmethod
-    async def update_service(
-        cls, auth: AuthSchema, department_id: str, data: DepartmentUpdateSchema
-    ) -> DepartmentOutSchema:
-        """更新部门"""
-        crud = DepartmentCRUD(auth)
+        request = AlipayCommerceEcDepartmentCreateRequest()
+        request.biz_model = department_create_model
 
-        existing = await crud.get_by_department_id(department_id)
-        if not existing:
-            raise CustomException(msg="部门不存在")
+        response = client.execute(request)
 
-        update_data = data.model_dump(exclude_none=True)
-        if update_data.get("parent_id"):
-            if update_data["parent_id"] == department_id:
-                raise CustomException(msg="不能将自己设为父部门")
-            parent = await crud.get_by_department_id(update_data["parent_id"])
-            if not parent:
-                raise CustomException(msg="父部门不存在")
+        if not response:
+            raise CustomException(msg="创建部门失败: 无响应")
 
-        updated = await crud.update_by_department_id(department_id, update_data)
-        if not updated:
-            raise CustomException(msg="更新部门失败")
+        result = AlipayCommerceEcDepartmentCreateResponse()
+        result.parse_response_content(response)
 
-        return DepartmentOutSchema.model_validate(updated)
+        if not result.is_success():
+            log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
+            raise CustomException(msg=f"创建部门失败: {result.sub_code or result.msg or result.code}")
 
-    @classmethod
-    async def delete_service(
-        cls, auth: AuthSchema, department_id: str
-    ) -> None:
-        """删除部门"""
-        crud = DepartmentCRUD(auth)
-
-        existing = await crud.get_by_department_id(department_id)
-        if not existing:
-            raise CustomException(msg="部门不存在")
-
-        if await crud.has_children(department_id):
-            raise CustomException(msg="请先删除子部门")
-
-        await crud.delete(department_id)
-
-    @classmethod
-    async def detail_service(
-        cls, auth: AuthSchema, department_id: str
-    ) -> DepartmentOutSchema:
-        """查询部门详情"""
-        crud = DepartmentCRUD(auth)
-
-        department = await crud.get_by_department_id(department_id)
-        if not department:
-            raise CustomException(msg="部门不存在")
-
-        return DepartmentOutSchema.model_validate(department)
-
-    @classmethod
-    async def list_service(
-        cls,
-        auth: AuthSchema,
-        page_no: int = 1,
-        page_size: int = 100,
-        search: dict | None = None,
-    ) -> dict:
-        """查询部门列表(平铺)"""
+            # 保存到数据库
         crud = DepartmentCRUD(auth)
-        offset = (page_no - 1) * page_size
-        return await crud.page(
-            offset=offset,
-            limit=page_size,
-            order_by=[{"sort_order": "desc"}, {"id": "desc"}],
-            search=search or {},
-            out_schema=DepartmentOutSchema,
+        department_data = {
+            "department_id": result.department_id or "",
+            "department_name": data.department_name,
+            "department_code": data.department_code,
+            "parent_department_id": data.parent_department_id or "-1",
+            "enterprise_id": data.enterprise_id,
+            "sort_order": data.sort_order,
+            "leader_employee_id": data.leader_employee_id,
+            "leader_employee_name": data.leader_employee_name
+        }
+        
+        await crud.create(department_data)
+
+        return DepartmentOperationOutSchema(
+            department_id=result.department_id or "",
+            department_name=data.department_name
         )
 
     @classmethod
-    async def tree_service(cls, auth: AuthSchema) -> list[DepartmentTreeSchema]:
-        """获取部门树形结构"""
-        crud = DepartmentCRUD(auth)
-
-        search = {}
-        if hasattr(auth, "enterprise_id") and auth.enterprise_id:
-            search["enterprise_id"] = auth.enterprise_id
-
-        result = await crud.page(
-            offset=0,
-            limit=1000,
-            order_by=[{"sort_order": "desc"}, {"id": "desc"}],
-            search=search,
-            out_schema=DepartmentOutSchema,
+    async def delete_department_service(
+        cls, auth: AuthSchema, department_id: str, enterprise_id: str
+    ) -> DepartmentOperationOutSchema:
+        return DepartmentOperationOutSchema(
+            department_id=department_id,
+            department_name=""
         )
 
-        items = result.get("items", [])
-        return cls._build_tree(items)
-
     @classmethod
-    def _build_tree(
-        cls, departments: list[dict[str, Any]]
-    ) -> list[DepartmentTreeSchema]:
-        """构建部门树形结构"""
-        department_map = {}
-        roots = []
-
-        for dept in departments:
-            dept_id = dept.get("department_id")
-            if dept_id:
-                department_map[dept_id] = DepartmentTreeSchema(
-                    department_id=dept.get("department_id"),
-                    name=dept["name"],
-                    code=dept.get("code"),
-                    parent_id=dept.get("parent_id"),
-                    leader_employee_id=dept.get("leader_employee_id"),
-                    sort_order=dept.get("sort_order", 0),
-                    status=dept["status"],
-                    children=[],
-                )
-
-        for dept in departments:
-            dept_id = dept.get("department_id")
-            if dept_id and dept_id in department_map:
-                parent_id = dept.get("parent_id")
-                if parent_id and parent_id in department_map:
-                    department_map[parent_id].children.append(
-                        department_map[dept_id]
-                    )
-                else:
-                    roots.append(department_map[dept_id])
-
-        return roots
-
-    @classmethod
-    async def children_service(
-        cls, auth: AuthSchema, department_id: str
-    ) -> list[DepartmentOutSchema]:
-        """获取子部门列表"""
-        crud = DepartmentCRUD(auth)
-
-        children = await crud.get_children(department_id)
-        return [DepartmentOutSchema.model_validate(child) for child in children]
+    async def update_department_service(
+        cls, auth: AuthSchema, department_id: str, enterprise_id: str, data: DepartmentUpdateSchema
+    ) -> DepartmentOperationOutSchema:
+        """更新部门"""
+        return DepartmentOperationOutSchema(
+            department_id=department_id,
+            department_name=""
+        )
 
     @classmethod
-    async def move_service(
-        cls, auth: AuthSchema, department_id: str, parent_id: str | None
-    ) -> DepartmentOutSchema:
-        """移动部门(变更父级)"""
-        if parent_id == department_id:
-            raise CustomException(msg="不能将自己设为父部门")
-
-        if parent_id:
-            crud = DepartmentCRUD(auth)
-            parent = await crud.get_by_department_id(parent_id)
-            if not parent:
-                raise CustomException(msg="目标父部门不存在")
-
-            descendants = await cls._get_all_descendants(department_id)
-            if parent_id in descendants:
-                raise CustomException(msg="不能移动到子部门下方")
-
-        return await cls.update_service(
-            auth, department_id, DepartmentUpdateSchema(parent_id=parent_id)
+    async def get_department_service(
+        cls, auth: AuthSchema, department_id: str, enterprise_id: str
+    ) -> DepartmentDetailOutSchema:
+        """查询部门详情"""
+        return DepartmentDetailOutSchema(
+            department=None,
+            sub_departments=[]
         )
 
     @classmethod
-    async def _get_all_descendants(
-        cls, department_id: str, crud: DepartmentCRUD | None = None
-    ) -> set[str]:
-        """获取部门所有后代ID"""
-        if crud is None:
-            from app.api.v1.module_system.auth.schema import AuthSchema
-            from app.core.request import get_current_auth
-
-            auth = await get_current_auth()
-            crud = DepartmentCRUD(auth)
-
-        descendants = set()
-        children = await crud.get_children(department_id)
-
-        for child in children:
-            if child.department_id:
-                descendants.add(child.department_id)
-                child_descendants = await cls._get_all_descendants(
-                    child.department_id, crud
-                )
-                descendants.update(child_descendants)
-
-        return descendants
+    async def get_sub_departments_service(
+        cls, auth: AuthSchema, parent_department_id: str, enterprise_id: str
+    ) -> List[str]:
+        """查询子部门列表"""
+        return []
 
     @classmethod
-    async def options_service(cls, auth: AuthSchema) -> list[dict]:
-        """获取部门选择选项"""
-        crud = DepartmentCRUD(auth)
-
-        search = {}
-        if hasattr(auth, "enterprise_id") and auth.enterprise_id:
-            search["enterprise_id"] = auth.enterprise_id
-
-        result = await crud.page(
-            offset=0,
-            limit=1000,
-            order_by=[{"sort_order": "desc"}, {"id": "desc"}],
-            search=search,
-            out_schema=DepartmentOutSchema,
-        )
+    async def list_service(
+        cls, auth: AuthSchema, page_no: int, page_size: int, search: Dict[str, Any]
+    ) -> Dict[str, Any]:
+        """分页查询部门列表"""
+        return {}
 
-        items = result.get("items", [])
-        return [
-            {
-                "value": item.department_id,
-                "label": item.name,
-                "disabled": False
-            }
-            for item in items if item.department_id
-        ]
+    @classmethod
+    async def get_department_tree_service(
+        cls, auth: AuthSchema, enterprise_id: str
+    ) -> List[Dict[str, Any]]:
+        """获取部门树形结构"""
+        return []

+ 2 - 2
backend/app/plugin/module_payment/employee/__init__.py

@@ -3,7 +3,7 @@ from .crud import EmployeeCRUD
 from .enums import EmployeeStatusEnum
 from .model import EmployeeModel
 from .schema import (
-    EmployeeCreateSchema,
+    EmployeeCreateOrUpdateSchema,
     EmployeeListOutSchema,
     EmployeeOperationOutSchema,
     EmployeeOutSchema,
@@ -17,7 +17,7 @@ __all__ = [
     "EmployeeCRUD",
     "EmployeeModel",
     "EmployeeStatusEnum",
-    "EmployeeCreateSchema",
+    "EmployeeCreateOrUpdateSchema",
     "EmployeeOutSchema",
     "EmployeeListOutSchema",
     "EmployeeOperationOutSchema",

+ 15 - 12
backend/app/plugin/module_payment/employee/controller.py

@@ -1,4 +1,4 @@
-from typing import Annotated
+from typing import Annotated, Optional
 
 from fastapi import APIRouter, Depends, Path, Query
 from fastapi.responses import JSONResponse
@@ -12,7 +12,7 @@ from app.core.router_class import OperationLogRoute
 from alipay.aop.api.domain.EmployeeInfoDTO import EmployeeInfoDTO
 
 from .schema import (
-    EmployeeCreateSchema,
+    EmployeeCreateOrUpdateSchema,
     EmployeeListOutSchema,
     EmployeeOperationOutSchema,
     EmployeeOutSchema,
@@ -34,13 +34,15 @@ EmployeeRouter = APIRouter(
     description="查询员工详情",
 )
 async def info_employee_controller(
-        auth: Annotated[AuthSchema, Depends(get_current_user)],
+    enterprise_id: Annotated[str, Query(description="企业ID")],
+    auth: Annotated[AuthSchema, Depends(get_current_user)],
+    employee_id: Annotated[Optional[str], Query(description="员工ID")] = None,
+    employee_email: Annotated[Optional[str], Query(description="员工邮箱")] = None,
+    employee_mobile: Annotated[Optional[str], Query(description="员工手机号")] = None,
 ) -> JSONResponse:
     """查询员工详情"""
-    # result = await EmployeeService.info_service(auth=auth)
-    # return SuccessResponse(data=result, msg="查询员工详情成功")
-    pass
-
+    result = await EmployeeService.info_service(auth=auth, employee_id=employee_id, employee_email=employee_email, employee_mobile=employee_mobile, enterprise_id=enterprise_id)
+    return SuccessResponse(data=result, msg="查询员工详情成功")
 
 
 @EmployeeRouter.post(
@@ -50,7 +52,7 @@ async def info_employee_controller(
     response_model=ResponseSchema[EmployeeOperationOutSchema],
 )
 async def add_employee_controller(
-    data: EmployeeCreateSchema,
+    data: EmployeeCreateOrUpdateSchema,
     auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:employee:create"]))],
 ) -> JSONResponse:
     """添加员工"""
@@ -92,18 +94,19 @@ async def list_employee_controller(
 
 
 @EmployeeRouter.get(
-    "/{employee_id}",
+    "/detail",
     summary="查询员工详情",
     description="根据 employee_id 查询员工详情",
-    # response_model=ResponseSchema[Any],
 )
 async def get_detail_controller(
-    employee_id: Annotated[str, Path(description="员工ID")],
     enterprise_id: Annotated[str, Query(description="企业ID")],
     auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:employee:detail"]))],
+    employee_id: Annotated[Optional[str], Query(description="员工ID")] = None,
+    employee_email: Annotated[Optional[str], Query(description="员工邮箱")] = None,
+    employee_mobile: Annotated[Optional[str], Query(description="员工手机号")] = None,
 ) -> JSONResponse:
     """查询员工详情"""
-    result = await EmployeeService.detail_service(auth=auth, employee_id=employee_id, enterprise_id=enterprise_id)
+    result = await EmployeeService.detail_service(auth=auth, employee_id=employee_id, employee_email=employee_email, employee_mobile=employee_mobile, enterprise_id=enterprise_id)
     log.info(f"查询员工详情成功: {employee_id}, enterprise_id={enterprise_id}")
     return SuccessResponse(data=result, msg="查询员工详情成功")
 

+ 26 - 17
backend/app/plugin/module_payment/employee/crud.py

@@ -1,12 +1,14 @@
+from typing import Optional
+
 from app.api.v1.module_system.auth.schema import AuthSchema
 from app.core.base_crud import CRUDBase
 from app.core.exceptions import CustomException
 
 from .model import EmployeeModel
-from .schema import EmployeeCreateSchema, EmployeeOutSchema
+from .schema import EmployeeCreateOrUpdateSchema, EmployeeOutSchema
 
 
-class EmployeeCRUD(CRUDBase[EmployeeModel, EmployeeCreateSchema, EmployeeCreateSchema]):
+class EmployeeCRUD(CRUDBase[EmployeeModel, EmployeeCreateOrUpdateSchema, EmployeeCreateOrUpdateSchema]):
     """员工 CRUD 操作"""
 
     def __init__(self, auth: AuthSchema) -> None:
@@ -14,16 +16,21 @@ class EmployeeCRUD(CRUDBase[EmployeeModel, EmployeeCreateSchema, EmployeeCreateS
         super().__init__(model=EmployeeModel, auth=auth)
 
     async def get_by_employee_id(
-        self, employee_id: str
+        self, employee_id: str, enterprise_id: str | None = None
     ) -> EmployeeModel | None:
         """根据员工ID查询员工(业务主键)"""
-        return await self.get(employee_id=employee_id)
-
-    async def update_by_employee_id(
-        self, employee_id: str, data: dict
+        return await self.get(employee_id=employee_id, enterprise_id=enterprise_id)
+
+    async def update_by(
+        self, 
+        employee_mobile: Optional[str] = None, 
+        employee_email: Optional[str] = None, 
+        identity_open_id: Optional[str] = None, 
+        data: dict = {},
     ) -> EmployeeModel | None:
-        """根据员工ID更新员工(业务主键)"""
-        obj = await self.get(employee_id=employee_id, preload=[])
+        """根据员工手机号或邮箱更新员工(业务主键)"""
+        print(data)
+        obj = await self.get(employee_mobile=employee_mobile, employee_email=employee_email, identity_open_id=identity_open_id, preload=[])
         if not obj:
             raise CustomException(msg="更新失败!对象不存在")
 
@@ -37,25 +44,27 @@ class EmployeeCRUD(CRUDBase[EmployeeModel, EmployeeCreateSchema, EmployeeCreateS
         await self.auth.db.flush()
         await self.auth.db.refresh(obj)
 
-        verify_obj = await self.get(employee_id=employee_id, preload=[])
-        if not verify_obj:
-            raise CustomException(msg="更新失败!对象不存在或无权限访问")
+        # verify_obj = await self.get(employee_id=obj.employee_id, enterprise_id=obj.enterprise_id, preload=[])
+        # if not verify_obj:
+        #     raise CustomException(msg="更新失败!对象不存在或无权限访问")
 
         return obj
 
-    async def delete_by_employee_id(self, employee_id: str) -> None:
+    async def delete_by_employee_id(
+        self, employee_id: str, *, enterprise_id: str | None = None
+    ) -> None:
         """根据员工ID将员工状态更新为解约(业务主键)"""
         from .enums import EmployeeStatusEnum
 
-        obj = await self.get(employee_id=employee_id, preload=[])
+        obj = await self.get(employee_id=employee_id, enterprise_id=enterprise_id, preload=[])
         if not obj:
             return  # 员工不存在,直接返回
 
-        # 更新状态为解约
-        obj.status = EmployeeStatusEnum.EMPLOYEE_UNSIGN.value
-
         if self.auth.user and hasattr(obj, "updated_id"):
             setattr(obj, "updated_id", self.auth.user.id)
 
+        # 直接删除
+        await self.auth.db.delete(obj)
+
         await self.auth.db.flush()
         await self.auth.db.refresh(obj)

+ 8 - 1
backend/app/plugin/module_payment/employee/model.py

@@ -48,7 +48,7 @@ class EmployeeModel(PaymentModelMixin, TenantMixin, EnterpriseMixin):
     )
     status: Mapped[str] = mapped_column(
         String(32), 
-        default=None, 
+        default="EMPLOYEE_CREATE",
         comment="状态: EMPLOYEE_CREATE/EMPLOYEE_ACTIVATED/EMPLOYEE_UNSIGN"
     )
     iot_check_type: Mapped[str | None] = mapped_column(
@@ -92,6 +92,13 @@ class EmployeeModel(PaymentModelMixin, TenantMixin, EnterpriseMixin):
     free_sign_token: Mapped[str | None] = mapped_column(
         String(256), comment="免登签约token"
     )
+
+    department_list: Mapped[JSON | None] = mapped_column(
+        JSON, comment="部门列表"
+    ) 
+    role_list: Mapped[JSON | None] = mapped_column(
+        JSON, comment="角色列表"
+    )
     
     user_id: Mapped[int | None] = mapped_column(
         Integer,

+ 7 - 3
backend/app/plugin/module_payment/employee/schema.py

@@ -4,7 +4,7 @@ from typing import Optional
 from pydantic import BaseModel, ConfigDict, Field, model_validator
 
 
-class EmployeeCreateSchema(BaseModel):
+class EmployeeCreateOrUpdateSchema(BaseModel):
     """
     创建员工请求
 
@@ -16,7 +16,7 @@ class EmployeeCreateSchema(BaseModel):
     - identity: 身份标识
     """
 
-    enterprise_id: Optional[str] = Field(default=None, description="企业ID")
+    enterprise_id: str = Field(description="企业ID")
     employee_id: Optional[str] = Field(default=None, description="员工ID")
     employee_name: Optional[str] = Field(default=None, max_length=128, description="员工姓名")
     employee_no: Optional[str] = Field(default=None, max_length=64, description="员工工号")
@@ -35,6 +35,10 @@ class EmployeeCreateSchema(BaseModel):
     create_share_code: Optional[bool] = Field(default=False, description="是否创建分享码")
     sign_url_carry_info: Optional[bool] = Field(default=False, description="签约链接是否携带信息")
     profiles: Optional[dict] = Field(default=None, description="扩展参数")
+    status: Optional[str] = Field(default=None, description="状态")
+
+    department_list: Optional[list] = Field(default=None, description="部门列表")
+    role_list: Optional[list] = Field(default=None, description="角色列表")
 
 
 class EmployeeOutSchema(BaseModel):
@@ -62,7 +66,7 @@ class EmployeeOutSchema(BaseModel):
     sign_return_url: Optional[str] = Field(default=None, description="签约跳转URL")
     create_share_code: Optional[bool] = Field(default=None, description="是否创建分享码")
     sign_url_carry_info: Optional[bool] = Field(default=None, description="签约链接是否携带信息")
-    invite_url: Optional[str] = Field(default=None, description="邀请链接")
+    sign_url: Optional[str] = Field(default=None, description="邀请链接")
     share_code: Optional[str] = Field(default=None, description="分享码")
     profiles: Optional[dict] = Field(default=None, description="扩展参数")
     withholding_sign_str: Optional[str] = Field(default=None, description="代扣签约串")

+ 65 - 12
backend/app/plugin/module_payment/employee/service.py

@@ -1,4 +1,4 @@
-from typing import cast
+from typing import Optional, cast
 
 from app.api.v1.module_system.auth.schema import AuthSchema
 from app.api.v1.module_system.user.crud import UserCRUD
@@ -10,11 +10,12 @@ from app.utils.hash_bcrpy_util import PwdUtil
 
 from .crud import EmployeeCRUD
 from .schema import (
-    EmployeeCreateSchema,
+    EmployeeCreateOrUpdateSchema,
     EmployeeListOutSchema,
     EmployeeOperationOutSchema,
     EmployeeInviteQuerySchema,
     EmployeeInviteQueryOutSchema,
+    EmployeeOutSchema,
 )
 
 from alipay.aop.api.domain.EmployeeInfoDTO import EmployeeInfoDTO
@@ -24,7 +25,7 @@ class EmployeeService:
 
     @classmethod
     async def add_employee_service(
-        cls, auth: AuthSchema, data: EmployeeCreateSchema
+        cls, auth: AuthSchema, data: EmployeeCreateOrUpdateSchema
     ) -> EmployeeOperationOutSchema:
         """
         添加员工
@@ -42,7 +43,7 @@ class EmployeeService:
         from alipay.aop.api.response.AlipayCommerceEcEmployeeAddResponse import (
             AlipayCommerceEcEmployeeAddResponse,
         )
-
+  
         model = AlipayCommerceEcEmployeeAddModel()
         # 必选
         model.enterprise_id = data.enterprise_id
@@ -81,7 +82,7 @@ class EmployeeService:
 
         if not result.is_success():
             log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
-            raise CustomException(msg=f"添加员工失败: {result.msg}")
+            raise CustomException(msg=f"添加员工失败: {result.sub_msg or result.msg or result.code}")
 
         result_data = EmployeeOperationOutSchema(
             employee_id=result.employee_id,
@@ -107,8 +108,8 @@ class EmployeeService:
                 mobile=data.employee_mobile,
                 email=data.employee_email,
                 tenant_id=auth.tenant_id,
-                role_ids=[11],
-                status="0",  # 启用状态
+                # role_ids=[11],
+                # status="0",  # 启用状态
                 description=f"e:{data.enterprise_id}:{result.employee_id}"
             )
             user_dict = user_data.model_dump(exclude_unset=True)
@@ -142,11 +143,21 @@ class EmployeeService:
             out_schema=EmployeeListOutSchema,
             preload=["user"]
         )
+    
+    @classmethod
+    async def info_service(
+        cls, auth: AuthSchema, employee_id: Optional[str], employee_email: Optional[str], employee_mobile: Optional[str], enterprise_id: str
+    ) -> EmployeeOutSchema:
+        crud = EmployeeCRUD(auth)
+        out_data = await crud.get(employee_id=employee_id, employee_email=employee_email, employee_mobile=employee_mobile, enterprise_id=enterprise_id)
+        if not out_data:
+            raise CustomException(msg="员工不存在")
+        return EmployeeOutSchema.model_validate(out_data)
 
     @classmethod
     async def detail_service(
-        cls, auth: AuthSchema, employee_id: str, enterprise_id: str
-    ):
+        cls, auth: AuthSchema, employee_id: Optional[str], employee_email: Optional[str], employee_mobile: Optional[str], enterprise_id: str
+    ) -> dict:
         """
         查询员工详情
 
@@ -159,6 +170,8 @@ class EmployeeService:
         model = AlipayCommerceEcEmployeeInfoQueryModel()
         model.enterprise_id = enterprise_id
         model.employee_id = employee_id
+        model.employee_email = employee_email
+        model.mobile = employee_mobile
 
         request = AlipayCommerceEcEmployeeInfoQueryRequest()
         request.biz_model = model
@@ -176,7 +189,7 @@ class EmployeeService:
             log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
             raise CustomException(msg=f"查询员工详情失败: {result.msg}")
 
-        return cast(EmployeeInfoDTO, result.employee_info)
+        return EmployeeInfoDTO.to_alipay_dict(cast(EmployeeInfoDTO, result.employee_info))
 
     @classmethod
     async def delete_employee_service(
@@ -209,10 +222,20 @@ class EmployeeService:
 
         if not result.is_success():
             log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
-            raise CustomException(msg=f"删除员工失败: {result.msg}")
+            raise CustomException(msg=f"删除员工失败: {result.sub_msg or result.msg or result.code}")
 
-        # 从本地数据库删除
+        # 从本地数据库删除, 并删除关联的用户
+        # 先查询员工是否存在
         crud = EmployeeCRUD(auth)
+        employee = await crud.get(employee_id=employee_id, enterprise_id=enterprise_id, preload=["user"])
+        if not employee:
+            raise CustomException(msg=f"员工 {employee_id} 不存在")
+
+        # 先删除关联的用户
+        if employee.user_id:
+            user_service = UserCRUD(auth)
+            await user_service.delete(ids=[employee.user_id])
+    
         await crud.delete_by_employee_id(employee_id)
 
         return EmployeeOperationOutSchema(
@@ -261,3 +284,33 @@ class EmployeeService:
             mini_app_sign_url=result.mini_app_sign_url or "",
             share_code=getattr(result, 'share_code', None)
         )
+
+
+    @classmethod
+    async def update_employee_from_alipay(cls, auth: AuthSchema, data: EmployeeCreateOrUpdateSchema):   
+        """
+        从支付宝更新员工信息
+        """        
+        # 先查询支付宝员工信息
+        employee = await cls.detail_service(
+            auth=auth, 
+            employee_id=data.employee_id, 
+            employee_email=data.employee_email, 
+            employee_mobile=data.employee_mobile, 
+            enterprise_id=data.enterprise_id
+        )
+
+        if not employee :
+            raise CustomException(msg=f"员工 {data.employee_id} 不存在")
+        if hasattr(employee, 'employee_id') and employee['employee_id'] != data.employee_id:
+            raise CustomException(msg=f"员工 {data.employee_id} 不存在")
+
+        crud = EmployeeCRUD(auth)
+        await crud.update_by(
+            employee_mobile=data.employee_mobile, 
+            employee_email=data.employee_email, 
+            identity_open_id=data.identity_open_id, 
+            data=data.model_dump(exclude_none=True)
+        )
+    
+

+ 3 - 3
backend/app/plugin/module_payment/notification/enums.py

@@ -21,11 +21,11 @@ class EmployeeActionEnum(str, Enum):
     """员工变更动作枚举"""
 
     EMPLOYEE_ADD = "EMPLOYEE_ADD"
-    EMPLOYEE_ACTIVATE = "EMPLOYEE_ACTIVATE"
-    EMPLOYEE_UPDATE = "EMPLOYEE_UPDATE"
+    EMPLOYEE_ACTIVATE = "EMPLOYEE_ACTIVATED"
+    EMPLOYEE_UPDATE = "EMPLOYEE_MODIFY"
     EMPLOYEE_DEPARTMENT_CHANGE = "EMPLOYEE_DEPARTMENT_CHANGE"
     EMPLOYEE_ROLE_CHANGE = "EMPLOYEE_ROLE_CHANGE"
-    EMPLOYEE_DELETE = "EMPLOYEE_DELETE"
+    EMPLOYEE_DELETE = "EMPLOYEE_LEAVE"
 
 
 class FundChangeTypeEnum(str, Enum):

+ 27 - 2
backend/app/plugin/module_payment/notification/handlers/employee_handler.py

@@ -2,6 +2,8 @@ from redis.asyncio import Redis
 
 from app.api.v1.module_system.auth.schema import AuthSchema
 from app.core.logger import log
+from app.plugin.module_payment.employee.schema import EmployeeCreateOrUpdateSchema
+from app.plugin.module_payment.employee.service import EmployeeService
 
 from ..schemas import EmployeeChangeContent
 from ..enums import EmployeeActionEnum
@@ -11,6 +13,23 @@ from .base_handler import BaseHandler
 class EmployeeHandler(BaseHandler[dict]):
     """员工变更通知处理器"""
 
+    async def update_employee(self, data: EmployeeChangeContent, auth: AuthSchema):
+        """更新员工信息"""
+
+        update_data = EmployeeCreateOrUpdateSchema(
+            enterprise_id=data.enterprise_id,
+            employee_id=data.employee_id,
+            employee_name=data.employee_name,
+            employee_mobile=data.mobile,
+            status=data.activate,
+            employee_email=data.email,
+            identity_open_id=data.open_id,
+            department_list=data.department_list,
+            role_list=data.role_list,
+        )
+
+        await EmployeeService.update_employee_from_alipay(auth, update_data)
+
     async def handle(self, method: str, content: dict, auth: AuthSchema, redis: Redis) -> bool:
         """
         处理员工变更通知
@@ -57,29 +76,35 @@ class EmployeeHandler(BaseHandler[dict]):
     async def _handle_add(self, data: EmployeeChangeContent, auth: AuthSchema) -> bool:
         """处理员工新增"""
         log.info(f"员工新增: employee_id={data.employee_id}, enterprise_id={data.enterprise_id}")
+        await self.update_employee(data, auth)
         return True
 
     async def _handle_activate(self, data: EmployeeChangeContent, auth: AuthSchema) -> bool:
         """处理员工激活"""
         log.info(f"员工激活: employee_id={data.employee_id}")
+        await self.update_employee(data, auth)
         return True
 
     async def _handle_update(self, data: EmployeeChangeContent, auth: AuthSchema) -> bool:
         """处理员工信息修改"""
         log.info(f"员工信息修改: employee_id={data.employee_id}")
+        await self.update_employee(data, auth)
         return True
 
     async def _handle_department_change(self, data: EmployeeChangeContent, auth: AuthSchema) -> bool:
         """处理部门修改"""
-        log.info(f"部门修改: employee_id={data.employee_id}, department_id={data.department_id}")
+        log.info(f"部门修改: employee_id={data.employee_id}")
+        await self.update_employee(data, auth)
         return True
 
     async def _handle_role_change(self, data: EmployeeChangeContent, auth: AuthSchema) -> bool:
         """处理角色修改"""
-        log.info(f"角色修改: employee_id={data.employee_id}, role_id={data.role_id}")
+        log.info(f"角色修改: employee_id={data.employee_id}")
+        await self.update_employee(data, auth)
         return True
 
     async def _handle_delete(self, data: EmployeeChangeContent, auth: AuthSchema) -> bool:
         """处理员工删除"""
         log.info(f"员工删除: employee_id={data.employee_id}")
+        # await self.update_employee(data, auth)
         return True

+ 15 - 9
backend/app/plugin/module_payment/notification/schemas.py

@@ -69,17 +69,23 @@ class EnterpriseChangeContent(BaseModel):
 
 
 class EmployeeChangeContent(BaseModel):
-    """员工变更通知内容"""
+    """
+    员工变更通知内容
+     {'gmt_create': '2026-04-24 11:15:49', 'role_list': ['USER'], 'mobile': '13874410370', 'employee_name': '胡森林', 'gmt_modified': '2026-04-24 11:15:49', 'ext_info': '{}', 'enterprise_id': '2088480767913636', 'iot_face_status': '2', 'department_list': [{'department_id': '1001163032541324', 'department_name': '湖南花米惠科技有限公司'}], 'employee_id': '2300063240699375', 'activate': 'UNACTIVATED', 'action': 'EMPLOYEE_ADD', 'change_time': '2026-04-24 11:15:49'}
+    """
 
     enterprise_id: str = Field(..., description="企业id")
-    out_biz_no: str = Field(..., description="外部业务号")
-    action: str = Field(..., description="变更动作")
-    change_time: str = Field(..., description="变更时间")
-    employee_id: str | None = Field(None, description="员工id")
-    employee_identity: str | None = Field(None, description="员工身份标识")
-    department_id: str | None = Field(None, description="部门id")
-    role_id: str | None = Field(None, description="角色id")
-    remark: str | None = Field(None, description="备注")
+    employee_id: str = Field(..., description="员工id")
+    action: str | None = Field(None, description="变更动作")
+    change_time: str | None = Field(None, description="变更时间")
+    activate: str | None = Field(None, description="激活状态")
+    department_list: list | None = Field(None, description="部门列表")
+    role_list: list | None = Field(None, description="角色列表")
+    mobile: str | None = Field(None, description="手机号")
+    email: str | None = Field(None, description="邮箱")
+    open_id: str | None = Field(None, description="open_id")
+    employee_name: str | None = Field(None, description="员工姓名")
+    iot_face_status: str | None = Field(None, description="人脸状态状态")
 
 
 class VoucherChangeContent(BaseModel):

+ 11 - 11
backend/app/scripts/data/sys_param.json

@@ -2,7 +2,7 @@
   {
     "config_name": "网站名称",
     "config_key": "sys_web_title",
-    "config_value": "FastApiAdmin",
+    "config_value": "公司管理",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -10,7 +10,7 @@
   {
     "config_name": "网站描述",
     "config_key": "sys_web_description",
-    "config_value": "FastApiAdmin 是完全开源的权限管理系统",
+    "config_value": "公司管理",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -18,7 +18,7 @@
   {
     "config_name": "网页图标",
     "config_key": "sys_web_favicon",
-    "config_value": "https://service.fastapiadmin.com/api/v1/static/image/favicon.png",
+    "config_value": "",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -26,7 +26,7 @@
   {
     "config_name": "网站Logo",
     "config_key": "sys_web_logo",
-    "config_value": "https://service.fastapiadmin.com/api/v1/static/image/logo.png",
+    "config_value": "",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -42,7 +42,7 @@
   {
     "config_name": "版权信息",
     "config_key": "sys_web_copyright",
-    "config_value": "Copyright © 2025-2026 service.fastapiadmin.com 版权所有",
+    "config_value": "Copyright © 2025-2026 公司管理 版权所有",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -50,7 +50,7 @@
   {
     "config_name": "备案信息",
     "config_key": "sys_keep_record",
-    "config_value": "陕ICP备2025069493号-1",
+    "config_value": "",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -58,7 +58,7 @@
   {
     "config_name": "帮助文档",
     "config_key": "sys_help_doc",
-    "config_value": "https://service.fastapiadmin.com",
+    "config_value": "",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -66,7 +66,7 @@
   {
     "config_name": "隐私政策",
     "config_key": "sys_web_privacy",
-    "config_value": "https://github.com/fastapiadmin/FastapiAdmin/blob/master/LICENSE",
+    "config_value": "",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -74,7 +74,7 @@
   {
     "config_name": "用户协议",
     "config_key": "sys_web_clause",
-    "config_value": "https://github.com/fastapiadmin/FastapiAdmin/blob/master/LICENSE",
+    "config_value": "",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -82,7 +82,7 @@
   {
     "config_name": "源码代码",
     "config_key": "sys_git_code",
-    "config_value": "https://github.com/fastapiadmin/FastapiAdmin.git",
+    "config_value": "",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"
@@ -90,7 +90,7 @@
   {
     "config_name": "项目版本",
     "config_key": "sys_web_version",
-    "config_value": "2.0.0",
+    "config_value": "1.0.0",
     "config_type": true,
     "status": "0",
     "description": "初始化数据"

+ 52 - 0
docker-compose.yml

@@ -0,0 +1,52 @@
+version: '3.8'
+
+services:
+  backend:
+    build:
+      context: ./backend
+    ports:
+      - "8000:8000"
+    environment:
+      - DATABASE_URL=postgresql://admin:password@database:5432/example_db
+      - REDIS_URL=redis://redis:6379/0
+    depends_on:
+      - database
+      - redis
+    networks:
+      - app-network
+
+  frontend:
+    build:
+      context: ./frontend
+    ports:
+      - "80:80"
+    depends_on:
+      - backend
+    networks:
+      - app-network
+
+  database:
+    image: postgres:13
+    environment:
+      - POSTGRES_USER=admin
+      - POSTGRES_PASSWORD=password
+      - POSTGRES_DB=example_db
+    volumes:
+      - postgres_data:/var/lib/postgresql/data
+    networks:
+      - app-network
+
+  redis:
+    image: redis:6
+    volumes:
+      - redis_data:/data
+    networks:
+      - app-network
+
+networks:
+  app-network:
+    driver: bridge
+
+volumes:
+  postgres_data:
+  redis_data:

+ 21 - 0
frontend/Dockerfile

@@ -0,0 +1,21 @@
+FROM node:18-alpine AS build
+
+WORKDIR /app
+
+COPY package.json pnpm-lock.yaml ./
+
+RUN npm install -g pnpm && pnpm install
+
+COPY . .
+
+RUN pnpm build
+
+FROM nginx:alpine AS runtime
+
+COPY --from=build /app/dist /usr/share/nginx/html
+
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+
+EXPOSE 80
+
+CMD ["nginx", "-g", "daemon off;"]

+ 182 - 105
frontend/src/api/module_payment/department.ts

@@ -1,141 +1,218 @@
-import request from '@/utils/request';
+import request from "@/utils/request";
 
-// 部门状态枚举
-export enum DepartmentStatus {
-  ACTIVE = 'DEPARTMENT_ACTIVE',
-  INACTIVE = 'DEPARTMENT_INACTIVE'
-}
+const API_PATH = "/payment/department";
 
 // 部门状态标签类型
-export const DEPARTMENT_STATUS_TAG_TYPE = {
-  [DepartmentStatus.ACTIVE]: 'success',
-  [DepartmentStatus.INACTIVE]: 'danger'
+export const DEPARTMENT_STATUS_TAG_TYPE: Record<string, string> = {
+  NORMAL: "success",
+  DISABLED: "warning"
+};
+
+// 部门状态标签
+export const DEPARTMENT_STATUS_LABEL: Record<string, string> = {
+  NORMAL: "正常",
+  DISABLED: "禁用"
 };
 
-// 部门状态标签文本
-export const DEPARTMENT_STATUS_LABEL = {
-  [DepartmentStatus.ACTIVE]: '激活',
-  [DepartmentStatus.INACTIVE]: '禁用'
+// 部门状态枚举
+export enum DepartmentStatus {
+  ACTIVE = "NORMAL",
+  DISABLED = "DISABLED"
 };
 
-// 部门查询参数
+// 部门列表查询参数
 export interface DepartmentPageQuery {
   page_no?: number;
   page_size?: number;
   enterprise_id?: string;
-  name?: string;
-  code?: string;
+  department_name?: string;
+  department_code?: string;
+  parent_department_id?: string;
   status?: string;
 }
 
-// 部门表单数据
-export interface DepartmentForm {
-  name: string;
-  code: string;
-  parent_id?: string;
-  leader_employee_id?: string;
+// 部门信息
+export interface DepartmentInfo {
+  id: number;
+  department_id: string;
+  department_name: string;
+  department_code?: string;
+  parent_department_id?: string;
+  enterprise_id: string;
+  status: string;
   sort_order?: number;
-  status?: string;
+  leader_employee_id?: string;
+  leader_employee_name?: string;
+  created_time: string;
+  updated_time: string;
+  tenant_id: number;
 }
 
 // 部门列表项
 export interface DepartmentTable {
   id: number;
   department_id: string;
-  name: string;
-  code: string;
-  parent_id?: string;
-  leader_employee_id?: string;
-  leader_employee_name?: string;
-  sort_order: number;
+  department_name: string;
+  department_code?: string;
+  parent_department_id?: string;
+  enterprise_id: string;
   status: string;
+  sort_order?: number;
+  leader_employee_name?: string;
   created_time: string;
-  updated_time: string;
-  children?: DepartmentTable[];
 }
 
-// 部门选择选项
-export interface DepartmentOption {
-  value: string;
+// 部门树形节点
+export interface DepartmentTreeNode {
+  id: string;
   label: string;
-  disabled?: boolean;
+  code?: string;
+  sort_order?: number;
+  leader_employee_name?: string;
+  children?: DepartmentTreeNode[];
 }
 
-class DepartmentAPI {
-  /**
-   * 获取部门列表
-   */
-  listDepartment(params: DepartmentPageQuery) {
-    return request({
-      url: '/payment/department',
-      method: 'get',
-      params
+// 部门操作响应
+export interface DepartmentOperation {
+  department_id: string;
+  department_name: string;
+}
+
+// 部门详情响应
+export interface DepartmentDetail {
+  department: DepartmentInfo;
+  sub_departments: DepartmentInfo[];
+}
+
+// 部门API
+export default {
+  // 创建部门
+  createDepartment(data: {
+    enterprise_id: string;
+    department_name: string;
+    department_code?: string;
+    parent_department_id?: string;
+    sort_order?: number;
+    leader_employee_id?: string;
+    leader_employee_name?: string;
+  }) {
+    return request<ApiResponse<DepartmentOperation>>({
+      url: API_PATH,
+      method: "post",
+      data,
     });
-  }
-
-  /**
-   * 获取部门树形结构
-   */
-  getDepartmentTree(params: { enterprise_id?: string }) {
-    return request({
-      url: '/payment/department/tree',
-      method: 'get',
-      params
+  },
+
+  // 删除部门
+  deleteDepartment(department_id: string, enterprise_id: string) {
+    return request<ApiResponse<DepartmentOperation>>({
+      url: `${API_PATH}/${department_id}`,
+      method: "delete",
+      params: {
+        enterprise_id,
+      },
     });
-  }
-
-  /**
-   * 获取部门详情
-   */
-  getDepartmentDetail(departmentId: string) {
-    return request({
-      url: `/payment/department/${departmentId}`,
-      method: 'get'
+  },
+
+  // 更新部门
+  updateDepartment(department_id: string, enterprise_id: string, data: {
+    department_name?: string;
+    department_code?: string;
+    parent_department_id?: string;
+    sort_order?: number;
+    leader_employee_id?: string;
+    leader_employee_name?: string;
+  }) {
+    return request<ApiResponse<DepartmentOperation>>({
+      url: `${API_PATH}/${department_id}`,
+      method: "put",
+      params: {
+        enterprise_id,
+      },
+      data,
     });
-  }
-
-  /**
-   * 创建部门
-   */
-  createDepartment(data: DepartmentForm) {
-    return request({
-      url: '/payment/department',
-      method: 'post',
-      data
+  },
+
+  // 查询部门详情
+  getDepartmentDetail(department_id: string, enterprise_id: string) {
+    return request<ApiResponse<DepartmentDetail>>({
+      url: `${API_PATH}/${department_id}`,
+      method: "get",
+      params: {
+        enterprise_id,
+      },
     });
-  }
-
-  /**
-   * 更新部门
-   */
-  updateDepartment(departmentId: string, data: DepartmentForm) {
-    return request({
-      url: `/payment/department/${departmentId}`,
-      method: 'put',
-      data
+  },
+
+  // 获取部门选项
+  getDepartmentOptions(params: {
+    enterprise_id: string;
+  }) {
+    return request<ApiResponse<any[]>>({ 
+      url: `${API_PATH}/tree/list`,
+      method: "get",
+      params,
+    }).then(response => {
+      // 转换数据结构以匹配前端选择器
+      if (response.data.code === 200 && response.data.data) {
+        const convertTreeToOptions = (tree: any[], level = 0): any[] => {
+          const options: any[] = [];
+          const indent = '  '.repeat(level);
+          
+          tree.forEach(node => {
+            options.push({
+              value: node.id,
+              label: `${indent}${node.label}`,
+              disabled: false
+            });
+            
+            if (node.children && node.children.length > 0) {
+              options.push(...convertTreeToOptions(node.children, level + 1));
+            }
+          });
+          
+          return options;
+        };
+        
+        response.data.data = convertTreeToOptions(response.data.data);
+      }
+      return response;
     });
-  }
-
-  /**
-   * 删除部门
-   */
-  deleteDepartment(departmentId: string) {
-    return request({
-      url: `/payment/department/${departmentId}`,
-      method: 'delete'
+  },
+
+  // 查询子部门列表
+  getSubDepartments(department_id: string, enterprise_id: string) {
+    return request<ApiResponse<string[]>>({
+      url: `${API_PATH}/${department_id}/sub`,
+      method: "get",
+      params: {
+        enterprise_id,
+      },
     });
-  }
-
-  /**
-   * 获取部门选择选项
-   */
-  getDepartmentOptions(params: { enterprise_id?: string }) {
-    return request({
-      url: '/payment/department/options',
-      method: 'get',
-      params
+  },
+
+  // 分页查询部门列表
+  listDepartment(query: DepartmentPageQuery) {
+    return request<ApiResponse<{
+      items: DepartmentTable[];
+      total: number;
+      page_no: number;
+      page_size: number;
+    }>> ({
+      url: API_PATH,
+      method: "get",
+      params: query,
     });
-  }
-}
+  },
 
-export default new DepartmentAPI();
+  // 获取部门树形结构
+  getDepartmentTree(enterprise_id: string) {
+    return request<ApiResponse<DepartmentTreeNode[]>>({
+      url: `${API_PATH}/tree/list`,
+      method: "get",
+      params: {
+        enterprise_id,
+      },
+    });
+  },
+};

+ 48 - 22
frontend/src/api/module_payment/employee.ts

@@ -11,10 +11,36 @@ const EmployeeAPI = {
     });
   },
 
-  detailEmployee(employee_id: string) {
+  detailEmployee(enterprise_id: string, employee_id?: string, employee_email?: string, employee_mobile?: string) {
     return request<ApiResponse<EmployeeDetail>>({
-      url: `${API_PATH}/${employee_id}`,
+      url: `${API_PATH}/info`,
       method: "get",
+      params: {
+        enterprise_id,
+        employee_id,
+        employee_email,
+        employee_mobile
+      },
+    });
+  },
+
+  // 获取员工签约激活链接
+  inviteQuery(data: {
+    enterprise_id: string;
+    employee_id?: string;
+    page_content_code?: string;
+    withholding_sign_str?: string;
+    create_share_code?: string;
+  }) {
+    return request<ApiResponse<{
+      enterprise_id: string;
+      sign_url: string;
+      mini_app_sign_url: string;
+      share_code?: string;
+    }>> ({
+      url: `${API_PATH}/invite/query`,
+      method: "post",
+      data,
     });
   },
 
@@ -33,9 +59,9 @@ export interface EmployeePageQuery {
   page_no?: number;
   page_size?: number;
   enterprise_id?: string;
-  name?: string;
+  employee_name?: string;
   employee_no?: string;
-  employee_id?: string;
+  employee_mobile?: string;
   status?: string;
 }
 
@@ -47,29 +73,28 @@ export interface EmployeePageResp {
 export interface EmployeeTable {
   id: number;
   employee_id?: string;
-  alipay_employee_id?: string;
   enterprise_id?: string;
-  name?: string;
+  employee_name?: string;
   employee_no?: string;
   status: string;
-  mobile?: string;
+  employee_mobile?: string;
+  user_id?: number;
   created_time: string;
 }
 
 export interface EmployeeDetail {
   id: number;
   employee_id?: string;
-  alipay_employee_id?: string;
   enterprise_id?: string;
-  name?: string;
+  employee_name?: string;
   employee_no?: string;
   identity_type?: string;
   identity?: string;
   identity_open_id?: string;
-  mobile?: string;
-  email?: string;
-  cert_type?: string;
-  cert_no?: string;
+  employee_mobile?: string;
+  employee_email?: string;
+  employee_cert_type?: string;
+  employee_cert_no?: string;
   status: string;
   iot_check_type?: string;
   department_ids?: string[];
@@ -78,7 +103,7 @@ export interface EmployeeDetail {
   sign_return_url?: string;
   create_share_code?: boolean;
   sign_url_carry_info?: boolean;
-  invite_url?: string;
+  sign_url?: string;
   share_code?: string;
   expire_time?: string;
   profiles?: Record<string, unknown>;
@@ -90,15 +115,15 @@ export interface EmployeeDetail {
 
 export interface EmployeeForm {
   enterprise_id: string;
-  name: string;
-  employee_no: string;
-  identity_type: string;
+  employee_name: string;
+  employee_no?: string;
+  identity_type?: string;
   identity?: string;
   identity_open_id?: string;
-  mobile?: string;
-  email?: string;
-  cert_type?: string;
-  cert_no?: string;
+  employee_mobile?: string;
+  employee_email?: string;
+  employee_cert_type?: string;
+  employee_cert_no?: string;
   iot_check_type?: string;
   department_ids?: string[];
   accounting_entity_ids?: string[];
@@ -112,7 +137,7 @@ export interface EmployeeForm {
 export interface EmployeeOperation {
   employee_id?: string;
   alipay_employee_id?: string;
-  invite_url?: string;
+  sign_url?: string;
   share_code?: string;
   expire_time?: string;
 }
@@ -142,6 +167,7 @@ export const STATUS_TAG_TYPE: Record<string, string> = {
 };
 
 export const STATUS_LABEL: Record<string, string> = {
+  UNACTIVATED: "待激活",
   EMPLOYEE_CREATE: "待激活",
   EMPLOYEE_ACTIVATED: "已激活",
   EMPLOYEE_UNSIGN: "已解约",

+ 19 - 10
frontend/src/layouts/components/NavBar/components/EnterpriseSwitcher.vue

@@ -4,21 +4,21 @@
       <div class="enterprise-switcher__trigger">
         <el-icon><OfficeBuilding /></el-icon>
         <span class="enterprise-switcher__name">
-          {{ currentEnterpriseName }}
+          {{ currentEnterprise?.name }}
         </span>
         <el-icon class="enterprise-switcher__arrow"><ArrowDown /></el-icon>
       </div>
       <template #dropdown>
         <el-dropdown-menu>
           <el-dropdown-item
-            v-for="ent in enterpriseStore.loadEnterpriseList()"
+            v-for="ent in enterpriseList"
             :key="ent.enterprise_id"
             :command="ent.enterprise_id"
-            :class="{ 'is-active': ent.enterprise_id === userStore.currentEnterprise?.enterprise_id }"
+            :class="{ 'is-active': ent.enterprise_id === currentEnterprise.value?.enterprise_id }"
           >
             <div class="enterprise-item">
               <span class="enterprise-item__name">{{ ent.name }}</span>
-              <el-icon v-if="ent.enterprise_id === userStore.currentEnterprise?.enterprise_id" class="enterprise-item__check">
+              <el-icon v-if="ent.enterprise_id === currentEnterprise.value?.enterprise_id" class="enterprise-item__check">
                 <Check />
               </el-icon>
             </div>
@@ -36,15 +36,24 @@ import { OfficeBuilding, ArrowDown, Check } from "@element-plus/icons-vue";
 
 const enterpriseStore = useEnterpriseStore();
 
-const currentEnterprise = computed(() => {
-  let current = enterpriseStore.getCurrentEnterprise();
-  if (!current && enterpriseStore.hasEnterprise) {
-    enterpriseStore.setCurrentEnterprise(enterpriseStore.getEnterpriseList[0].enterprise_id);
-    current = enterpriseStore.getCurrentEnterprise();
+const currentEnterprise = computed(() => enterpriseStore.getCurrentEnterprise);
+const hasEnterprise = computed(() => enterpriseStore.hasEnterprise);
+const enterpriseList = computed(() => enterpriseStore.getEnterpriseList);
+
+// 在组件挂载或依赖变化时处理
+onMounted(() => {
+  if (!currentEnterprise.value && hasEnterprise.value && enterpriseList.value.length > 0) {
+    enterpriseStore.setCurrentEnterprise(enterpriseList.value[0].enterprise_id);
   }
-  return current;
 });
 
+// 或使用 watch 监听变化
+// watch([currentEnterprise, hasEnterprise, enterpriseList], ([current, has, list]) => {
+//   if (!current && has && list.length > 0) {
+//     enterpriseStore.setCurrentEnterprise(list[0].enterprise_id);
+//   }
+// }, { immediate: true });
+
 function handleSwitch(enterpriseId: string) {
   enterpriseStore.setCurrentEnterprise(enterpriseId);
 }

+ 9 - 9
frontend/src/layouts/components/NavBar/components/NavbarActions.vue

@@ -4,9 +4,9 @@
     <template v-if="isDesktop">
 
       <!-- 企业切换器 -->
-      <!-- <div v-if="showEnterpriseSwitcher" class="navbar-actions__item">
+      <div v-if="showEnterpriseSwitcher" class="navbar-actions__item">
         <EnterpriseSwitcher />
-      </div> -->
+      </div>
 
       <!-- 搜索 -->
       <div v-if="settingStore.showMenuSearch" class="navbar-actions__item">
@@ -14,12 +14,12 @@
       </div>
 
       <!-- 全屏 -->
-      <div v-if="settingStore.showFullscreen" class="navbar-actions__item">
+      <div v-if="settingStore.showFullscreen && userStore.is_platform_user" class="navbar-actions__item">
         <Fullscreen />
       </div>
 
       <!-- 布局大小 -->
-      <div v-if="settingStore.showSizeSelect" class="navbar-actions__item">
+      <div v-if="settingStore.showSizeSelect && userStore.is_platform_user" class="navbar-actions__item">
         <SizeSelect />
       </div>
 
@@ -29,13 +29,13 @@
       </div> -->
 
       <!-- 通知 -->
-      <div v-if="settingStore.showNotification" class="navbar-actions__item">
+      <div v-if="settingStore.showNotification && userStore.is_platform_user" class="navbar-actions__item">
         <Notification />
       </div>
 
       <!-- 系统设置按钮 -->
       <div
-        v-if="settingStore.showSettings"
+        v-if="settingStore.showSettings && userStore.is_platform_user"
         class="navbar-actions__item"
         @click="handleSettingsClick"
       >
@@ -139,9 +139,9 @@ const router = useRouter();
 const enterpriseStore = useEnterpriseStore();
 
 // 是否显示企业切换器(仅在有多个企业或已选择一个企业时显示)
-// const showEnterpriseSwitcher = computed(() => {
-//   return enterpriseStore.enterpriseList.length > 0;
-// });
+const showEnterpriseSwitcher = computed(() => {
+  return enterpriseStore.hasEnterprise;
+});
 
 // 是否为桌面设备
 const isDesktop = computed(() => appStore.device === DeviceEnum.DESKTOP);

+ 13 - 5
frontend/src/store/modules/enterprise.store.ts

@@ -23,12 +23,20 @@ export const useEnterpriseStore = defineStore("enterprise", {
 
   getters: {
     getEnterpriseList(state) {
-      state.enterpriseList = state.enterpriseList || JSON.parse(sessionStorage.getItem(ENTERPRISE_LIST_KEY) || '[]');
-      return state.enterpriseList;
+      if (state.enterpriseList.length > 0) {
+        return state.enterpriseList;
+      }
+      return JSON.parse(sessionStorage.getItem(ENTERPRISE_LIST_KEY) || '[]');
     },
     getCurrentEnterprise(state) {
-      state.currentEnterprise = state.currentEnterprise || JSON.parse(sessionStorage.getItem(CURRENT_ENTERPRISE_KEY) || '')
-      return state.currentEnterprise;
+      if (state.currentEnterprise) {
+        return state.currentEnterprise;
+      }
+      const currentEnterpriseJson = sessionStorage.getItem(CURRENT_ENTERPRISE_KEY);
+      if (currentEnterpriseJson) {
+        return JSON.parse(currentEnterpriseJson);
+      }
+      return null;
     },
     hasEnterprise() {
       return this.getEnterpriseList.length > 0;
@@ -67,7 +75,7 @@ export const useEnterpriseStore = defineStore("enterprise", {
     },
 
     setCurrentEnterprise(enterpriseId: string) {
-      this.currentEnterprise = this.enterpriseList.find((e) => e.enterprise_id === enterpriseId) || null;
+      this.currentEnterprise = this.getEnterpriseList.find((e: EnterpriseItem) => e.enterprise_id === enterpriseId) || null;
       if (this.currentEnterprise) {
         sessionStorage.setItem(CURRENT_ENTERPRISE_KEY, JSON.stringify(this.currentEnterprise));
       }

+ 22 - 2
frontend/src/views/module_payment/employee/components/DepartmentDetail.vue

@@ -58,6 +58,7 @@
 <script setup lang="ts">
 import { ref, onMounted } from 'vue';
 import { ElMessage } from 'element-plus';
+import { useRoute } from 'vue-router';
 import DepartmentAPI, { DEPARTMENT_STATUS_LABEL, DEPARTMENT_STATUS_TAG_TYPE } from '@/api/module_payment/department';
 import EmployeeAPI, { STATUS_LABEL, STATUS_TAG_TYPE } from '@/api/module_payment/employee';
 
@@ -79,9 +80,28 @@ const loadDepartmentDetail = async () => {
   
   loading.value = true;
   try {
-    const res = await DepartmentAPI.getDepartmentDetail(props.departmentId);
+    // 从路由参数获取企业ID
+    const route = useRoute();
+    const enterpriseId = route.query.enterprise_id as string;
+    
+    if (!enterpriseId) {
+      ElMessage.warning('请先选择企业');
+      return;
+    }
+    
+    const res = await DepartmentAPI.getDepartmentDetail(props.departmentId, enterpriseId);
     if (res.data.code === 200) {
-      departmentData.value = res.data.data;
+      const data = res.data.data;
+      departmentData.value = {
+        name: data.department.department_name,
+        code: data.department.department_code,
+        parent_name: '', // 可以根据parent_department_id查询上级部门名称
+        leader_employee_name: data.department.leader_employee_name,
+        sort_order: data.department.sort_order,
+        status: data.department.status,
+        created_time: data.department.created_time,
+        updated_time: data.department.updated_time
+      };
     } else {
       ElMessage.error(res.data.message || '加载部门详情失败');
     }

+ 35 - 11
frontend/src/views/module_payment/employee/components/DepartmentForm.vue

@@ -80,9 +80,19 @@
 <script setup lang="ts">
 import { ref, reactive, computed, onMounted, watch } from 'vue';
 import { ElMessage, ElLoading } from 'element-plus';
-import DepartmentAPI, { DepartmentForm, DEPARTMENT_STATUS_LABEL, DepartmentStatus } from '@/api/module_payment/department';
+import DepartmentAPI, { DepartmentInfo, DEPARTMENT_STATUS_LABEL, DepartmentStatus } from '@/api/module_payment/department';
 import EmployeeAPI from '@/api/module_payment/employee';
 
+// 部门表单数据类型
+interface DepartmentForm {
+  name: string;
+  code: string;
+  parent_id: string;
+  leader_employee_id: string;
+  sort_order: number;
+  status: string;
+}
+
 const props = defineProps<{
   type: 'create' | 'update';
   departmentId?: string;
@@ -171,17 +181,17 @@ const generateDepartmentCode = async () => {
 
 // 加载部门详情
 const loadDepartmentDetail = async () => {
-  if (props.type === 'update' && props.departmentId) {
+  if (props.type === 'update' && props.departmentId && props.enterpriseId) {
     try {
-      const res = await DepartmentAPI.getDepartmentDetail(props.departmentId);
+      const res = await DepartmentAPI.getDepartmentDetail(props.departmentId, props.enterpriseId);
       if (res.data.code === 200) {
         const data = res.data.data;
-        formData.name = data.name;
-        formData.code = data.code;
-        formData.parent_id = data.parent_id || '';
-        formData.leader_employee_id = data.leader_employee_id || '';
-        formData.sort_order = data.sort_order || 0;
-        formData.status = data.status;
+        formData.name = data.department.department_name;
+        formData.code = data.department.department_code;
+        formData.parent_id = data.department.parent_department_id || '';
+        formData.leader_employee_id = data.department.leader_employee_id || '';
+        formData.sort_order = data.department.sort_order || 0;
+        formData.status = data.department.status;
       }
     } catch (error) {
       console.error('加载部门详情失败:', error);
@@ -200,9 +210,22 @@ const submitForm = async (isContinue = false) => {
       try {
         let res;
         if (props.type === 'create') {
-          res = await DepartmentAPI.createDepartment(formData);
+          res = await DepartmentAPI.createDepartment({
+            enterprise_id: props.enterpriseId!,
+            department_name: formData.name,
+            department_code: formData.code,
+            parent_department_id: formData.parent_id,
+            sort_order: formData.sort_order,
+            leader_employee_id: formData.leader_employee_id
+          });
         } else {
-          res = await DepartmentAPI.updateDepartment(props.departmentId!, formData);
+          res = await DepartmentAPI.updateDepartment(props.departmentId!, props.enterpriseId!, {
+            department_name: formData.name,
+            department_code: formData.code,
+            parent_department_id: formData.parent_id,
+            sort_order: formData.sort_order,
+            leader_employee_id: formData.leader_employee_id
+          });
         }
         
         if (res.data.code === 200) {
@@ -213,6 +236,7 @@ const submitForm = async (isContinue = false) => {
             const enterpriseId = props.enterpriseId;
             Object.assign(formData, {
               name: '',
+              code: '',
               parent_id: '',
               leader_employee_id: '',
               sort_order: 0,

+ 73 - 21
frontend/src/views/module_payment/employee/components/EmployeeDetail.vue

@@ -2,7 +2,7 @@
   <div class="employee-detail">
     <el-descriptions :column="3" border>
       <el-descriptions-item label="员工姓名" :span="1">
-        {{ detailData.name || "-" }}
+        {{ detailData.employee_name || "-" }}
       </el-descriptions-item>
       <el-descriptions-item label="员工工号" :span="1">
         {{ detailData.employee_no || "-" }}
@@ -18,9 +18,6 @@
       <el-descriptions-item label="企业ID" :span="1">
         {{ detailData.enterprise_id || "-" }}
       </el-descriptions-item>
-      <el-descriptions-item label="支付宝员工ID" :span="2">
-        {{ detailData.alipay_employee_id || "-" }}
-      </el-descriptions-item>
       <el-descriptions-item label="身份类型" :span="1">
         {{ detailData.identity_type || "-" }}
       </el-descriptions-item>
@@ -28,16 +25,16 @@
         {{ detailData.identity || detailData.identity_open_id || "-" }}
       </el-descriptions-item>
       <el-descriptions-item label="手机号" :span="1">
-        {{ detailData.mobile || "-" }}
+        {{ detailData.employee_mobile || "-" }}
       </el-descriptions-item>
       <el-descriptions-item label="邮箱" :span="2">
-        {{ detailData.email || "-" }}
+        {{ detailData.employee_email || "-" }}
       </el-descriptions-item>
       <el-descriptions-item label="证件类型" :span="1">
-        {{ formatCertType(detailData.cert_type) }}
+        {{ formatCertType(detailData.employee_cert_type) }}
       </el-descriptions-item>
       <el-descriptions-item label="证件号码" :span="2">
-        {{ detailData.cert_no || "-" }}
+        {{ detailData.employee_cert_no || "-" }}
       </el-descriptions-item>
       <el-descriptions-item label="IoT核身方式" :span="3">
         {{ detailData.iot_check_type || "-" }}
@@ -48,22 +45,65 @@
 
     <el-descriptions :column="3" border>
       <el-descriptions-item label="邀请链接" :span="3">
-        <el-link v-if="detailData.invite_url" type="primary" :href="detailData.invite_url" target="_blank">
-          点击查看
-        </el-link>
+        <div v-if="detailData.sign_url" class="invite-url-container">
+          <el-link type="primary" :href="detailData.sign_url" target="_blank">
+            点击查看
+          </el-link>
+          <el-button
+            type="text"
+            size="small"
+            @click="copyToClipboard(detailData.sign_url)"
+            style="margin-left: 8px"
+          >
+            复制链接
+          </el-button>
+        </div>
         <span v-else>-</span>
       </el-descriptions-item>
       <el-descriptions-item label="分享码" :span="1">
-        {{ detailData.share_code || "-" }}
+        <div v-if="detailData.share_code" class="share-code-container">
+          <span>{{ detailData.share_code }}</span>
+          <el-button
+            type="text"
+            size="small"
+            @click="copyToClipboard(detailData.share_code)"
+            style="margin-left: 8px"
+          >
+            复制
+          </el-button>
+        </div>
+        <span v-else>-</span>
       </el-descriptions-item>
       <el-descriptions-item label="链接过期时间" :span="2">
         {{ detailData.expire_time || "-" }}
       </el-descriptions-item>
       <el-descriptions-item label="代扣签约串" :span="3">
-        {{ detailData.withholding_sign_str || "-" }}
+        <div v-if="detailData.withholding_sign_str" class="sign-string-container">
+          <span class="sign-string">{{ detailData.withholding_sign_str }}</span>
+          <el-button
+            type="text"
+            size="small"
+            @click="copyToClipboard(detailData.withholding_sign_str)"
+            style="margin-left: 8px"
+          >
+            复制
+          </el-button>
+        </div>
+        <span v-else>-</span>
       </el-descriptions-item>
       <el-descriptions-item label="免登签约token" :span="3">
-        {{ detailData.free_sign_token || "-" }}
+        <div v-if="detailData.free_sign_token" class="token-container">
+          <span class="token">{{ detailData.free_sign_token }}</span>
+          <el-button
+            type="text"
+            size="small"
+            @click="copyToClipboard(detailData.free_sign_token)"
+            style="margin-left: 8px"
+          >
+            复制
+          </el-button>
+        </div>
+        <span v-else>-</span>
       </el-descriptions-item>
     </el-descriptions>
 
@@ -81,23 +121,23 @@
       </el-descriptions-item>
     </el-descriptions>
 
-    <template v-if="detailData.sign_return_url">
+    <!-- <template v-if="detailData.sign_return_url">
       <el-divider content-position="left">签约跳转信息</el-divider>
       <el-descriptions :column="1" border>
         <el-descriptions-item label="签约跳转URL" :span="1">
           {{ detailData.sign_return_url }}
         </el-descriptions-item>
       </el-descriptions>
-    </template>
+    </template> -->
 
-    <template v-if="detailData.profiles && Object.keys(detailData.profiles).length > 0">
+    <!-- <template v-if="detailData.profiles && Object.keys(detailData.profiles).length > 0">
       <el-divider content-position="left">扩展参数</el-divider>
       <el-descriptions :column="1" border>
         <el-descriptions-item label="扩展参数" :span="1">
           {{ JSON.stringify(detailData.profiles) }}
         </el-descriptions-item>
       </el-descriptions>
-    </template>
+    </template> -->
 
     <el-divider content-position="left">时间信息</el-divider>
 
@@ -120,12 +160,15 @@ import EmployeeAPI, {
   CERT_TYPE_OPTIONS,
 } from "@/api/module_payment/employee";
 import { onMounted, ref } from "vue";
+import { ElMessage } from "element-plus";
 
 interface Props {
   employeeId: string;
 }
 
-const props = defineProps<Props>();
+const props = defineProps<Props & {
+  enterpriseId: string;
+}>();
 
 const detailData = ref<EmployeeDetail>({
   status: "",
@@ -144,9 +187,18 @@ function formatList(list?: string[]) {
   return list.join(", ");
 }
 
+async function copyToClipboard(text: string) {
+  try {
+    await navigator.clipboard.writeText(text);
+    ElMessage.success("链接已复制到剪贴板");
+  } catch (err) {
+    ElMessage.error("复制失败,请手动复制");
+  }
+}
+
 async function fetchDetail() {
-  if (!props.employeeId) return;
-  const res = await EmployeeAPI.detailEmployee(props.employeeId);
+  if (!props.enterpriseId) return;
+  const res = await EmployeeAPI.detailEmployee(props.enterpriseId, props.employeeId);
   if (res.data.data) {
     detailData.value = res.data.data;
   }

+ 65 - 62
frontend/src/views/module_payment/employee/components/EmployeeForm.vue

@@ -9,20 +9,20 @@
       label-position="top"
       class="employee-form__inner"
     >
-      <el-form-item label="员工姓名" prop="name" required>
-        <el-input v-model="formData.name" placeholder="请输入" :maxlength="128" />
+      <el-form-item label="员工姓名" prop="employee_name" required>
+        <el-input v-model="formData.employee_name" placeholder="请输入" :maxlength="128" />
       </el-form-item>
 
       <el-form-item label="员工工号" prop="employee_no">
         <el-input v-model="formData.employee_no" placeholder="请输入" :maxlength="64" />
       </el-form-item>
 
-      <el-form-item label="手机号" prop="mobile">
-        <el-input v-model="formData.mobile" placeholder="请输入" :maxlength="32" />
+      <el-form-item label="手机号" prop="employee_mobile">
+        <el-input v-model="formData.employee_mobile" placeholder="请输入" :maxlength="32" />
       </el-form-item>
 
-      <el-form-item label="邮箱" prop="email">
-        <el-input v-model="formData.email" placeholder="请输入" :maxlength="128" />
+      <el-form-item label="邮箱" prop="employee_email">
+        <el-input v-model="formData.employee_email" placeholder="请输入" :maxlength="128" />
         <div class="employee-form__hint">手机号/邮箱任意必填其中之一</div>
       </el-form-item>
 
@@ -81,9 +81,9 @@
         </el-select>
       </el-form-item> -->
 
-      <el-form-item label="标签" prop="label_names">
+      <!-- <el-form-item label="标签" prop="label_names">
         <el-input v-model="formData.label_names" placeholder="请输入" :maxlength="128" />
-      </el-form-item>
+      </el-form-item> -->
 
 
     </el-form>
@@ -93,6 +93,11 @@
 <script setup lang="ts">
 import { computed, reactive, ref, watch } from "vue";
 import EmployeeAPI, { EmployeeForm, EmployeeDetail } from "@/api/module_payment/employee";
+import { useEnterpriseStore } from "@/store/modules/enterprise.store";
+
+
+const enterpriseStore = useEnterpriseStore();
+
 
 interface Props {
   type: "create" | "update";
@@ -110,16 +115,16 @@ const emit = defineEmits<{
 const dataFormRef = ref();
 
 const initialFormData: EmployeeForm = {
-  enterprise_id: '',
-  name: '',
+  enterprise_id: enterpriseStore.getCurrentEnterprise?.enterprise_id || "",
+  employee_name: '',
   employee_no: '',
-  identity_type: "ALIPAY_USER_ID",
+  identity_type: undefined,
   identity: undefined,
   identity_open_id: undefined,
-  mobile: undefined,
-  email: undefined,
-  cert_type: undefined,
-  cert_no: undefined,
+  employee_mobile: undefined,
+  employee_email: undefined,
+  employee_cert_type: undefined,
+  employee_cert_no: undefined,
   iot_check_type: undefined,
   department_ids: [],
   accounting_entity_ids: [],
@@ -167,19 +172,19 @@ watch(
   () => props.employeeId,
   async (newVal) => {
     if (newVal && props.type === "update") {
-      const res = await EmployeeAPI.detailEmployee(newVal);
+      const res = await EmployeeAPI.detailEmployee(newVal, props.enterpriseId || "");
       const data = res.data.data as EmployeeDetail;
       if (data) {
-        formData.enterprise_id = data.enterprise_id;
-        formData.name = data.name;
-        formData.employee_no = data.employee_no;
-        formData.identity_type = data.identity_type || "ALIPAY_USER_ID";
+        // formData.enterprise_id = data.enterprise_id;
+        formData.employee_name = data.employee_name || "";
+        formData.employee_no = data.employee_no || "";
+        formData.identity_type = data.identity_type || undefined;
         formData.identity = data.identity;
         formData.identity_open_id = data.identity_open_id;
-        formData.mobile = data.mobile;
-        formData.email = data.email;
-        formData.cert_type = data.cert_type;
-        formData.cert_no = data.cert_no;
+        formData.employee_mobile = data.employee_mobile;
+        formData.employee_email = data.employee_email;
+        formData.employee_cert_type = data.employee_cert_type;
+        formData.employee_cert_no = data.employee_cert_no;
         formData.iot_check_type = data.iot_check_type;
         formData.department_ids = data.department_ids || [];
         formData.accounting_entity_ids = data.accounting_entity_ids || [];
@@ -187,11 +192,7 @@ watch(
         formData.sign_return_url = data.sign_return_url;
         formData.create_share_code = data.create_share_code || false;
         formData.sign_url_carry_info = data.sign_url_carry_info || false;
-        formData.profiles = data.profiles || {
-          join_date: undefined,
-          city: undefined,
-          role: undefined,
-        };
+        formData.profiles = data.profiles || {};
       }
     }
   },
@@ -210,8 +211,8 @@ watch(
 
 // 验证手机号或邮箱至少一个必填
 const validateContact = (_rule: unknown, value: string, callback: (error?: Error) => void) => {
-  const hasMobile = formData.mobile && formData.mobile.trim() !== "";
-  const hasEmail = formData.email && formData.email.trim() !== "";
+  const hasMobile = formData.employee_mobile && formData.employee_mobile.trim() !== "";
+  const hasEmail = formData.employee_email && formData.employee_email.trim() !== "";
 
   if (!hasMobile && !hasEmail) {
     callback(new Error("手机号和邮箱至少需要一个不为空"));
@@ -221,24 +222,24 @@ const validateContact = (_rule: unknown, value: string, callback: (error?: Error
 };
 
 const rules = reactive({
-  enterprise_id: [
-    {
-      required: true,
-      message: "请输入企业ID",
-      trigger: "blur",
-      validator: (_rule: unknown, value: string, callback: (error?: Error) => void) => {
-        if (props.type === "create" && !formData.enterprise_id) {
-          callback(new Error("企业ID不能为空"));
-        } else {
-          callback();
-        }
-      },
-    },
-  ],
-  name: [{ required: true, message: "请输入员工姓名", trigger: "blur" }],
+  // enterprise_id: [
+  //   {
+  //     required: true,
+  //     message: "请输入企业ID",
+  //     trigger: "blur",
+  //     validator: (_rule: unknown, value: string, callback: (error?: Error) => void) => {
+  //       if (props.type === "create" && !formData.enterprise_id) {
+  //         callback(new Error("企业ID不能为空"));
+  //       } else {
+  //         callback();
+  //       }
+  //     },
+  //   },
+  // ],
+  employee_name: [{ required: true, message: "请输入员工姓名", trigger: "blur" }],
   employee_no: [{ message: "请输入员工工号", trigger: "blur" }],
-  mobile: [{ validator: validateContact, trigger: "blur" }],
-  email: [{ validator: validateContact, trigger: "blur" }],
+  employee_mobile: [{ validator: validateContact, trigger: "blur" }],
+  employee_email: [{ validator: validateContact, trigger: "blur" }],
   "profiles.role": [{ required: true, message: "请选择角色", trigger: "change" }],
 });
 
@@ -248,16 +249,16 @@ async function submitForm() {
 
   try {
     const submitData: Record<string, unknown> = {
-      enterprise_id: formData.enterprise_id,
-      name: formData.name,
+      enterprise_id: enterpriseStore.getCurrentEnterprise?.enterprise_id || "",
+      employee_name: formData.employee_name,
       employee_no: formData.employee_no,
       identity_type: formData.identity_type,
       identity: formData.identity || undefined,
       identity_open_id: formData.identity_open_id || undefined,
-      mobile: formData.mobile || undefined,
-      email: formData.email || undefined,
-      cert_type: formData.cert_type || undefined,
-      cert_no: formData.cert_no || undefined,
+      employee_mobile: formData.employee_mobile || undefined,
+      employee_email: formData.employee_email || undefined,
+      employee_cert_type: formData.employee_cert_type || undefined,
+      employee_cert_no: formData.employee_cert_no || undefined,
       iot_check_type: formData.iot_check_type || undefined,
       department_ids: formData.department_ids || [],
       accounting_entity_ids: formData.accounting_entity_ids || [],
@@ -268,10 +269,12 @@ async function submitForm() {
       profiles: formData.profiles,
     };
 
-    if (props.type === "create") {
-      await EmployeeAPI.createEmployee(submitData as EmployeeForm);
+    if (props.type === "create") {  
+      await EmployeeAPI.createEmployee(submitData as unknown as EmployeeForm);
     }
+
     emit("success");
+    
   } catch (error) {
     console.error(error);
   }
@@ -284,15 +287,15 @@ async function handleSaveAndAddNext() {
   try {
     const submitData: Record<string, unknown> = {
       enterprise_id: formData.enterprise_id,
-      name: formData.name,
+      employee_name: formData.employee_name,
       employee_no: formData.employee_no,
       identity_type: formData.identity_type,
       identity: formData.identity || undefined,
       identity_open_id: formData.identity_open_id || undefined,
-      mobile: formData.mobile || undefined,
-      email: formData.email || undefined,
-      cert_type: formData.cert_type || undefined,
-      cert_no: formData.cert_no || undefined,
+      employee_mobile: formData.employee_mobile || undefined,
+      employee_email: formData.employee_email || undefined,
+      employee_cert_type: formData.employee_cert_type || undefined,
+      employee_cert_no: formData.employee_cert_no || undefined,
       iot_check_type: formData.iot_check_type || undefined,
       department_ids: formData.department_ids || [],
       accounting_entity_ids: formData.accounting_entity_ids || [],
@@ -304,7 +307,7 @@ async function handleSaveAndAddNext() {
     };
 
     if (props.type === "create") {
-      await EmployeeAPI.createEmployee(submitData as EmployeeForm);
+      await EmployeeAPI.createEmployee(submitData as unknown as EmployeeForm);
     }
     resetForm();
     emit("successAndAddNext");

+ 197 - 60
frontend/src/views/module_payment/employee/index.vue

@@ -28,21 +28,16 @@
                 </template>
                 <el-table-column v-if="contentCols.find((col) => col.prop === 'selection')?.show" type="selection"
                   min-width="55" align="center" />
-                <el-table-column v-if="contentCols.find((col) => col.prop === 'employee_id')?.show" key="employee_id"
-                  label="员工ID" prop="employee_id" min-width="150" show-overflow-tooltip />
-                <el-table-column v-if="contentCols.find((col) => col.prop === 'alipay_employee_id')?.show"
-                  key="alipay_employee_id" label="支付宝员工ID" prop="alipay_employee_id" min-width="150"
-                  show-overflow-tooltip />
-                <el-table-column v-if="contentCols.find((col) => col.prop === 'name')?.show" key="name" label="员工姓名"
-                  prop="name" min-width="120" show-overflow-tooltip />
+                <!-- <el-table-column v-if="contentCols.find((col) => col.prop === 'employee_id')?.show" key="employee_id"
+                  label="员工ID" prop="employee_id" min-width="150" show-overflow-tooltip /> -->
+                <el-table-column v-if="contentCols.find((col) => col.prop === 'employee_name')?.show" key="employee_name" label="员工姓名"
+                  prop="employee_name" min-width="120" show-overflow-tooltip />
                 <el-table-column v-if="contentCols.find((col) => col.prop === 'employee_no')?.show" key="employee_no"
                   label="员工工号" prop="employee_no" min-width="120" show-overflow-tooltip />
-                <el-table-column v-if="contentCols.find((col) => col.prop === 'mobile')?.show" key="mobile" label="手机号"
-                  prop="mobile" min-width="120" />
-                <el-table-column v-if="contentCols.find((col) => col.prop === 'id_card')?.show" key="id_card"
-                  label="身份证号" prop="id_card" min-width="180" show-overflow-tooltip />
-                <el-table-column v-if="contentCols.find((col) => col.prop === 'email')?.show" key="email" label="邮箱"
-                  prop="email" min-width="150" show-overflow-tooltip />
+                <el-table-column v-if="contentCols.find((col) => col.prop === 'employee_mobile')?.show" key="employee_mobile" label="手机号"
+                  prop="employee_mobile" min-width="120" />
+                <el-table-column v-if="contentCols.find((col) => col.prop === 'employee_email')?.show" key="employee_email" label="邮箱"
+                  prop="employee_email" min-width="150" show-overflow-tooltip />
                 <el-table-column v-if="contentCols.find((col) => col.prop === 'status')?.show" key="status" label="激活状态"
                   prop="status" min-width="100">
                   <template #default="scope">
@@ -51,14 +46,6 @@
                     </el-tag>
                   </template>
                 </el-table-column>
-                <el-table-column v-if="contentCols.find((col) => col.prop === 'role')?.show" key="role" label="角色"
-                  prop="role" min-width="100">
-                  <template #default="scope">
-                    <el-tag type="info">
-                      {{ scope.row.role_name || '普通员工' }}
-                    </el-tag>
-                  </template>
-                </el-table-column>
                 <el-table-column v-if="contentCols.find((col) => col.prop === 'created_time')?.show" key="created_time"
                   label="创建时间" prop="created_time" min-width="160" sortable />
                 <el-table-column v-if="contentCols.find((col) => col.prop === 'operation')?.show" fixed="right"
@@ -72,6 +59,10 @@
                       @click="handleOpenDialog('update', scope.row.employee_id)">
                       编辑
                     </el-button>
+                    <el-button v-hasPerm="['module_payment:employee:invite']" type="success" size="small" link
+                      @click="handleGetInviteLink(scope.row)">
+                      签约链接
+                    </el-button>
                   </template>
                 </el-table-column>
               </el-table>
@@ -154,7 +145,7 @@
     <EnhancedDialog v-model="dialogVisible.visible" :title="dialogVisible.title" @close="handleCloseDialog">
       <template v-if="dialogVisible.type === 'detail'">
         <template v-if="dialogVisible.entity === 'employee'">
-          <EmployeeDetail :employee-id="currentEmployeeId" />
+          <EmployeeDetail :employee-id="currentEmployeeId" :enterprise-id="currentEnterpriseId" />
         </template>
         <template v-else-if="dialogVisible.entity === 'department'">
           <DepartmentDetail :department-id="currentDepartmentId" />
@@ -168,7 +159,7 @@
         </template>
         <template v-else-if="dialogVisible.entity === 'department'">
           <DepartmentForm ref="deptFormRef" :type="dialogVisible.type" :department-id="currentDepartmentId"
-            :enterprise-id="currentEnterpriseId" @success="handleDeptFormSuccess" />
+            :enterprise-id="currentEnterpriseId || enterpriseIdFromUrl.value" @success="handleDeptFormSuccess" />
         </template>
       </template>
 
@@ -200,6 +191,53 @@
         </div>
       </template>
     </EnhancedDialog>
+
+    <!-- 签约链接对话框 -->
+    <el-dialog
+      v-model="inviteDialogVisible.visible"
+      :title="inviteDialogVisible.title"
+      width="600px"
+      @close="inviteDialogVisible.data = null"
+    >
+      <div v-if="inviteDialogVisible.data" class="invite-link-container">
+        <el-descriptions :column="1" border>
+          <el-descriptions-item label="邀请链接">
+            <div class="link-item">
+              <el-link type="primary" :href="inviteDialogVisible.data.sign_url" target="_blank">
+                {{ inviteDialogVisible.data.sign_url }}
+              </el-link>
+              <el-button type="text" size="small" @click="copyInviteLink(inviteDialogVisible.data.sign_url)">
+                复制
+              </el-button>
+            </div>
+          </el-descriptions-item>
+          
+          <!-- <el-descriptions-item label="小程序签约二维码">
+            <div class="qrcode-item">
+              <div v-if="miniAppQrCode" class="qrcode-container">
+                <img :src="miniAppQrCode" alt="小程序签约二维码" class="qrcode" />
+                <p class="qrcode-tip">请使用支付宝扫码</p>
+              </div>
+              <div v-else class="qrcode-error">
+                二维码生成失败
+              </div>
+              <el-button type="text" size="small" @click="copyInviteLink(inviteDialogVisible.data.mini_app_sign_url)">
+                复制链接
+              </el-button>
+            </div>
+          </el-descriptions-item> -->
+
+          <el-descriptions-item v-if="inviteDialogVisible.data.share_code" label="签约吱口令">
+            <div class="link-item">
+              <span>{{ inviteDialogVisible.data.share_code }}</span>
+              <el-button type="text" size="small" @click="copyInviteLink(inviteDialogVisible.data.share_code)">
+                复制
+              </el-button>
+            </div>
+          </el-descriptions-item>
+        </el-descriptions>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -209,16 +247,14 @@ defineOptions({
   inheritAttrs: false,
 });
 
-import { View, Plus } from "@element-plus/icons-vue";
+import { View } from "@element-plus/icons-vue";
 import EmployeeAPI, {
   EmployeePageQuery,
-  EmployeeTable,
   STATUS_TAG_TYPE,
   STATUS_LABEL,
 } from "@/api/module_payment/employee";
 import DepartmentAPI, {
   DepartmentPageQuery,
-  DepartmentTable,
   DEPARTMENT_STATUS_TAG_TYPE,
   DEPARTMENT_STATUS_LABEL,
 } from "@/api/module_payment/department";
@@ -235,8 +271,10 @@ import type { ISearchConfig, IContentConfig } from "@/components/CURD/types";
 import { useCrudList } from "@/components/CURD/useCrudList";
 import { useLoadingAction } from "@/composables/useLoadingAction";
 import { useRoute } from "vue-router";
-import { ref, reactive, onMounted, computed } from "vue";
+import { ref, reactive, computed } from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
+import { useEnterpriseStore } from "@/store/modules/enterprise.store";
+import QRCode from "qrcode";
 
 const route = useRoute();
 
@@ -258,13 +296,13 @@ const searchConfig = reactive<ISearchConfig>({
   form: { labelWidth: "auto" },
   formItems: [
     {
-      prop: "name",
+      prop: "employee_name",
       label: "员工姓名",
       type: "input",
       attrs: { placeholder: "请输入员工姓名", clearable: true },
     },
     {
-      prop: "mobile",
+      prop: "employee_mobile",
       label: "手机号",
       type: "input",
       attrs: { placeholder: "请输入手机号", clearable: true },
@@ -278,6 +316,30 @@ const searchConfig = reactive<ISearchConfig>({
   ],
 });
 
+const dialogVisible = reactive({
+  title: "",
+  visible: false,
+  type: "create" as "create" | "update" | "detail",
+  entity: "employee" as "employee" | "department",
+});
+
+const inviteDialogVisible = reactive({
+  visible: false,
+  title: "员工签约激活链接",
+  data: null as any,
+});
+
+const useEnterprise = useEnterpriseStore();
+
+const currentEmployeeId = ref<string>('');
+const currentDepartmentId = ref<string>('');
+const currentEnterpriseId = computed(() => useEnterprise.getCurrentEnterprise?.enterprise_id || "");
+
+// 小程序链接二维码
+const miniAppQrCode = computed(() => {
+  return '';
+});
+
 // 部门搜索配置
 const deptSearchConfig = reactive<ISearchConfig>({
   permPrefix: "module_payment:department",
@@ -340,15 +402,12 @@ const contentCols = reactive<
 >([
   { prop: "selection", label: "选择框", show: false },
   { prop: "index", label: "序号", show: true },
-  { prop: "name", label: "员工姓名", show: true },
+  { prop: "employee_name", label: "员工姓名", show: true },
   { prop: "employee_no", label: "员工工号", show: true },
   { prop: "employee_id", label: "员工ID", show: true },
-  { prop: "alipay_employee_id", label: "支付宝员工ID", show: true },
-  { prop: "mobile", label: "手机号", show: true },
-  { prop: "id_card", label: "身份证号", show: true },
-  { prop: "email", label: "邮箱", show: true },
+  { prop: "employee_mobile", label: "手机号", show: true },
+  { prop: "employee_email", label: "邮箱", show: true },
   { prop: "status", label: "激活状态", show: true },
-  { prop: "role", label: "角色", show: true },
   { prop: "created_time", label: "创建时间", show: true },
   { prop: "operation", label: "操作", show: true },
 ]);
@@ -361,17 +420,13 @@ const contentConfig = reactive<IContentConfig<EmployeePageQuery>>({
   toolbar: [],
   defaultToolbar: ["refresh", "filter", "import", "export"],
   pagination: true,
-  initialParams: computed(() => ({
-    enterprise_id: enterpriseIdFromUrl.value,
-  })) as Record<string, unknown>,
   indexAction: async (params) => {
     const query: EmployeePageQuery = {
       page_no: params.page_no,
       page_size: params.page_size,
     };
-    if (params.enterprise_id) query.enterprise_id = params.enterprise_id;
-    if (params.name) query.name = params.name;
-    if (params.mobile) query.mobile = params.mobile;
+    if (params.employee_name) query.employee_name = params.employee_name;
+    if (params.employee_mobile) query.employee_mobile = params.employee_mobile;
     if (params.employee_no) query.employee_no = params.employee_no;
     const res = await EmployeeAPI.listEmployee(query);
     return {
@@ -399,33 +454,34 @@ const deptContentConfig = reactive<IContentConfig<DepartmentPageQuery>>({
       page_size: params.page_size,
     };
     if (params.enterprise_id) query.enterprise_id = params.enterprise_id;
-    if (params.name) query.name = params.name;
-    if (params.code) query.code = params.code;
+    if (params.name) query.department_name = params.name;
+    if (params.code) query.department_code = params.code;
     if (params.status) query.status = params.status;
     const res = await DepartmentAPI.listDepartment(query);
+    // 转换数据结构以匹配前端表格显示
+    const items = res.data.data?.items.map((item: any) => ({
+      department_id: item.department_id,
+      name: item.department_name,
+      code: item.department_code,
+      parent_id: item.parent_department_id,
+      parent_name: '', // 可以根据parent_id查询上级部门名称
+      leader_employee_name: item.leader_employee_name,
+      sort_order: item.sort_order,
+      status: item.status,
+      created_time: item.created_time,
+      updated_time: item.updated_time
+    })) || [];
     return {
-      list: res.data.data?.items || [],
+      list: items,
       total: res.data.data?.total || 0,
     };
   },
 });
 
-const dialogVisible = reactive({
-  title: "",
-  visible: false,
-  type: "create" as "create" | "update" | "detail",
-  entity: "employee" as "employee" | "department",
-});
-
-const currentEmployeeId = ref<string>();
-const currentDepartmentId = ref<string>();
-const currentEnterpriseId = ref<string>();
-
-function handleOpenDialog(type: "create" | "update" | "detail", employeeId?: string) {
+function handleOpenDialog(type: "create" | "update" | "detail", employeeId: string) {
   dialogVisible.type = type;
   dialogVisible.entity = "employee";
   currentEmployeeId.value = employeeId;
-  currentEnterpriseId.value = enterpriseIdFromUrl.value;
   if (type === "create") {
     dialogVisible.title = "添加员工";
   } else if (type === "update") {
@@ -436,11 +492,10 @@ function handleOpenDialog(type: "create" | "update" | "detail", employeeId?: str
   dialogVisible.visible = true;
 }
 
-function handleOpenDeptDialog(type: "create" | "update" | "detail", departmentId?: string) {
+function handleOpenDeptDialog(type: "create" | "update" | "detail", departmentId: string) {
   dialogVisible.type = type;
   dialogVisible.entity = "department";
   currentDepartmentId.value = departmentId;
-  currentEnterpriseId.value = enterpriseIdFromUrl.value;
   if (type === "create") {
     dialogVisible.title = "添加部门";
   } else if (type === "update") {
@@ -483,6 +538,12 @@ function handleDeptFormSuccess() {
 
 async function handleDeleteDepartment(departmentId: string, departmentName: string) {
   try {
+    const enterpriseId = currentEnterpriseId.value || enterpriseIdFromUrl.value;
+    if (!enterpriseId) {
+      ElMessage.warning("请先选择企业");
+      return;
+    }
+    
     await ElMessageBox.confirm(
       `确定要删除部门「${departmentName}」吗?`,
       "删除部门",
@@ -493,7 +554,7 @@ async function handleDeleteDepartment(departmentId: string, departmentName: stri
       }
     );
     
-    const res = await DepartmentAPI.deleteDepartment(departmentId);
+    const res = await DepartmentAPI.deleteDepartment(departmentId, enterpriseId);
     if (res.data.code === 200) {
       ElMessage.success("删除部门成功");
       refreshDeptList();
@@ -507,6 +568,40 @@ async function handleDeleteDepartment(departmentId: string, departmentName: stri
     }
   }
 }
+
+// 获取员工签约激活链接
+async function handleGetInviteLink(row: any) {
+  try {
+    const enterpriseId = currentEnterpriseId.value || enterpriseIdFromUrl.value;
+    if (!enterpriseId) {
+      ElMessage.warning("请先选择企业");
+      return;
+    }
+
+    const res = await EmployeeAPI.inviteQuery({
+      enterprise_id: enterpriseId,
+      employee_id: row.employee_id,
+      create_share_code: "Y", // 默认生成签约吱口令
+    });
+
+    if (res.data.data) {
+      inviteDialogVisible.data = res.data.data;
+      inviteDialogVisible.visible = true;
+    }
+  } catch (error) {
+    ElMessage.error("获取签约链接失败");
+  }
+}
+
+// 复制链接
+async function copyInviteLink(text: string) {
+  try {
+    await navigator.clipboard.writeText(text);
+    ElMessage.success("链接已复制到剪贴板");
+  } catch (err) {
+    ElMessage.error("复制失败,请手动复制");
+  }
+}
 </script>
 
 <style scoped lang="scss">
@@ -522,4 +617,46 @@ async function handleDeleteDepartment(departmentId: string, departmentName: stri
     font-weight: 600;
   }
 }
+
+.invite-link-container {
+  .link-item {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    
+    .el-link {
+      flex: 1;
+      word-break: break-all;
+    }
+  }
+
+  .qrcode-item {
+    display: flex;
+    align-items: flex-start;
+    gap: 16px;
+  }
+
+  .qrcode-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+
+  .qrcode {
+    width: 200px;
+    height: 200px;
+    border: 1px solid #e0e0e0;
+  }
+
+  .qrcode-tip {
+    margin-top: 8px;
+    font-size: 14px;
+    color: #666;
+  }
+
+  .qrcode-error {
+    padding: 20px;
+    color: #f56c6c;
+  }
+}
 </style>

+ 0 - 1
frontend/src/views/module_payment/enterprise/index.vue

@@ -171,7 +171,6 @@ const enterpriseStore = useEnterpriseStore();
 const hasEnterprise = computed(() => enterpriseStore.hasEnterprise);
 
 onMounted(async () => {
- 
 });
 
 const searchConfig = reactive<ISearchConfig>({