From e92cd56bb56b111c1afe9922191f9897215ff6b0 Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期五, 28 八月 2026 17:55:27 +0800
Subject: [PATCH] 提交数据库和服务器部署说明文档
---
frontend-admin/src/views/StaffGroupDetailView.vue | 103 +++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 87 insertions(+), 16 deletions(-)
diff --git a/frontend-admin/src/views/StaffGroupDetailView.vue b/frontend-admin/src/views/StaffGroupDetailView.vue
index c745d28..f2933ed 100644
--- a/frontend-admin/src/views/StaffGroupDetailView.vue
+++ b/frontend-admin/src/views/StaffGroupDetailView.vue
@@ -15,22 +15,44 @@
</el-card>
<el-card shadow="never">
- <el-table :data="detail.members || []" v-loading="loading" stripe border>
+ <el-form :inline="true" class="member-search" @submit.prevent>
+ <el-form-item label="员工姓名">
+ <el-input v-model="memberFilters.realName" placeholder="请输入员工姓名" clearable style="width: 160px" />
+ </el-form-item>
+ <el-form-item label="联系电话">
+ <el-input v-model="memberFilters.mobile" placeholder="请输入联系电话" clearable style="width: 160px" />
+ </el-form-item>
+ <el-form-item label="所属部门">
+ <el-tree-select
+ v-model="memberFilters.deptId"
+ :data="deptOptions"
+ :props="{ value: 'id', label: 'label', children: 'children' }"
+ check-strictly
+ :render-after-expand="false"
+ placeholder="全部"
+ clearable
+ style="width: 240px"
+ />
+ </el-form-item>
+ </el-form>
+
+ <el-table :data="filteredMembers" v-loading="loading" stripe border>
<el-table-column label="序号" width="64" type="index" />
<el-table-column prop="realName" label="姓名" width="120" show-overflow-tooltip />
<el-table-column prop="staffNo" label="编号" width="140" show-overflow-tooltip />
+ <el-table-column prop="mobileMasked" label="联系电话" width="140" />
<el-table-column prop="deptName" label="所属部门" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right">
<template #default="{ row }">
<div class="table-ops">
<el-tooltip v-if="row.id === detail.leaderMpUserId" content="不能移除负责人" placement="top">
<span class="btn-wrap">
- <el-button type="danger" link disabled>删除</el-button>
+ <el-button type="primary" link disabled>删除</el-button>
</span>
</el-tooltip>
<el-button
v-else
- type="danger"
+ type="primary"
link
:loading="removingId === row.id"
@click="confirmRemoveMember(row)"
@@ -44,14 +66,14 @@
</el-card>
<el-dialog v-model="picker" title="添加成员" width="960px" destroy-on-close>
- <p class="picker-hint">仅展示未加入本分组的员工;勾选后点击「追加成员」加入分组。</p>
+ <p class="picker-hint">已在本分组的成员会默认勾选;勾选后点击「追加成员」加入分组。</p>
<div class="picker-toolbar">
<el-form :inline="true" @submit.prevent="pickerSearch">
<el-form-item label="姓名">
- <el-input v-model="pickerFilters.realName" placeholder="模糊" clearable style="width: 140px" />
+ <el-input v-model="pickerFilters.realName" placeholder="请输入姓名" clearable style="width: 140px" />
</el-form-item>
<el-form-item label="联系电话">
- <el-input v-model="pickerFilters.mobile" placeholder="模糊" clearable style="width: 140px" />
+ <el-input v-model="pickerFilters.mobile" placeholder="请输入联系电话" clearable style="width: 140px" />
</el-form-item>
<el-form-item label="部门">
<el-tree-select
@@ -75,12 +97,13 @@
<el-table
ref="pickerTableRef"
:data="pickerRows"
+ row-key="id"
v-loading="pickerLoading"
stripe
border
@selection-change="onPickerSelectionChange"
>
- <el-table-column type="selection" width="48" />
+ <el-table-column type="selection" width="48" :reserve-selection="true" />
<el-table-column prop="realName" label="姓名" width="120" show-overflow-tooltip />
<el-table-column prop="staffNo" label="编号" width="140" show-overflow-tooltip />
<el-table-column prop="mobileMasked" label="联系电话" width="140" />
@@ -90,11 +113,13 @@
<el-pagination
class="pager"
background
- layout="prev, pager, next"
+ layout="sizes, prev, pager, next"
:total="pickerTotal"
:page-size="pickerSize"
+ :page-sizes="[10, 20, 50, 100]"
v-model:current-page="pickerPage"
@current-change="loadPicker"
+ @size-change="onPickerSizeChange"
/>
<template #footer>
@@ -109,13 +134,14 @@
</template>
<script setup lang="ts">
-import { nextTick, onMounted, reactive, ref } from "vue";
+import { computed, nextTick, onMounted, reactive, ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { useRoute } from "vue-router";
import http from "../api/http";
+import { normalizeFormData } from "../utils/formHelper";
type DeptTree = { id: number; label: string; children?: DeptTree[] };
-type StaffRow = { id: number; uuid: string; realName?: string; staffNo?: string; mobileMasked: string; deptId?: number; deptName?: string };
+type StaffRow = { id: number; uuid: string; realName?: string; staffNo?: string; mobileMasked?: string; deptId?: number; deptName?: string };
const route = useRoute();
@@ -135,7 +161,7 @@
leaderMobile: "",
memberCount: 0,
createTime: "",
- members: [] as { id: number; uuid: string; realName: string; staffNo?: string; deptId?: number; deptName?: string }[],
+ members: [] as { id: number; uuid: string; realName: string; staffNo?: string; mobileMasked?: string; deptId?: number; deptName?: string }[],
});
function formatTime(s: string) {
@@ -189,9 +215,28 @@
mobile: "",
deptId: undefined as number | undefined,
});
+const memberFilters = reactive({
+ realName: "",
+ mobile: "",
+ deptId: undefined as number | undefined,
+});
const pickedMap = ref(new Map<number, StaffRow>());
+const restoringPickerSelection = ref(false);
const deptOptions = ref<DeptTree[]>([]);
+
+const filteredMembers = computed(() => {
+ const keywordName = memberFilters.realName.trim().toLowerCase();
+ const keywordMobile = memberFilters.mobile.trim();
+ const deptId = memberFilters.deptId;
+ return (detail.value.members || []).filter((m) => {
+ const okName =
+ !keywordName || (m.realName || "").toLowerCase().includes(keywordName);
+ const okMobile = !keywordMobile || (m.mobileMasked || "").includes(keywordMobile);
+ const okDept = deptId == null || m.deptId === deptId;
+ return okName && okMobile && okDept;
+ });
+});
function mapDeptSelect(nodes: Record<string, unknown>[]): DeptTree[] {
return nodes.map((n) => ({
@@ -208,7 +253,19 @@
function openAddMembers() {
picker.value = true;
- pickedMap.value = new Map();
+ const map = new Map<number, StaffRow>();
+ for (const m of detail.value.members || []) {
+ map.set(m.id, {
+ id: m.id,
+ uuid: m.uuid,
+ realName: m.realName,
+ staffNo: m.staffNo,
+ mobileMasked: "",
+ deptId: m.deptId,
+ deptName: m.deptName,
+ });
+ }
+ pickedMap.value = map;
pickerPage.value = 1;
loadPicker();
}
@@ -221,7 +278,6 @@
realName: pickerFilters.realName?.trim() || undefined,
mobile: pickerFilters.mobile?.trim() || undefined,
status: 1,
- excludeStaffGroupId: id,
};
}
@@ -232,19 +288,26 @@
pickerRows.value = (data.result || []) as StaffRow[];
pickerTotal.value = Number(data.total) || 0;
await nextTick();
+ restoringPickerSelection.value = true;
+ pickerTableRef.value?.clearSelection?.();
for (const r of pickerRows.value) {
if (pickedMap.value.has(r.id)) {
pickerTableRef.value?.toggleRowSelection?.(r, true);
}
}
+ restoringPickerSelection.value = false;
} catch (e: unknown) {
ElMessage.error(e instanceof Error ? e.message : "加载失败");
} finally {
+ restoringPickerSelection.value = false;
pickerLoading.value = false;
}
}
function onPickerSelectionChange(cur: StaffRow[]) {
+ if (restoringPickerSelection.value) {
+ return;
+ }
const idsOnPage = new Set(pickerRows.value.map((x) => x.id));
const selectedOnPage = new Set(cur.map((x) => x.id));
for (const rid of idsOnPage) {
@@ -270,6 +333,12 @@
loadPicker();
}
+function onPickerSizeChange(nextSize: number) {
+ pickerSize.value = nextSize;
+ pickerPage.value = 1;
+ loadPicker();
+}
+
function clearPickerSelection() {
pickedMap.value = new Map();
pickerTableRef.value?.clearSelection?.();
@@ -281,14 +350,16 @@
}
async function submitAppendMembers() {
- const memberIds = Array.from(pickedMap.value.keys());
+ const existingIds = new Set((detail.value.members || []).map((m) => m.id));
+ const memberIds = Array.from(pickedMap.value.keys()).filter((id) => !existingIds.has(id));
if (memberIds.length === 0) {
- ElMessage.warning("请至少选择一名待追加的成员");
+ ElMessage.warning("没有可追加的新成员");
return;
}
saving.value = true;
try {
- await http.post(`/sys/staff-groups/${id}/members`, { uuid, memberIds });
+ const payload = normalizeFormData({ uuid, memberIds });
+ await http.post(`/sys/staff-groups/${id}/members`, payload);
ElMessage.success("已追加成员");
picker.value = false;
pickedMap.value = new Map();
--
Gitblit v1.7.1