Просмотр исходного кода

feat(post): list name link and show bound user names

Enrich post list with boundUserNames and open view dialog from the name column.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 2 месяцев назад
Родитель
Сommit
3e60ed56f0

+ 49 - 3
backend/app/crud/post.py

@@ -9,10 +9,52 @@ from sqlalchemy import and_, func, or_
 from sqlalchemy.orm import Session
 
 from app.core.utils import generate_uuid
-from app.models.models import SysPost, SysPostRole, SysRole
+from app.models.models import SysPost, SysPostRole, SysRole, SysUser, SysUserPost
 
 
-def _row_to_dict(p: SysPost) -> Dict[str, Any]:
+def _user_display_name(nick_name: Optional[str], user_no: Optional[str]) -> str:
+    n = (nick_name or "").strip()
+    if n:
+        return n
+    return (user_no or "").strip()
+
+
+def _bound_user_names_by_post_ids(
+    db: Session, post_ids: List[str], tenant_no: str
+) -> Dict[str, List[str]]:
+    """批量查询职位关联用户显示名(nick_name 优先,否则 user_no)。"""
+    out: Dict[str, List[str]] = {}
+    ids = [pid for pid in (post_ids or []) if pid]
+    tenant = (tenant_no or "").strip()
+    if not ids or not tenant:
+        return out
+    rows = (
+        db.query(SysUserPost.post_id, SysUser.nick_name, SysUser.user_no)
+        .join(
+            SysUser,
+            and_(
+                SysUser.id == SysUserPost.user_id,
+                SysUser.del_flag == 0,
+            ),
+        )
+        .filter(
+            and_(
+                SysUserPost.post_id.in_(ids),
+                SysUserPost.tenant_no == tenant,
+                SysUserPost.del_flag == 0,
+            )
+        )
+        .all()
+    )
+    for post_id, nick_name, user_no in rows:
+        name = _user_display_name(nick_name, user_no)
+        if not name:
+            continue
+        out.setdefault(post_id, []).append(name)
+    return out
+
+
+def _row_to_dict(p: SysPost, bound_user_names: Optional[List[str]] = None) -> Dict[str, Any]:
     return {
         "id": p.id,
         "postName": p.post_name,
@@ -21,6 +63,7 @@ def _row_to_dict(p: SysPost) -> Dict[str, Any]:
         "tenantNo": p.tenant_no,
         "orderNum": p.order_num if p.order_num is not None else 0,
         "remark": p.remark or "",
+        "boundUserNames": list(bound_user_names or []),
         "createTime": p.create_time.strftime("%Y-%m-%d %H:%M:%S") if p.create_time else "",
         "updateTime": p.update_time.strftime("%Y-%m-%d %H:%M:%S") if p.update_time else "",
     }
@@ -58,7 +101,10 @@ def list_posts(
         .limit(page_size)
         .all()
     )
-    return [_row_to_dict(x) for x in rows], int(total)
+    name_map = _bound_user_names_by_post_ids(db, [x.id for x in rows], tenant_no or "")
+    return [
+        _row_to_dict(x, bound_user_names=name_map.get(x.id, [])) for x in rows
+    ], int(total)
 
 
 def get_post(db: Session, post_id: str) -> Optional[SysPost]:

+ 28 - 0
backend/tests/test_post_list_bound_users.py

@@ -0,0 +1,28 @@
+"""职位列表 boundUserNames enrichment。"""
+import inspect
+
+from app.crud import post as post_crud
+
+
+class _DummySession:
+    def query(self, *args, **kwargs):  # pragma: no cover
+        raise AssertionError("空入参不应访问数据库")
+
+
+def test_bound_user_names_by_post_ids_empty():
+    db = _DummySession()
+    assert post_crud._bound_user_names_by_post_ids(db, [], "zkxw") == {}
+    assert post_crud._bound_user_names_by_post_ids(db, ["p1"], "") == {}
+
+
+def test_user_display_name_prefers_nick():
+    assert post_crud._user_display_name("张三", "zhangsan") == "张三"
+    assert post_crud._user_display_name("", "zhangsan") == "zhangsan"
+    assert post_crud._user_display_name("  ", "u1") == "u1"
+    assert post_crud._user_display_name(None, None) == ""
+
+
+def test_list_posts_enriches_bound_user_names():
+    source = inspect.getsource(post_crud.list_posts)
+    assert "_bound_user_names_by_post_ids" in source
+    assert "bound_user_names" in source or "boundUserNames" in source

+ 1 - 0
web-admin/src/types/api.d.ts

@@ -470,6 +470,7 @@ declare namespace Api {
       tenantNo: string
       orderNum: number
       remark: string
+      boundUserNames?: string[]
       createTime: string
       updateTime: string
     }

+ 31 - 1
web-admin/src/views/system/post/index.vue

@@ -72,7 +72,13 @@
             min-width="140"
             align="center"
             header-align="center"
-          />
+          >
+            <template #default="{ row }">
+              <el-button link type="primary" @click="handleView(row)">
+                {{ row.postName || '-' }}
+              </el-button>
+            </template>
+          </el-table-column>
           <el-table-column
             prop="postCode"
             label="职位编码"
@@ -80,6 +86,20 @@
             align="center"
             header-align="center"
           />
+          <el-table-column
+            label="关联人员"
+            min-width="180"
+            align="center"
+            header-align="center"
+          >
+            <template #default="{ row }">
+              <span class="post-bound-users-cell">{{
+                (row.boundUserNames || []).length
+                  ? (row.boundUserNames || []).join('、')
+                  : '—'
+              }}</span>
+            </template>
+          </el-table-column>
           <el-table-column
             prop="status"
             label="状态"
@@ -712,6 +732,7 @@
     try {
       await fetchUpdatePostBoundUsers(postBindPostId.value, [...postBindUserCheckedKeys.value])
       postBindDialogVisible.value = false
+      await loadData()
     } finally {
       postBindSubmitLoading.value = false
     }
@@ -865,6 +886,15 @@
 </script>
 
 <style scoped lang="scss">
+  .post-bound-users-cell {
+    display: inline-block;
+    max-width: 100%;
+    white-space: normal;
+    word-break: break-word;
+    line-height: 1.45;
+    text-align: center;
+  }
+
   .post-dialog-form {
     :deep(.el-form-item) {
       margin-bottom: 0;