Ver Fonte

项目邀请成员

dsy há 5 dias atrás
pai
commit
d8000a886d

+ 8 - 0
src/api/list.js

@@ -162,3 +162,11 @@ export function PageQueryPinyinCorrectionListAdmin(data) {
 export function GetAuditorList_Select(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_audit_manager-GetAuditorList_Select`, data);
 }
+
+/**
+ * @description 分页查询我的消息
+ * @param {object} data
+ */
+export function PageQueryMyMessage(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=page_query-PageQueryMyMessage`, data);
+}

+ 28 - 0
src/api/project.js

@@ -345,3 +345,31 @@ export function ProjectRequestOperate(data) {
 export function ProjectAuditOperate(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_manager-ProjectAuditOperate`, data);
 }
+
+/**
+ * @description 邀请人员加入项目
+ * @param {object} data
+ * @param {string} data.user_id_list 用户ID列表
+ * @param {string} data.project_id 项目ID
+ */
+export function InvitePersonJoinProject(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_manager-InvitePersonJoinProject`, data);
+}
+
+/**
+ * @description 获取项目邀请人员列表
+ * @param {object} data
+ * @param {string} data.project_id 项目ID
+ */
+export function GetProjectInvitePersonList(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_manager-GetProjectInvitePersonList`, data);
+}
+
+/**
+ * @description 删除项目邀请人员
+ * @param {object} data
+ * @param {string} data.id 邀请ID
+ */
+export function DeleteProjectInvitePerson(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_manager-DeleteProjectInvitePerson`, data);
+}

+ 19 - 0
src/api/user.js

