增加创建团队成员时的模糊查找功能与进度管理页面
This commit is contained in:
parent
c657fed236
commit
1d954e144a
|
@ -63,3 +63,8 @@ export function resetUserPwd(data: any, id: string) {
|
|||
export function updateUserRole(data: { roleIds: string[] }, id: string) {
|
||||
return http.patch(`${BASE_URL}/${id}/role`, data)
|
||||
}
|
||||
|
||||
/** @desc 按姓名模糊搜索用户 */
|
||||
export function searchUserByName(name: string) {
|
||||
return http.get<T.UserNewResp[]>(`${BASE_URL}/searchByName`, { name })
|
||||
}
|
||||
|
|
|
@ -70,6 +70,6 @@ declare global {
|
|||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
|
|
|
@ -180,7 +180,17 @@
|
|||
<div class="form-row">
|
||||
<div class="form-item">
|
||||
<label>姓名 <span class="required">*</span></label>
|
||||
<a-input v-model="memberForm.name" placeholder="请输入姓名" />
|
||||
<a-auto-complete
|
||||
v-model="memberForm.name"
|
||||
placeholder="请输入姓名进行搜索"
|
||||
:data="userSearchOptions"
|
||||
:loading="userSearchLoading"
|
||||
@search="handleUserSearch"
|
||||
@select="handleUserSelect"
|
||||
allow-clear
|
||||
:filter-option="false"
|
||||
:trigger-on-focus="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label>联系电话 <span class="required">*</span></label>
|
||||
|
@ -304,7 +314,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { TeamMemberResp, TeamMemberQuery, TeamMemberExportQuery, CreateTeamMemberForm, UpdateTeamMemberForm, BackendTeamMemberResp } from '@/apis/project/type'
|
||||
|
@ -317,6 +327,8 @@ import {
|
|||
exportTeamMembers,
|
||||
downloadImportTemplate
|
||||
} from '@/apis/project/personnel-dispatch'
|
||||
import { searchUserByName } from '@/apis/system/user'
|
||||
import type { UserNewResp } from '@/apis/system/type'
|
||||
|
||||
// 获取路由参数
|
||||
const route = useRoute()
|
||||
|
@ -411,6 +423,12 @@ const statusForm = reactive<{
|
|||
|
||||
const fileList = ref<any[]>([])
|
||||
|
||||
// 用户搜索相关数据
|
||||
const userSearchResults = ref<UserNewResp[]>([])
|
||||
const userSearchOptions = ref<Array<{ label: string; value: string; user: UserNewResp }>>([])
|
||||
const userSearchLoading = ref(false)
|
||||
const searchTimeout = ref<NodeJS.Timeout | null>(null)
|
||||
|
||||
// 方法
|
||||
const loadData = async () => {
|
||||
if (!projectId) {
|
||||
|
@ -505,6 +523,70 @@ const openAddModal = () => {
|
|||
memberModalVisible.value = true
|
||||
}
|
||||
|
||||
// 用户搜索处理函数(带防抖)
|
||||
const handleUserSearch = async (value: string) => {
|
||||
console.log('开始搜索用户,输入值:', value)
|
||||
|
||||
// 清除之前的定时器
|
||||
if (searchTimeout.value) {
|
||||
clearTimeout(searchTimeout.value)
|
||||
}
|
||||
|
||||
// 如果输入为空,清空结果
|
||||
if (!value || value.trim().length < 1) {
|
||||
userSearchResults.value = []
|
||||
console.log('输入为空,清空搜索结果')
|
||||
return
|
||||
}
|
||||
|
||||
// 设置防抖延迟(300ms)
|
||||
searchTimeout.value = setTimeout(async () => {
|
||||
console.log('执行搜索,搜索值:', value.trim())
|
||||
userSearchLoading.value = true
|
||||
try {
|
||||
const response = await searchUserByName(value.trim())
|
||||
console.log('API响应完整数据:', response)
|
||||
// 根据后端返回的数据结构,使用 response.rows 而不是 response.data
|
||||
const users = response.rows || []
|
||||
userSearchResults.value = users
|
||||
|
||||
// 转换为 a-auto-complete 需要的格式
|
||||
userSearchOptions.value = users.map(user => ({
|
||||
label: `${user.name} | ${user.deptName || '未分配部门'}`,
|
||||
value: user.name,
|
||||
user: user
|
||||
}))
|
||||
|
||||
console.log('设置搜索结果:', userSearchResults.value)
|
||||
console.log('设置搜索选项:', userSearchOptions.value)
|
||||
} catch (error) {
|
||||
console.error('搜索用户失败:', error)
|
||||
userSearchResults.value = []
|
||||
userSearchOptions.value = []
|
||||
Message.error('搜索用户失败')
|
||||
} finally {
|
||||
userSearchLoading.value = false
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// 用户选择处理函数
|
||||
const handleUserSelect = (value: string, option: any) => {
|
||||
const selectedOption = userSearchOptions.value.find(option => option.value === value)
|
||||
if (selectedOption) {
|
||||
const selectedUser = selectedOption.user
|
||||
// 自动填充用户信息
|
||||
memberForm.name = selectedUser.name
|
||||
memberForm.phone = selectedUser.mobile || ''
|
||||
// 后端返回的数据中没有email字段,所以不自动填充邮箱
|
||||
// memberForm.email = selectedUser.email || ''
|
||||
// 清空搜索结果
|
||||
userSearchResults.value = []
|
||||
userSearchOptions.value = []
|
||||
console.log('选择的用户:', selectedUser)
|
||||
}
|
||||
}
|
||||
|
||||
const openEditModal = (record: TeamMemberResp) => {
|
||||
isEdit.value = true
|
||||
Object.assign(memberForm, {
|
||||
|
@ -568,6 +650,9 @@ const saveMember = async () => {
|
|||
const cancelMember = () => {
|
||||
memberModalVisible.value = false
|
||||
resetMemberForm()
|
||||
// 清空用户搜索结果
|
||||
userSearchResults.value = []
|
||||
userSearchOptions.value = []
|
||||
}
|
||||
|
||||
const resetMemberForm = () => {
|
||||
|
@ -582,6 +667,9 @@ const resetMemberForm = () => {
|
|||
joinDate: '',
|
||||
remark: ''
|
||||
})
|
||||
// 清空用户搜索结果
|
||||
userSearchResults.value = []
|
||||
userSearchOptions.value = []
|
||||
}
|
||||
|
||||
const openStatusModal = (record: TeamMemberResp) => {
|
||||
|
@ -715,11 +803,11 @@ const getRoleTypeText = (roleType: string) => {
|
|||
|
||||
const getStatusColor = (status: string) => {
|
||||
const colorMap: Record<string, string> = {
|
||||
ACTIVE: 'success',
|
||||
SUSPENDEN: 'warning',
|
||||
INACTIVE: 'danger'
|
||||
ACTIVE: '#52c41a', // 绿色 - 可用
|
||||
SUSPENDEN: '#faad14', // 橙色 - 忙碌
|
||||
INACTIVE: '#ff4d4f' // 红色 - 离线
|
||||
}
|
||||
return colorMap[status] || 'danger'
|
||||
return colorMap[status] || '#ff4d4f'
|
||||
}
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
|
@ -741,6 +829,13 @@ onMounted(() => {
|
|||
Message.warning('未获取到项目信息,请从项目详情页面进入')
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理搜索定时器
|
||||
if (searchTimeout.value) {
|
||||
clearTimeout(searchTimeout.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -1307,4 +1402,24 @@ onMounted(() => {
|
|||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
// 用户搜索选项样式
|
||||
.user-option {
|
||||
.user-name {
|
||||
font-weight: 500;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
|
||||
// 自动完成组件样式优化
|
||||
:deep(.arco-auto-complete) {
|
||||
.arco-input {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--color-primary-6);
|
||||
box-shadow: 0 0 0 2px rgba(var(--color-primary-6), 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -79,7 +79,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -215,7 +215,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -351,7 +351,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -487,7 +487,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -623,7 +623,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -759,7 +759,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -895,7 +895,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -1031,7 +1031,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -1180,7 +1180,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -1327,7 +1327,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -1474,7 +1474,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -1621,7 +1621,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -1915,7 +1915,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -2062,7 +2062,7 @@
|
|||
<icon-user class="detail-icon" />
|
||||
<span>负责人: {{ project.manager || '未指定' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-item clickable" @click.stop="openPersonnelManagement(project)">
|
||||
<icon-user-group class="detail-icon" />
|
||||
<span>团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }}</span>
|
||||
</div>
|
||||
|
@ -3041,11 +3041,11 @@ const openSiteManagement = (project: any) => {
|
|||
}
|
||||
|
||||
// 人员管理
|
||||
const openPersonnelManagement = () => {
|
||||
if (currentProject.value && currentProject.value.id) {
|
||||
const openPersonnelManagement = (project: any) => {
|
||||
if (project && project.id) {
|
||||
router.push({
|
||||
path: '/project-management/personnel-dispatch/construction-personnel',
|
||||
query: { projectId: currentProject.value.id }
|
||||
query: { projectId: project.id }
|
||||
})
|
||||
} else {
|
||||
Message.error('项目信息不完整,无法进入团队成员管理')
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue