增加创建团队成员时的模糊查找功能与进度管理页面

This commit is contained in:
马诗敏 2025-08-14 14:38:21 +08:00
parent c657fed236
commit 1d954e144a
5 changed files with 1516 additions and 424 deletions

View File

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

View File

@ -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')
}

View File

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

View File

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