@@ -47,3 +47,22 @@ export function getSysConfigMailbox() {
 export function setSysConfigMailbox(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=sys_config_manager-SetSysConfig_Mailbox`, data);
 }
+
+/**
+ * @description 回复消息
+ * @param {object} data
+ * @param {string} data.id
+ * @param {1|2} data.reply_result 1-同意 2-不同意
+ */
+export function ReplyMessage(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=message_manager-ReplyMessage`, data);
+}
+
+/**
+ * @description 得到我的消息个数
+ * @param {object} data
+ * @param {-1|0|1|2} data.reply_result 回复结果 -1【全部】0【未回复】1【同意】2【不同意】
+ */
+export function GetMyMessageCount(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=message_manager-GetMyMessageCount`, data);
+}

+ 7 - 1
src/components/PaginationPage.vue

@@ -30,6 +30,10 @@ export default {
       type: Number,
       default: 7,
     },
+    isInit: {
+      type: Boolean,
+      default: true,
+    },
   },
   data() {
     return {
@@ -38,7 +42,9 @@ export default {
     };
   },
   created() {
-    this.getList();
+    if (this.isInit) {
+      this.getList();
+    }
   },
   methods: {
     changePage(number) {

+ 156 - 0
src/layouts/components/MyMessage.vue

@@ -0,0 +1,156 @@
+<template>
+  <el-dialog
+    :visible="visible"
+    title="我的消息"
+    class="my-message"
+    width="1000px"
+    :close-on-click-modal="false"
+    :append-to-body="true"
+    @close="dialogClose"
+  >
+    <div class="query-criteria">
+      <span class="criteria-label">回复状态</span>
+      <el-select v-model="replyResult" placeholder="请选择回复状态">
+        <el-option v-for="(label, value) in replyResultMap" :key="value" :label="label" :value="value" />
+      </el-select>
+
+      <span class="query-button">
+        <el-button type="primary" @click="pageQueryMyMessage">查询</el-button>
+      </span>
+    </div>
+
+    <el-table ref="user" :data="messageList" height="440">
+      <el-table-column label="序号" width="55" header-align="center" align="center">
+        <template slot-scope="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column label="类型" width="110" header-align="center" align="center">
+        <template slot-scope="{ row }">
+          {{ messageTypeMap[row.type] || '未知类型' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="message" label="消息" width="120" header-align="center" />
+      <el-table-column prop="create_time" label="邀请时间" width="160" header-align="center" align="center" />
+      <el-table-column prop="creator_name" label="邀请人" width="100" header-align="center" align="center" />
+      <el-table-column label="回复结果" width="80" header-align="center" align="center">
+        <template slot-scope="{ row }">
+          {{ replyResultMap[row.reply_result] || '未知状态' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="reply_time" label="回复时间" header-align="center" align="center" />
+
+      <el-table-column fixed="right" label="操作" width="100" header-align="center" align="center">
+        <template v-if="row.type === 0" slot-scope="{ row }">
+          <span v-if="row.type === 0" class="link" @click="replyMessage(row.id, 1)">同意</span>
+          <span style="margin: 0 6px">|</span>
+          <span class="link" @click="replyMessage(row.id, 2)">拒绝</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <PaginationPage :total="total" @getList="pageQueryMyMessage" />
+  </el-dialog>
+</template>
+
+<script>
+import { PageQueryMyMessage } from '@/api/list';
+import { ReplyMessage } from '@/api/user';
+
+import PaginationPage from '@/components/PaginationPage.vue';
+
+export default {
+  name: 'MyMessage',
+  components: {
+    PaginationPage,
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      // 消息列表数据
+      messageList: [],
+      replyResult: '0', // 默认选择未回复
+      replyResultMap: {
+        '-1': '全部',
+        0: '未回复',
+        1: '同意',
+        2: '不同意',
+      },
+      total: 0, // 消息总数
+      page_capacity: 10,
+      cur_page: 1,
+      messageTypeMap: {
+        0: '邀请加入项目',
+      },
+    };
+  },
+  methods: {
+    pageQueryMyMessage(data) {
+      const params = {
+        reply_result: this.replyResult,
+        page_capacity: this.page_capacity,
+        cur_page: this.cur_page,
+        ...data,
+      };
+      PageQueryMyMessage(params).then(({ message_list, total, cur_page }) => {
+        this.messageList = message_list || [];
+        this.total = total || 0;
+        this.page_capacity = data?.page_capacity || this.page_capacity;
+        this.cur_page = cur_page;
+      });
+    },
+    replyMessage(id, reply_result) {
+      ReplyMessage({
+        id,
+        reply_result,
+      })
+        .then(() => {
+          this.$message.success('回复成功');
+          this.pageQueryMyMessage();
+        })
+        .catch(() => {});
+    },
+    dialogClose() {
+      this.$emit('update:visible', false);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.my-message {
+  .query-criteria {
+    display: flex;
+    column-gap: 8px;
+    align-items: center;
+    margin-bottom: 12px;
+
+    .criteria-label {
+      white-space: nowrap;
+    }
+
+    .el-input {
+      width: 200px;
+    }
+
+    .el-select {
+      width: 100px;
+    }
+
+    :deep .el-input__inner {
+      background-color: #fff;
+      border-color: #dcdcdc;
+    }
+
+    .query-button {
+      display: flex;
+      flex: 1;
+      justify-content: flex-end;
+    }
+  }
+}
+</style>

+ 71 - 5
src/layouts/default/header/index.vue

@@ -22,27 +22,40 @@
     <!-- 用户头像和用户名 -->
     <el-dropdown v-else trigger="click" class="user">
       <span class="el-dropdown-link">
-        <img
-          class="avatar"
-          :src="token.image_url ? token.image_url : require('@/assets/header/avatar-default.png')"
-          alt="head portrait"
-        />
+        <span class="avatar-wrap">
+          <img
+            class="avatar"
+            :src="token.image_url ? token.image_url : require('@/assets/header/avatar-default.png')"
+            alt="head portrait"
+          />
+          <span v-if="messageCount > 0" class="message-badge">{{ displayMessageCount }}</span>
+        </span>
         <span class="real_name">{{ token.user_real_name }}</span>
       </span>
       <el-dropdown-menu slot="dropdown" class="user-menu">
+        <el-dropdown-item @click.native="showMessageList">
+          <i class="el-icon-message"></i><span>我的消息</span>
+        </el-dropdown-item>
         <el-dropdown-item @click.native="logout">
           <img :src="require('@/assets/header/exit.png')" /><span>退出登录</span>
         </el-dropdown-item>
       </el-dropdown-menu>
     </el-dropdown>
+    <MyMessage :visible.sync="visibleMessage" />
   </header>
 </template>
 
 <script>
 import { getToken } from '@/utils/auth';
+import { GetMyMessageCount } from '@/api/user';
+
+import MyMessage from '@/layouts/components/MyMessage.vue';
 
 export default {
   name: 'LayoutHeader',
+  components: {
+    MyMessage,
+  },
   data() {
     const token = getToken();
     const popedomCodeList = this.$store.state.user?.popedom_code_list || [];
@@ -78,12 +91,27 @@ export default {
       activePro: '',
       LoginNavIndex: 0,
       projectList: projectListMap[userType]?.filter((item) => item.isShow !== false) || projectListMap.USER,
+      messageCount: 0,
+      visibleMessage: false,
     };
   },
+  computed: {
+    displayMessageCount() {
+      return this.messageCount > 99 ? '99+' : this.messageCount;
+    },
+  },
+  watch: {
+    visibleMessage(newVal) {
+      if (!newVal) {
+        this.getMyMessageCount();
+      }
+    },
+  },
   created() {
     let path = this.$route.path.replace(/^\//, '');
     this.activePro = path;
     this.LoginNavIndex = this.projectList.findIndex((item) => item.key === path);
+    this.getMyMessageCount();
   },
   methods: {
     logout() {
@@ -100,6 +128,14 @@ export default {
       this.activePro = key;
       this.$router.push({ path: `/${key}` });
     },
+    getMyMessageCount() {
+      GetMyMessageCount({ reply_result: 0 }).then(({ count }) => {
+        this.messageCount = count || 0;
+      });
+    },
+    showMessageList() {
+      this.visibleMessage = true;
+    },
   },
 };
 </script>
@@ -165,12 +201,37 @@ export default {
       display: flex;
       align-items: center;
 
+      .avatar-wrap {
+        position: relative;
+        display: inline-flex;
+      }
+
       .avatar {
         width: 32px;
         height: 32px;
         border-radius: 50%;
       }
 
+      .message-badge {
+        position: absolute;
+        top: -6px;
+        right: -10px;
+        box-sizing: border-box;
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        min-width: 18px;
+        height: 18px;
+        padding: 0 5px;
+        font-size: 12px;
+        font-weight: 600;
+        line-height: 1;
+        color: #fff;
+        background: #f56c6c;
+        border: 1px solid #fff;
+        border-radius: 999px;
+      }
+
       .real_name {
         display: inline-block;
         padding-left: 10px;
@@ -193,6 +254,11 @@ export default {
     font-size: 16px;
     color: #000;
 
+    i {
+      margin-right: 10px;
+      font-size: 20px;
+    }
+
     img {
       width: 24px;
       height: 24px;

+ 68 - 6
src/layouts/home/header/index.vue

@@ -11,42 +11,79 @@
     <!-- 用户头像和用户名 -->
     <el-dropdown v-else trigger="click" class="user">
       <span class="el-dropdown-link">
-        <img
-          class="avatar"
-          :src="token.image_url ? token.image_url : require('@/assets/header/avatar-default.png')"
-          alt="head portrait"
-        />
+        <span class="avatar-wrap">
+          <img
+            class="avatar"
+            :src="token.image_url ? token.image_url : require('@/assets/header/avatar-default.png')"
+            alt="head portrait"
+          />
+          <span v-if="messageCount > 0" class="message-badge">{{ displayMessageCount }}</span>
+        </span>
         <span class="real_name">{{ token.user_real_name }}</span>
       </span>
       <el-dropdown-menu slot="dropdown" class="user-menu">
         <el-dropdown-item @click.native="$router.push('/personal_center')">
           <img :src="require('@/assets/header/personal-center.png')" /><span>个人中心</span>
         </el-dropdown-item>
+        <el-dropdown-item @click.native="showMessageList">
+          <i class="el-icon-message"></i><span>我的消息</span>
+        </el-dropdown-item>
         <el-dropdown-item @click.native="logout">
           <img :src="require('@/assets/header/exit.png')" /><span>退出登录</span>
         </el-dropdown-item>
       </el-dropdown-menu>
     </el-dropdown>
+    <MyMessage :visible.sync="visibleMessage" />
   </header>
 </template>
 
 <script>
 import { getToken } from '@/utils/auth';
+import { GetMyMessageCount } from '@/api/user';
+
+import MyMessage from '@/layouts/components/MyMessage.vue';
 
 export default {
   name: 'HomeHeader',
+  components: {
+    MyMessage,
+  },
   data() {
     const token = getToken();
     return {
       token: this.$store.state.user || token,
+      messageCount: 0,
+      visibleMessage: false,
     };
   },
-  created() {},
+  computed: {
+    displayMessageCount() {
+      return this.messageCount > 99 ? '99+' : this.messageCount;
+    },
+  },
+  watch: {
+    visibleMessage(newVal) {
+      if (!newVal) {
+        this.getMyMessageCount();
+      }
+    },
+  },
+  created() {
+    this.getMyMessageCount();
+  },
   methods: {
     logout() {
       this.$store.dispatch('user/signOut');
       this.$router.push('/login');
     },
+    getMyMessageCount() {
+      GetMyMessageCount({ reply_result: 0 }).then(({ count }) => {
+        this.messageCount = count || 0;
+      });
+    },
+    showMessageList() {
+      this.visibleMessage = true;
+    },
   },
 };
 </script>
@@ -91,12 +128,37 @@ export default {
       display: flex;
       align-items: center;
 
+      .avatar-wrap {
+        position: relative;
+        display: inline-flex;
+      }
+
       .avatar {
         width: 32px;
         height: 32px;
         border-radius: 50%;
       }
 
+      .message-badge {
+        position: absolute;
+        top: -6px;
+        right: -10px;
+        box-sizing: border-box;
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        min-width: 18px;
+        height: 18px;
+        padding: 0 5px;
+        font-size: 12px;
+        font-weight: 600;
+        line-height: 1;
+        color: #fff;
+        background: #f56c6c;
+        border: 1px solid #fff;
+        border-radius: 999px;
+      }
+
       .real_name {
         display: inline-block;
         padding-left: 10px;

+ 5 - 4
src/views/create_project/selectProjectMembers.vue

@@ -11,7 +11,7 @@
       <span class="criteria-label">真实姓名</span>
       <el-input v-model="real_name" placeholder="请输入姓名" @change="getUserList" />
       <span>所属机构</span>
-      <el-select v-model="org_id_list" placeholder="请选择机构" multiple @change="getUserList">
+      <el-select v-model="org_id" placeholder="请选择机构" disabled @change="getUserList">
         <el-option v-for="item in org_list" :key="item.id" :label="item.name" :value="item.id" />
       </el-select>
       <span class="query-button">
@@ -66,6 +66,7 @@
 <script>
 import { queryUserList } from '@/api/list';
 import { orgIndexList } from '@/api/user';
+import { getToken } from '@/utils/auth';
 
 import PaginationPage from '@/components/PaginationPage.vue';
 
@@ -93,10 +94,11 @@ export default {
     },
   },
   data() {
+    const token = getToken();
     return {
+      org_id: token.org_id,
       real_name: '', // 真实姓名
       org_list: [], // 机构列表
-      org_id_list: [], // 机构id列表
       user_list: [], // 用户列表
       total: 0,
       page_capacity: 10,
@@ -127,7 +129,7 @@ export default {
     getUserList(data) {
       const params = {
         real_name: this.real_name,
-        org_id_list: this.org_id_list,
+        org_id_list: [this.org_id],
         page_capacity: this.page_capacity,
         cur_page: this.cur_page,
         org_manager_status: -1,
@@ -168,7 +170,6 @@ export default {
     dialogClose() {
       this.$emit('update:visible', false);
       this.real_name = '';
-      this.org_id_list = [];
       this.selectedUsers = [];
       this.$refs.user.clearSelection();
     },

+ 140 - 0
src/views/personal_workbench/project/ProjectInfoManage.vue

@@ -157,6 +157,36 @@
           <span>{{ project.create_time }}</span>
         </div>
       </div>
+
+      <!-- 项目邀请成员 -->
+      <div class="project-invite-members">
+        <div class="invite-list-top">
+          <span class="title">项目邀请成员</span>
+          <span class="link" @click="visibleInvitePerson = true">邀请其它机构成员</span>
+        </div>
+        <div class="invite-list-header">
+          <span>序号</span>
+          <span>姓名</span>
+          <span>所属机构</span>
+          <span>邀请时间</span>
+          <span>回复结果</span>
+          <span>回复时间</span>
+          <span>操作</span>
+        </div>
+        <div class="invite-list">
+          <div v-for="(person, index) in invitePersonList" :key="index" class="invite-list-item">
+            <span>{{ index + 1 }}</span>
+            <span>{{ person.user_real_name }}</span>
+            <span>{{ person.user_org_name }}</span>
+            <span>{{ person.create_time }}</span>
+            <span>{{ replyResultMap[person.reply_result] }}</span>
+            <span>{{ person.reply_time }}</span>
+            <span>
+              <span class="link" @click="deleteProjectInvitePerson(person.id)">删除</span>
+            </span>
+          </div>
+        </div>
+      </div>
     </main>
 
     <SelectMembers
@@ -173,6 +203,7 @@
       :value="curFieldValue"
       @updateProjectFieldValue="updateProjectFieldValue"
     />
+    <InvitePerson :visible.sync="visibleInvitePerson" @invitePerson="handleInvitePerson" />
   </div>
 </template>
 
@@ -183,6 +214,9 @@ import {
   SetProjectLeader,
   UpdateProjectFieldValue,
   SetProjectLabel,
+  InvitePersonJoinProject,
+  GetProjectInvitePersonList,
+  DeleteProjectInvitePerson,
 } from '@/api/project';
 import { GetUserList_ID } from '@/api/user';
 
@@ -190,6 +224,7 @@ import SelectMembers from '@/views/create_project/selectProjectMembers.vue';
 import MenuPage from '@/views/personal_workbench/common/menu.vue';
 import UpdateProjectField from './components/UpdateProjectField.vue';
 import ProjectMenu from '@/views/project_manage/common/ProjectMenu.vue';
+import InvitePerson from './components/InvitePerson.vue';
 
 export default {
   name: 'ProjectInfoManage',
@@ -198,6 +233,7 @@ export default {
     MenuPage,
     UpdateProjectField,
     ProjectMenu,
+    InvitePerson,
   },
   data() {
     return {
@@ -239,10 +275,14 @@ export default {
       visibleUpdateField: false,
       curField: '',
       curFieldValue: '',
+      invitePersonList: [], // 项目邀请成员列表
+      replyResultMap: ['未回复', '同意', '不同意'], // 回复结果映射关系
+      visibleInvitePerson: false, // 是否显示邀请成员对话框
     };
   },
   created() {
     this.getProjectInfo();
+    this.getProjectInvitePersonList();
   },
   methods: {
     getProjectInfo() {
@@ -354,6 +394,34 @@ export default {
         this.getProjectInfo();
       });
     },
+    getProjectInvitePersonList() {
+      GetProjectInvitePersonList({ project_id: this.id }).then(({ invite_person_list }) => {
+        this.invitePersonList = invite_person_list;
+      });
+    },
+    /**
+     * 删除项目邀请成员
+     * @param {number} id - 邀请ID
+     */
+    deleteProjectInvitePerson(id) {
+      DeleteProjectInvitePerson({ id }).then(() => {
+        this.$message.success('邀请人删除成功');
+        this.getProjectInvitePersonList();
+      });
+    },
+    /**
+     * 处理邀请成员加入项目
+     * @param {string[]} user_id_list - 邀请的用户ID列表
+     */
+    handleInvitePerson(user_id_list) {
+      InvitePersonJoinProject({
+        project_id: this.id,
+        user_id_list,
+      }).then(() => {
+        this.$message.success('邀请发送成功');
+        this.getProjectInvitePersonList();
+      });
+    },
   },
 };
 </script>
@@ -462,5 +530,77 @@ export default {
       grid-template-columns: 120px 1fr;
     }
   }
+
+  .project-invite-members {
+    display: flex;
+    flex-direction: column;
+    margin-top: 24px;
+
+    .invite-list-top {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 8px 12px;
+      background-color: $fill-color;
+      border-bottom: $border;
+    }
+
+    @mixin column-width {
+      span {
+        padding: 8px 12px;
+
+        &:first-child {
+          width: 55px;
+        }
+
+        &:nth-child(2) {
+          width: 140px;
+        }
+
+        &:nth-child(3) {
+          width: 260px;
+        }
+
+        &:nth-child(4) {
+          width: 200px;
+        }
+
+        &:nth-child(5) {
+          width: 100px;
+        }
+
+        &:nth-child(6) {
+          width: 200px;
+        }
+
+        &:last-child {
+          flex: 1;
+        }
+      }
+    }
+
+    .invite-list-header {
+      display: flex;
+      font-size: 14px;
+      font-weight: bold;
+      text-align: center;
+      background-color: $main-background-color;
+      border-bottom: $border;
+
+      @include column-width;
+    }
+
+    .invite-list {
+      .invite-list-item {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        text-align: center;
+        border-bottom: $border;
+
+        @include column-width;
+      }
+    }
+  }
 }
 </style>

+ 150 - 0
src/views/personal_workbench/project/components/InvitePerson.vue

@@ -0,0 +1,150 @@
+<template>
+  <el-dialog
+    :visible="visible"
+    title="邀请其它机构成员"
+    class="invite-person"
+    width="900px"
+    :close-on-click-modal="false"
+    @close="dialogClose"
+  >
+    <div class="query-criteria">
+      <span class="criteria-label">真实姓名</span>
+      <el-input v-model="real_name" placeholder="请输入姓名" @change="getUserList" />
+      <span>所属机构</span>
+      <el-select v-model="org_id_list" placeholder="请选择机构" multiple @change="getUserList">
+        <el-option v-for="item in org_list" :key="item.id" :label="item.name" :value="item.id" />
+      </el-select>
+      <span class="query-button">
+        <el-button type="primary" @click="getUserList">查询</el-button>
+      </span>
+    </div>
+
+    <el-table ref="user" :data="user_list" height="440">
+      <el-table-column type="selection" width="55" align="center" header-align="center" class-name="index-column" />
+      <el-table-column prop="real_name" label="真实姓名" width="140" header-align="center" />
+      <el-table-column prop="user_name" label="用户名" width="140" header-align="center" />
+      <el-table-column prop="email" label="邮箱" width="200" header-align="center" />
+      <el-table-column prop="phone" label="电话" width="120" header-align="center" />
+      <el-table-column prop="org_name" label="所属机构" header-align="center" />
+    </el-table>
+    <PaginationPage :total="total" :is-init="false" @getList="getUserList" />
+
+    <div slot="footer">
+      <el-button @click="dialogClose">取消</el-button>
+      <el-button type="primary" @click="confirm">确定</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { queryUserList } from '@/api/list';
+import { orgIndexList } from '@/api/user';
+import { getToken } from '@/utils/auth';
+
+import PaginationPage from '@/components/PaginationPage.vue';
+
+export default {
+  name: 'InvitePerson',
+  components: {
+    PaginationPage,
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      required: true,
+    },
+  },
+  data() {
+    const token = getToken();
+    return {
+      org_id: token.org_id,
+      real_name: '', // 真实姓名
+      org_list: [], // 机构列表
+      org_id_list: [], // 选择的机构ID列表
+      user_list: [], // 用户列表
+      total: 0,
+      page_capacity: 10,
+      cur_page: 1,
+    };
+  },
+  created() {
+    this.getOrgIndexList();
+  },
+  methods: {
+    getUserList(data) {
+      if (this.org_id_list.length === 0) {
+        this.$message.warning('请至少选择一个机构');
+        return;
+      }
+      const params = {
+        real_name: this.real_name,
+        org_id_list: this.org_id_list,
+        page_capacity: this.page_capacity,
+        cur_page: this.cur_page,
+        org_manager_status: -1,
+      };
+      queryUserList({ ...params, ...data }).then(({ user_list, total_count, cur_page }) => {
+        this.user_list = user_list;
+        this.total = total_count;
+        this.page_capacity = data?.page_capacity || this.page_capacity;
+        this.cur_page = cur_page;
+      });
+    },
+    getOrgIndexList() {
+      orgIndexList().then(({ org_list }) => {
+        this.org_list = org_list.filter((org) => org.id !== this.org_id);
+      });
+    },
+    dialogClose() {
+      this.$emit('update:visible', false);
+      this.$refs.user.clearSelection();
+    },
+    confirm() {
+      const selectedUsers = this.$refs.user.selection;
+      if (selectedUsers.length === 0) {
+        this.$message.warning('请至少选择一名成员');
+        return;
+      }
+      this.$emit(
+        'invitePerson',
+        selectedUsers.map((user) => user.id),
+      );
+      this.dialogClose();
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.invite-person {
+  .query-criteria {
+    display: flex;
+    column-gap: 8px;
+    align-items: center;
+    margin-bottom: 12px;
+
+    .criteria-label {
+      white-space: nowrap;
+    }
+
+    .el-input {
+      width: 200px;
+    }
+
+    .el-select {
+      width: 360px;
+    }
+
+    :deep .el-input__inner {
+      background-color: #fff;
+      border-color: #dcdcdc;
+    }
+
+    .query-button {
+      display: flex;
+      flex: 1;
+      justify-content: flex-end;
+    }
+  }
+}
+</style>