Переглянути джерело

feat: 用户绑定企微账号

wuwenyi 1 тиждень тому
батько
коміт
113c31b496
3 змінених файлів з 134 додано та 5 видалено
  1. 26 0
      src/api/wecom.ts
  2. 1 0
      src/types/system.ts
  3. 107 5
      src/views/system/user/index.vue

+ 26 - 0
src/api/wecom.ts

@@ -0,0 +1,26 @@
+import request from '@/api/request'
+
+export interface WecomDepartment {
+  id: number
+  name: string
+  parentId: number
+}
+
+export interface WecomMember {
+  id: number
+  userid: string
+  name: string
+  departmentId: number
+}
+
+export function listSubDepartments(parentId: number = 1): Promise<WecomDepartment[]> {
+  return request.get('/wecom/contact/departments', { params: { parentId } })
+}
+
+export function listDepartmentMembers(departmentId: number): Promise<WecomMember[]> {
+  return request.get('/wecom/contact/members', { params: { departmentId } })
+}
+
+export function getMemberByUserid(userid: string): Promise<WecomMember> {
+  return request.get('/wecom/contact/member', { params: { userid } })
+}

+ 1 - 0
src/types/system.ts

@@ -13,6 +13,7 @@ export interface User {
   roleIds?: number[]
   password?: string
   wecomUserId?: string
+  wecomUserName?: string
   wecomRemindEnabled?: number
   createTime?: string
 }

+ 107 - 5
src/views/system/user/index.vue

@@ -133,24 +133,56 @@
           </el-radio-group>
         </el-form-item>
         <el-form-item label="企微账号">
-          <el-input v-model="form.wecomUserId" placeholder="企业微信用户ID" />
+          <span v-if="form.wecomUserId" style="margin-right: 8px;">{{ wecomUserName || form.wecomUserId }}</span>
+          <el-button type="primary" size="small" @click="openWecomDialog">绑定企微账号</el-button>
+          <el-button v-if="form.wecomUserId" type="danger" size="small" @click="form.wecomUserId = ''; wecomUserName = ''">解绑</el-button>
         </el-form-item>
       </el-form>
       <template #footer>
         <el-button @click="dialogVisible = false">取消</el-button>
         <el-button type="primary" @click="submitForm">确认</el-button>
       </template>
+
+      <!-- 企微绑定弹窗 -->
+      <el-dialog v-model="wecomDialogVisible" title="绑定企微账号" width="600px">
+        <el-row :gutter="16">
+          <el-col :span="8">
+            <div class="wecom-title">部门</div>
+            <el-tree
+              :props="{ label: 'name', isLeaf: 'leaf' }"
+              node-key="id"
+              :load="loadDeptChildren"
+              lazy
+              highlight-current
+              @node-click="onDeptClick"
+            />
+          </el-col>
+          <el-col :span="16">
+            <div class="wecom-title">成员</div>
+            <el-input v-model="memberSearch" placeholder="搜索成员" clearable size="small" style="margin-bottom: 8px;" :disabled="memberList.length === 0" />
+            <el-table v-loading="loadingMembers" :data="filteredMemberList" border height="370" highlight-current-row @row-click="onMemberClick">
+              <el-table-column prop="name" label="姓名" />
+            </el-table>
+            <el-empty v-if="!selectedDeptId" description="请选择部门" :image-size="60" />
+          </el-col>
+        </el-row>
+        <template #footer>
+          <el-button @click="wecomDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="confirmWecomBind">确认绑定</el-button>
+        </template>
+      </el-dialog>
     </el-dialog>
 
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, shallowRef, reactive, onMounted } from 'vue'
+import { ref, shallowRef, reactive, onMounted, computed } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import type { FormInstance, FormRules } from 'element-plus'
 import { listUser, addUser, updateUser, deleteUser, assignUserRoles } from '@/api/system/user'
 import { listRole } from '@/api/system/role'
+import { listSubDepartments, listDepartmentMembers, type WecomMember } from '@/api/wecom'
 import { listDept } from '@/api/system/dept'
 import { adminEmployeeTypeLabel } from '@/utils/format'
 import type { User, Role, Dept } from '@/types/system'
@@ -183,7 +215,8 @@ const form = reactive<Partial<User>>({
   phone: '',
   deptId: undefined,
   employeeType: 'common_user',
-  status: 0
+  status: 0,
+  wecomUserId: ''
 })
 
 const PASSWORD_PLACEHOLDER = '********'
@@ -257,8 +290,10 @@ function handleAdd() {
     phone: '',
     deptId: undefined,
     employeeType: 'common_user',
-    status: 0
+    status: 0,
+    wecomUserId: ''
   })
+  wecomUserName.value = ''
   dialogVisible.value = true
 }
 
@@ -274,8 +309,10 @@ function handleEdit(row: User) {
     phone: row.phone,
     deptId: row.deptId,
     employeeType: row.employeeType || 'common_user',
-    status: row.status
+    status: row.status,
+    wecomUserId: row.wecomUserId || ''
   })
+  wecomUserName.value = (row as any).wecomUserName || ''
   dialogVisible.value = true
 }
 
@@ -354,6 +391,68 @@ async function submitAssignRole() {
   }
 }
 
+// 企微绑定
+const wecomDialogVisible = ref(false)
+const selectedDeptId = ref<number | null>(null)
+const memberList = ref<WecomMember[]>([])
+const selectedMember = ref<WecomMember | null>(null)
+const loadingMembers = ref(false)
+const wecomUserName = ref('')
+const memberSearch = ref('')
+const filteredMemberList = computed(() => {
+  if (!memberSearch.value) return memberList.value
+  const kw = memberSearch.value.toLowerCase()
+  return memberList.value.filter(m => m.name?.toLowerCase().includes(kw))
+})
+
+function openWecomDialog() {
+  wecomDialogVisible.value = true
+  selectedDeptId.value = null
+  memberList.value = []
+  selectedMember.value = null
+}
+
+async function loadDeptChildren(node: any, resolve: any) {
+  const parentId = node.data?.id || 0
+  try {
+    const depts = await listSubDepartments(parentId)
+    if (depts.length === 0) {
+      node.leaf = true
+    }
+    resolve(depts.map((d: any) => ({ id: d.id, name: d.name, leaf: false })))
+  } catch {
+    node.leaf = true
+    resolve([])
+  }
+}
+
+async function onDeptClick(data: any) {
+  selectedDeptId.value = data.id
+  selectedMember.value = null
+  loadingMembers.value = true
+  try {
+    memberList.value = await listDepartmentMembers(data.id)
+  } catch {
+    memberList.value = []
+  } finally {
+    loadingMembers.value = false
+  }
+}
+
+function onMemberClick(row: WecomMember) {
+  selectedMember.value = row
+}
+
+function confirmWecomBind() {
+  if (!selectedMember.value) {
+    ElMessage.warning('请选择一个成员')
+    return
+  }
+  form.wecomUserId = selectedMember.value.userid
+  wecomUserName.value = selectedMember.value.name
+  wecomDialogVisible.value = false
+}
+
 onMounted(() => {
   Promise.all([loadDepts(), loadData(), loadAllRoles()])
 })
@@ -375,4 +474,7 @@ onMounted(() => {
 .text-gray {
   color: #909399;
 }
+.wecom-title {
+  font-weight: bold; margin-bottom: 8px; color: #303133;
+}
 </style>