liuhairui hai 1 semana
pai
achega
f41a6c9b71
Modificáronse 2 ficheiros con 237 adicións e 85 borrados
  1. 0 1
      src/pinia/modules/user.js
  2. 237 84
      src/view/yunyin/renliziyuan/GroupManagement.vue

+ 0 - 1
src/pinia/modules/user.js

@@ -171,7 +171,6 @@ const LoginIn2 = async (loginInfo) => {
     });
 
     const res = await login2(loginInfo);
-
     if (res.code === 0) {
       console.log("登录成功:", res);
 

+ 237 - 84
src/view/yunyin/renliziyuan/GroupManagement.vue

@@ -57,6 +57,7 @@
               >
                 <el-table-column align="left" label="员工编号" prop="员工编号" min-width="120" />
                 <el-table-column align="left" label="员工姓名" prop="员工姓名" min-width="120" />
+                <el-table-column align="left" label="小组名称" prop="小组名称" min-width="120" />
                 <el-table-column align="left" label="职位" prop="职位" min-width="100" />
                 <el-table-column label="操作" width="188" align="center" fixed="right">
                   <template #default="{ row }">
@@ -78,13 +79,15 @@
           <el-dialog
             v-model="addMemberVisible"
             title="小组新增成员"
-            width="480px"
+            width="860px"
             align-center
             destroy-on-close
             append-to-body
+            class="group-add-member-dialog"
             @closed="resetAddMemberForm"
           >
-            <el-form :model="addMemberForm" label-width="100px" class="add-member-form">
+            <div class="add-member-dialog-split">
+            <el-form :model="addMemberForm" label-width="100px" class="add-member-form add-member-left">
               <el-form-item label="生产工序" required>
                 <el-select
                   v-model="addMemberForm['生产工序']"
@@ -116,31 +119,31 @@
                 </el-select>
               </el-form-item>
               <el-form-item label="选择人员" required>
-                <el-select
-                  v-model="addMemberStaffId"
-                  class="add-member-select"
-                  filterable
-                  remote
-                  reserve-keyword
-                  clearable
-                  placeholder="输入员工编号或姓名搜索并选择"
-                  :remote-method="searchStaffForAdd"
-                  :loading="staffPickerLoading"
-                  @change="onAddMemberStaffPick"
-                >
-                  <el-option
-                    v-for="s in mergedStaffOptions"
-                    :key="staffRowKey(s)"
-                    :label="formatStaffOptionLabel(s)"
-                    :value="staffRowId(s)"
-                  />
-                </el-select>
-              </el-form-item>
-              <el-form-item label="员工编号">
-                <el-input :model-value="addMemberForm['员工编号']" readonly placeholder="选择人员后显示" />
-              </el-form-item>
-              <el-form-item label="员工姓名">
-                <el-input :model-value="addMemberForm['员工姓名']" readonly placeholder="选择人员后显示" />
+                <div class="add-member-pick-row">
+                  <el-select
+                    v-model="addMemberStaffIds"
+                    class="add-member-select"
+                    multiple
+                    collapse-tags
+                    collapse-tags-tooltip
+                    filterable
+                    remote
+                    clearable
+                    placeholder="输入员工编号或姓名搜索,选中后自动加入"
+                    :remote-method="searchStaffForAdd"
+                    :loading="staffPickerLoading"
+                    @visible-change="onAddMemberStaffDropdownVisible"
+                    @change="onAddMemberStaffPick"
+                  >
+                    <template #tag></template>
+                    <el-option
+                      v-for="s in mergedStaffOptions"
+                      :key="staffRowKey(s)"
+                      :label="formatStaffOptionLabel(s)"
+                      :value="staffRowId(s)"
+                    />
+                  </el-select>
+                </div>
               </el-form-item>
               <el-form-item label="职位">
                 <el-select v-model="addMemberForm['职位']" placeholder="请选择职位" clearable class="add-member-select">
@@ -148,6 +151,42 @@
                 </el-select>
               </el-form-item>
             </el-form>
+            <div class="add-member-right">
+              <div class="picked-title">已选人员({{ selectedStaffRows.length }})</div>
+              <div v-if="selectedStaffRows.length" class="picked-staff-table-wrap">
+                <el-table
+                  :data="selectedStaffRows"
+                  size="small"
+                  border
+                  height="360"
+                  class="picked-staff-table"
+                >
+                  <el-table-column label="员工编号" min-width="92" show-overflow-tooltip>
+                    <template #default="{ row }">
+                      {{ row['员工编号'] ?? row.staff_no ?? '' }}
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="员工姓名" min-width="92" show-overflow-tooltip>
+                    <template #default="{ row }">
+                      {{ row['员工姓名'] ?? row.staff_name ?? '' }}
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="操作" width="58" align="center">
+                    <template #default="{ row }">
+                      <el-button
+                        link
+                        type="danger"
+                        @click="removePickedStaff(staffRowId(row))"
+                      >
+                        删除
+                      </el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </div>
+              <div v-else class="picked-staff-empty">暂未选择人员</div>
+            </div>
+            </div>
             <template #footer>
               <el-button @click="addMemberVisible = false">取消</el-button>
               <el-button type="primary" :loading="addMemberSubmitting" @click="submitAddMember">确定</el-button>
@@ -268,11 +307,12 @@ function parseTeamStaffRowsFromApi(res) {
     const list = Array.isArray(team.staff_list) ? team.staff_list : []
     const gid = team['小组ID'] ?? team.team_id
     const gxTeam = String(team['生产工序'] ?? team.big_process ?? '').trim()
-    const nameTeam = String(team['设备编组'] ?? team.team_name ?? '').trim()
+    const nameTeam = String(team['小组名称'] ?? team['设备编组'] ?? team.team_name ?? '').trim()
     for (const s of list) {
       idx += 1
       const ybh = s['员工编号'] ?? s.staff_no
       const sid = s.id ?? s.ID ?? s.staff_id
+      const rowTeamName = String(s['小组名称'] ?? s.team_name ?? nameTeam).trim()
       rows.push({
         _rowKey: `${gid ?? 't'}-${ybh ?? idx}-${idx}`,
         id: sid,
@@ -280,7 +320,8 @@ function parseTeamStaffRowsFromApi(res) {
         员工姓名: s['员工姓名'] ?? s.staff_name,
         职位: s['职位'] ?? s.position,
         生产工序: gxTeam,
-        设备编组: nameTeam,
+        小组名称: rowTeamName,
+        设备编组: rowTeamName,
       })
     }
   }
@@ -364,10 +405,10 @@ const addMemberForm = reactive({
 })
 
 /** 新增成员:getStaffList 远程搜索选人 */
-const addMemberStaffId = ref(null)
+const addMemberStaffIds = ref([])
 const staffPickerLoading = ref(false)
 const staffPickerOptions = ref([])
-const selectedStaffRow = ref(null)
+const selectedStaffRows = ref([])
 let staffSearchTimer = null
 
 const staffRowId = (s) => s?.id ?? s?.ID
@@ -379,8 +420,7 @@ const mergedStaffOptions = computed(() => {
     const id = staffRowId(s)
     if (id != null && id !== '') map.set(id, s)
   }
-  const sel = selectedStaffRow.value
-  if (sel) {
+  for (const sel of selectedStaffRows.value) {
     const id = staffRowId(sel)
     if (id != null && id !== '' && !map.has(id)) map.set(id, sel)
   }
@@ -393,56 +433,68 @@ const formatStaffOptionLabel = (s) => {
   return `${a} ${b}`.trim() || String(staffRowId(s) ?? '')
 }
 
+const fetchStaffOptionsForAdd = async (query = '') => {
+  staffPickerLoading.value = true
+  try {
+    const res = await getStaffList({
+      search: String(query ?? '').trim(),
+      page: 1,
+      limit: 30,
+    })
+    if (res?.code === 0) {
+      staffPickerOptions.value = res.data?.list ?? []
+    } else {
+      staffPickerOptions.value = []
+    }
+  } catch (e) {
+    console.error(e)
+    staffPickerOptions.value = []
+  } finally {
+    staffPickerLoading.value = false
+  }
+}
+
+const onAddMemberStaffDropdownVisible = async (visible) => {
+  if (!visible) return
+  if (staffPickerOptions.value.length > 0) return
+  await fetchStaffOptionsForAdd('')
+}
+
 const searchStaffForAdd = (query) => {
   if (staffSearchTimer) clearTimeout(staffSearchTimer)
   const q = String(query ?? '').trim()
   if (!q) {
-    staffPickerOptions.value = []
+    fetchStaffOptionsForAdd('')
     return
   }
   staffSearchTimer = setTimeout(async () => {
-    staffPickerLoading.value = true
-    try {
-      const res = await getStaffList({
-        search: q,
-        page: 1,
-        limit: 50,
-      })
-      if (res?.code === 0) {
-        staffPickerOptions.value = res.data?.list ?? []
-      } else {
-        staffPickerOptions.value = []
-      }
-    } catch (e) {
-      console.error(e)
-      staffPickerOptions.value = []
-    } finally {
-      staffPickerLoading.value = false
-    }
+    await fetchStaffOptionsForAdd(q)
   }, 300)
 }
 
-const onAddMemberStaffPick = (id) => {
-  if (id === null || id === undefined || id === '') {
-    selectedStaffRow.value = null
-    addMemberForm['员工编号'] = ''
-    addMemberForm['员工姓名'] = ''
+const onAddMemberStaffPick = (pickedIds) => {
+  const ids = Array.isArray(pickedIds) ? pickedIds : []
+  if (ids.length === 0) {
+    selectedStaffRows.value = []
     return
   }
-  const row = mergedStaffOptions.value.find((x) => staffRowId(x) === id)
-  selectedStaffRow.value = row || null
-  if (row) {
-    addMemberForm['员工编号'] = String(row['员工编号'] ?? '')
-    addMemberForm['员工姓名'] = String(row['员工姓名'] ?? '')
-  } else {
-    addMemberForm['员工编号'] = ''
-    addMemberForm['员工姓名'] = ''
+  const byId = new Map(mergedStaffOptions.value.map((x) => [staffRowId(x), x]))
+  const rows = []
+  for (const id of ids) {
+    const row = byId.get(id)
+    if (row) rows.push(row)
   }
+  selectedStaffRows.value = rows
+}
+
+const removePickedStaff = (id) => {
+  selectedStaffRows.value = selectedStaffRows.value.filter((s) => staffRowId(s) !== id)
+  addMemberStaffIds.value = addMemberStaffIds.value.filter((x) => x !== id)
 }
 
 const resetStaffPicker = () => {
-  addMemberStaffId.value = null
-  selectedStaffRow.value = null
+  addMemberStaffIds.value = []
+  selectedStaffRows.value = []
   staffPickerOptions.value = []
   if (staffSearchTimer) {
     clearTimeout(staffSearchTimer)
@@ -793,26 +845,47 @@ const submitAddMember = async () => {
     return
   }
   syncUniqIdFromAddForm()
-  const ybh = String(addMemberForm['员工编号'] ?? '').trim()
-  const xm = String(addMemberForm['员工姓名'] ?? '').trim()
-  const staffId = addMemberStaffId.value
-  if (!ybh || !xm || staffId === null || staffId === undefined || staffId === '') {
-    ElMessage.warning('请先搜索并选择人员')
+  const picked = selectedStaffRows.value
+  if (!Array.isArray(picked) || picked.length === 0) {
+    ElMessage.warning('请先搜索并选择人员(可多选)')
+    return
+  }
+  const dedupByNo = new Map()
+  for (const row of picked) {
+    const ybh = String(row?.['员工编号'] ?? row?.staff_no ?? '').trim()
+    const xm = String(row?.['员工姓名'] ?? row?.staff_name ?? '').trim()
+    if (!ybh || !xm) continue
+    if (!dedupByNo.has(ybh)) dedupByNo.set(ybh, { ybh, xm })
+  }
+  const staffList = Array.from(dedupByNo.values())
+  if (staffList.length === 0) {
+    ElMessage.warning('所选人员缺少员工编号或姓名,请重新选择')
     return
   }
   addMemberSubmitting.value = true
   try {
-    const res = await AddTeamStaff({
-      team_id: addMemberForm.UniqId,
-      big_process: gx,
-      team_name: team,
-      staff_no: ybh,
-      staff_name: xm,
-      position: addMemberForm['职位'] || '',
-      sys_id: userStore.userInfo?.nickName ?? '',
-    })
-    if (res?.code === 0) {
-      ElMessage.success(res?.msg || '新增成功')
+    const reqs = staffList.map((s) =>
+      AddTeamStaff({
+        team_id: addMemberForm.UniqId,
+        big_process: gx,
+        team_name: team,
+        staff_no: s.ybh,
+        staff_name: s.xm,
+        position: addMemberForm['职位'] || '',
+        sys_id: userStore.userInfo?.nickName ?? '',
+      })
+    )
+    const results = await Promise.allSettled(reqs)
+    let ok = 0
+    let fail = 0
+    for (const r of results) {
+      if (r.status === 'fulfilled' && r.value?.code === 0) ok += 1
+      else fail += 1
+    }
+    if (ok > 0) {
+      ElMessage.success(
+        fail > 0 ? `批量新增完成:成功 ${ok} 人,失败 ${fail} 人` : `批量新增成功:共 ${ok} 人`
+      )
       addMemberVisible.value = false
       if (globalSearchMode.value) {
         const q = (staffSearchInput.value || '').trim()
@@ -826,7 +899,7 @@ const submitAddMember = async () => {
     }
   } catch (e) {
     console.error(e)
-    ElMessage.error('新增失败')
+    ElMessage.error('批量新增失败')
   } finally {
     addMemberSubmitting.value = false
   }
@@ -1067,9 +1140,75 @@ onMounted(() => {
 .add-member-form :deep(.el-select) {
   width: 100%;
 }
+.add-member-dialog-split {
+  display: flex;
+  gap: 16px;
+}
+.add-member-left {
+  flex: 0 0 58%;
+  min-width: 0;
+}
+.add-member-right {
+  flex: 1;
+  min-width: 300px;
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 6px;
+  padding: 10px;
+  box-sizing: border-box;
+  background: var(--el-fill-color-extra-light);
+}
+.picked-title {
+  margin-bottom: 8px;
+  font-size: 13px;
+  font-weight: 600;
+}
 .add-member-select {
   width: 100%;
 }
+.add-member-pick-row {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  width: 100%;
+}
+.add-member-pick-row .add-member-select {
+  flex: 1;
+  min-width: 0;
+}
+.add-member-pick-row :deep(.el-select__wrapper) {
+  min-height: 32px;
+}
+.add-member-pick-row :deep(.el-select__input) {
+  min-width: 120px;
+}
+.picked-staff-table-wrap {
+  margin-top: 0;
+  height: 360px;
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 4px;
+  padding: 0;
+  box-sizing: border-box;
+  background: var(--el-fill-color-blank);
+  overflow: hidden;
+}
+.picked-staff-table :deep(.el-table__header th.el-table__cell) {
+  padding: 4px 0;
+}
+.picked-staff-table :deep(.el-table__body td.el-table__cell) {
+  padding: 4px 0;
+}
+.picked-staff-empty {
+  margin-top: 0;
+  height: 360px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 0 10px;
+  border: 1px dashed var(--el-border-color);
+  border-radius: 4px;
+  color: var(--el-text-color-placeholder);
+  box-sizing: border-box;
+}
 .change-team-hint {
   margin: 0 0 14px;
   font-size: 13px;
@@ -1080,3 +1219,17 @@ onMounted(() => {
   margin-left: 8px;
 }
 </style>
+
+<style>
+/* 小组新增成员:固定弹窗高度,内容超出时在 body 内滚动 */
+.group-add-member-dialog .el-dialog {
+  height: 540px;
+  display: flex;
+  flex-direction: column;
+}
+.group-add-member-dialog .el-dialog__body {
+  flex: 1;
+  min-height: 0;
+  overflow-y: auto;
+}
+</style>