|
|
@@ -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>
|