From 9d4cbff7464ebfd7803c2ce53ded49d0f73fe011 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期一, 29 六月 2026 20:33:55 +0800
Subject: [PATCH] 初步调整fix
---
frontend-admin/src/views/DeptManageView.vue | 242 +++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 235 insertions(+), 7 deletions(-)
diff --git a/frontend-admin/src/views/DeptManageView.vue b/frontend-admin/src/views/DeptManageView.vue
index 2ea82db..7cffb4c 100644
--- a/frontend-admin/src/views/DeptManageView.vue
+++ b/frontend-admin/src/views/DeptManageView.vue
@@ -59,6 +59,23 @@
<el-form-item label="部门名称" required>
<el-input v-model="form.deptName" maxlength="128" placeholder="请输入" />
</el-form-item>
+ <el-form-item label="部门负责人">
+ <div class="leader-picker">
+ <el-button @click="openLeaderPicker">选择负责人</el-button>
+ <el-button v-if="selectedLeaders.length" link type="primary" @click="clearLeaders">清空</el-button>
+ <div v-if="selectedLeaders.length" class="leader-tags">
+ <el-tag
+ v-for="leader in selectedLeaders"
+ :key="leader.id"
+ closable
+ @close="removeLeader(leader.id)"
+ >
+ {{ leader.realName }}
+ </el-tag>
+ </div>
+ <div v-else class="leader-empty">未选择负责人</div>
+ </div>
+ </el-form-item>
<el-form-item label="部门状态" required>
<el-radio-group v-model="form.status">
<el-radio :label="1">启用</el-radio>
@@ -78,13 +95,60 @@
</el-button>
</template>
</el-dialog>
+
+ <el-dialog v-model="leaderDlg" title="选择部门负责人" width="760px" destroy-on-close @closed="resetLeaderDialog">
+ <el-form class="leader-search" @submit.prevent="loadLeaderCandidates">
+ <el-form-item label="姓名">
+ <el-input v-model="leaderFilters.realName" placeholder="请输入姓名" clearable />
+ </el-form-item>
+ <el-form-item label="联系方式">
+ <el-input v-model="leaderFilters.mobile" placeholder="请输入联系方式" clearable />
+ </el-form-item>
+ <el-form-item>
+ <el-button @click="resetLeaderSearch">重置</el-button>
+ <el-button type="primary" native-type="submit">搜索</el-button>
+ </el-form-item>
+ </el-form>
+ <el-table
+ ref="leaderTableRef"
+ :data="leaderRows"
+ v-loading="leaderLoading"
+ row-key="id"
+ border
+ height="360"
+ @selection-change="onLeaderSelectionChange"
+ >
+ <el-table-column type="selection" width="48" reserve-selection />
+ <el-table-column prop="realName" label="姓名" min-width="120" show-overflow-tooltip />
+ <el-table-column prop="mobileMasked" label="联系方式" width="140" />
+ <el-table-column prop="deptPath" label="所属部门" min-width="220" show-overflow-tooltip />
+ </el-table>
+ <div class="leader-pager">
+ <div>已选 {{ leaderSelection.length }} 人</div>
+ <el-pagination
+ background
+ layout="sizes, prev, pager, next"
+ :total="leaderTotal"
+ :page-size="leaderSize"
+ :page-sizes="[10, 20, 50, 100]"
+ v-model:current-page="leaderPage"
+ @current-change="loadLeaderCandidates"
+ @size-change="onLeaderSizeChange"
+ />
+ </div>
+ <template #footer>
+ <el-button @click="leaderDlg = false">取消</el-button>
+ <el-button type="primary" @click="confirmLeaders">确定</el-button>
+ </template>
+ </el-dialog>
</div>
</template>
<script setup lang="ts">
-import { computed, ref } from "vue";
+import { computed, nextTick, ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import http from "../api/http";
+import { normalizeFormData } from "../utils/formHelper";
type DeptNode = {
id: number;
@@ -94,6 +158,8 @@
sortOrder?: number;
status: number;
level: number;
+ leaderIds?: number[];
+ leaderNames?: string;
children?: DeptNode[];
};
@@ -116,12 +182,25 @@
const parentDeptName = ref("");
const childParentId = ref<number | null>(null);
const pendingExpandRowKey = ref<string | null>(null);
+type StaffOption = { id: number; realName: string; deptPath: string; mobileMasked?: string };
+const staffOptions = ref<StaffOption[]>([]);
+const selectedLeaders = ref<StaffOption[]>([]);
+const leaderDlg = ref(false);
+const leaderTableRef = ref();
+const leaderLoading = ref(false);
+const leaderRows = ref<StaffOption[]>([]);
+const leaderSelection = ref<StaffOption[]>([]);
+const leaderPage = ref(1);
+const leaderSize = ref(10);
+const leaderTotal = ref(0);
+const leaderFilters = ref({ realName: "", mobile: "" });
const form = ref({
uuid: "",
deptName: "",
status: 1,
parentId: undefined as number | undefined,
+ leaderIds: [] as number[],
});
const dlgTitle = computed(() => {
@@ -210,13 +289,38 @@
expandedRowKeys.value = expandedRowKeys.value.filter(id => !collapsedKeys.has(id));
}
+async function loadStaffOptionsByIds(ids: number[]) {
+ const missingIds = ids.filter(id => !staffOptions.value.some(s => s.id === id));
+ if (!missingIds.length) return;
+ try {
+ const { data } = await http.get("/sys/staff", { params: { page: 1, size: 1000, status: 1 } });
+ const staffList = (data.result || []) as { id: number; realName: string; deptPath?: string }[];
+ staffOptions.value = staffList.map(s => ({
+ id: s.id,
+ realName: s.realName,
+ deptPath: s.deptPath || '未分配部门',
+ }));
+ } catch (e: unknown) {
+ console.error("加载员工列表失败", e);
+ }
+}
+
+function syncSelectedLeadersFromIds(ids: number[], namesText?: string) {
+ const names = (namesText || "").split("、").filter(Boolean);
+ selectedLeaders.value = ids.map((id, index) => {
+ const cached = staffOptions.value.find(s => s.id === id);
+ return cached || { id, realName: names[index] || `员工${id}`, deptPath: "" };
+ });
+}
+
function openAddRoot() {
mode.value = "root";
editingId.value = null;
editingIsRoot.value = true;
parentDeptName.value = "";
childParentId.value = null;
- form.value = { uuid: "", deptName: "", status: 1, parentId: undefined };
+ form.value = { uuid: "", deptName: "", status: 1, parentId: undefined, leaderIds: [] };
+ selectedLeaders.value = [];
dlg.value = true;
}
@@ -227,23 +331,28 @@
editingIsRoot.value = false;
parentDeptName.value = row.deptName;
childParentId.value = row.id;
- form.value = { uuid: "", deptName: "", status: 1, parentId: row.id };
+ form.value = { uuid: "", deptName: "", status: 1, parentId: row.id, leaderIds: [] };
+ selectedLeaders.value = [];
dlg.value = true;
}
-function openEdit(row: DeptNode) {
+async function openEdit(row: DeptNode) {
mode.value = "edit";
editingId.value = row.id;
const root = !row.parentId || row.parentId === 0;
editingIsRoot.value = root;
parentDeptName.value = "";
childParentId.value = null;
+ const leaderIds = Array.isArray(row.leaderIds) ? row.leaderIds : [];
+ await loadStaffOptionsByIds(leaderIds);
form.value = {
uuid: row.uuid,
deptName: row.deptName,
status: row.status,
parentId: root ? undefined : row.parentId,
+ leaderIds,
};
+ syncSelectedLeadersFromIds(leaderIds, row.leaderNames);
if (!root) {
const disabled = new Set(collectDescendantIds(row));
parentTreeSelectData.value = toTreeSelectData(treeData.value, disabled);
@@ -258,6 +367,89 @@
parentTreeSelectData.value = [];
}
+function openLeaderPicker() {
+ leaderSelection.value = [...selectedLeaders.value];
+ leaderDlg.value = true;
+ loadLeaderCandidates();
+}
+
+async function loadLeaderCandidates() {
+ leaderLoading.value = true;
+ try {
+ const { data } = await http.get("/sys/staff", {
+ params: {
+ page: leaderPage.value,
+ size: leaderSize.value,
+ status: 1,
+ realName: leaderFilters.value.realName.trim() || undefined,
+ mobile: leaderFilters.value.mobile.trim() || undefined,
+ },
+ });
+ leaderRows.value = ((data.result || []) as StaffOption[]).map(s => ({
+ ...s,
+ deptPath: s.deptPath || "未分配部门",
+ }));
+ leaderTotal.value = Number(data.total) || 0;
+ await nextTick();
+ syncLeaderTableSelection();
+ } catch (e: unknown) {
+ ElMessage.error(e instanceof Error ? e.message : "加载员工失败");
+ } finally {
+ leaderLoading.value = false;
+ }
+}
+
+function syncLeaderTableSelection() {
+ const selectedIds = new Set(leaderSelection.value.map(s => s.id));
+ leaderRows.value.forEach(row => {
+ leaderTableRef.value?.toggleRowSelection(row, selectedIds.has(row.id));
+ });
+}
+
+function onLeaderSelectionChange(selection: StaffOption[]) {
+ const currentPageIds = new Set(leaderRows.value.map(s => s.id));
+ const kept = leaderSelection.value.filter(s => !currentPageIds.has(s.id));
+ const merged = [...kept, ...selection];
+ const seen = new Set<number>();
+ leaderSelection.value = merged.filter(item => {
+ if (seen.has(item.id)) return false;
+ seen.add(item.id);
+ return true;
+ });
+}
+
+function resetLeaderSearch() {
+ leaderFilters.value = { realName: "", mobile: "" };
+ leaderPage.value = 1;
+ loadLeaderCandidates();
+}
+
+function onLeaderSizeChange(v: number) {
+ leaderSize.value = v;
+ leaderPage.value = 1;
+ loadLeaderCandidates();
+}
+
+function confirmLeaders() {
+ selectedLeaders.value = [...leaderSelection.value];
+ form.value.leaderIds = selectedLeaders.value.map(s => s.id);
+ leaderDlg.value = false;
+}
+
+function resetLeaderDialog() {
+ leaderRows.value = [];
+}
+
+function removeLeader(id: number) {
+ selectedLeaders.value = selectedLeaders.value.filter(s => s.id !== id);
+ form.value.leaderIds = selectedLeaders.value.map(s => s.id);
+}
+
+function clearLeaders() {
+ selectedLeaders.value = [];
+ form.value.leaderIds = [];
+}
+
async function submitDlg() {
if (!form.value.deptName.trim()) {
ElMessage.warning("请填写部门名称");
@@ -266,11 +458,13 @@
saving.value = true;
try {
if (mode.value === "root") {
- await http.post("/sys/dept-manage", {
+ const payload = normalizeFormData({
parentId: 0,
deptName: form.value.deptName.trim(),
status: form.value.status,
+ leaderIds: form.value.leaderIds,
});
+ await http.post("/sys/dept-manage", payload);
ElMessage.success("已创建");
} else if (mode.value === "child") {
if (childParentId.value == null) {
@@ -278,17 +472,20 @@
return;
}
pendingExpandRowKey.value = toRowKey(childParentId.value);
- await http.post("/sys/dept-manage", {
+ const payload = normalizeFormData({
parentId: childParentId.value,
deptName: form.value.deptName.trim(),
status: form.value.status,
+ leaderIds: form.value.leaderIds,
});
+ await http.post("/sys/dept-manage", payload);
ElMessage.success("已创建");
} else if (editingId.value != null) {
const body: Record<string, unknown> = {
uuid: form.value.uuid,
deptName: form.value.deptName.trim(),
status: form.value.status,
+ leaderIds: form.value.leaderIds,
};
if (!editingIsRoot.value) {
if (form.value.parentId == null) {
@@ -297,7 +494,8 @@
}
body.parentId = form.value.parentId;
}
- await http.put(`/sys/dept-manage/${editingId.value}`, body);
+ const payload = normalizeFormData(body);
+ await http.put(`/sys/dept-manage/${editingId.value}`, payload);
ElMessage.success("已保存");
}
dlg.value = false;
@@ -344,6 +542,36 @@
width: 100%;
}
+.leader-picker {
+ width: 100%;
+}
+
+.leader-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ margin-top: 8px;
+}
+
+.leader-empty {
+ color: #909399;
+ font-size: 13px;
+ margin-top: 8px;
+}
+
+.leader-search {
+ display: grid;
+ grid-template-columns: 1fr 1fr auto;
+ gap: 12px;
+}
+
+.leader-pager {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 12px;
+}
+
:deep(.table-ops .el-button) {
font-size: 14px;
}
--
Gitblit v1.7.1