明树Git Lab

Commit 7ffc99c8 authored by zfp1's avatar zfp1

update

parent 33cec7e4
......@@ -278,8 +278,8 @@
:data="item.data"
:props="
item.props || {
label: 'label',
value: 'value',
label: 'name',
value: 'id',
children: 'children',
}
"
......@@ -391,31 +391,28 @@
/>
</el-form-item>
</el-col>
<!-- 表单操作按钮 -->
<el-form-item v-if="config.showButtons !== false" class="form-buttons">
<el-button
v-if="config.showSubmit !== false"
type="primary"
:loading="config.submitLoading"
@click="handleSubmit"
>
{{ config.submitText || "提交" }}
</el-button>
<el-button
v-for="btn in config.customButtons"
:key="btn.key"
:type="btn.type"
:loading="btn.loading"
@click="btn.handler"
>
{{ btn.text }}
</el-button>
</el-form-item>
</el-row>
<!-- 表单操作按钮 -->
<el-form-item v-if="config.showButtons !== false" class="form-buttons">
<el-button v-if="config.showReset !== false" @click="handleReset">
{{ config.resetText || "重置" }}
</el-button>
<el-button
v-if="config.showSubmit !== false"
type="primary"
:loading="config.submitLoading"
@click="handleSubmit"
>
{{ config.submitText || "提交" }}
</el-button>
<el-button
v-for="btn in config.customButtons"
:key="btn.key"
:type="btn.type"
:loading="btn.loading"
@click="btn.handler"
>
{{ btn.text }}
</el-button>
</el-form-item>
</el-form>
</div>
</template>
......
......@@ -2,7 +2,9 @@
<div class="common-table">
<div class="table-header" v-if="showHeader">
<div class="header-left">
<h3 v-if="title">{{ title }}</h3>
<slot name="header-left">
<h3 v-if="title">{{ title }}</h3>
</slot>
</div>
<div class="header-right">
<slot name="header-actions"></slot>
......@@ -524,6 +526,10 @@ const handleNextClick = (val) => {
margin-bottom: 16px;
.header-left {
flex: 1; /* 占满剩余空间 */
display: flex;
align-items: center;
gap: 12px;
h3 {
margin: 0;
font-size: 18px;
......
......@@ -7,8 +7,8 @@
<div class="add-project-header">
<div class="header-left"></div>
<div class="header-right">
<el-button type="default" v-if="formData.projectLzType === 1" @click="backClick">返回</el-button>
<el-button type="primary" v-if="formData.projectLzType === 1" @click="saveClick('save')">保存</el-button>
<el-button type="default" @click="backClick">返回</el-button>
<el-button type="primary" v-if="formData.projectLzType === 1 || !formData.projectLzType" @click="saveClick('save')">保存</el-button>
<el-button type="primary" v-if="formData.projectLzType === 1" @click="saveClick('submit')">发起立项填报</el-button>
</div>
</div>
......
<template>
<div class="user-manage" v-loading="loading">
<div class="search-form">
<commonForm
v-model="searchForm"
:config="searchConfig"
:items="searchItems"
@submit="handleSearch"
@reset="handleReset"
/>
</div>
<!-- search form moved into table header to align with 新增 button -->
<div class="table-container">
<common-table
:autoHeight="true"
......@@ -19,13 +11,22 @@
:total="total"
:current-page="currentPage"
:page-size="pageSize"
title="角色管理"
title=""
:border="true"
@size-change="handleSizeChange"
@current-page-change="handleCurrentPageChange"
>
<template #header-actions>
<el-button type="primary" @click="handleAdd"> 新增 </el-button>
<template #header-left>
<div style="display:flex;align-items:center;gap:12px;">
<commonForm
v-model="searchForm"
:config="searchConfig"
:items="searchItems"
@submit="handleSearch"
@reset="handleReset"
/>
<el-button type="primary" @click="handleAdd"> 新增 </el-button>
</div>
</template>
<template #operations="{ row, index }">
<el-button type="text" size="small" @click="handleEdit(row, index)">
......@@ -52,7 +53,21 @@
:rules="formRules"
@submit="handleFormSubmit"
@reset="handleFormReset"
/>
>
<template #menus>
<el-tree
:data="treeData"
node-key="id"
:props="{
label: 'name',
children: 'children'
}"
show-checkbox
@check="hanldeSubmit"
ref="treeRef"
/>
</template>
</commonForm>
</el-dialog>
<!-- <el-dialog v-model="menuVisible" title="菜单配置">
<el-tree
......@@ -74,7 +89,7 @@
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance, computed } from "vue";
import { ref, reactive, onMounted, getCurrentInstance, computed, nextTick } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { Plus, Edit, Delete } from "@element-plus/icons-vue";
import commonForm from "@/components/common/commonForm.vue";
......@@ -84,16 +99,16 @@ import { da } from "element-plus/es/locales.mjs";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const treeData = ref([]);
const tree = ref(null);
const treeRef = ref(null);
// 数据转换函数
const convertToTreeData = (apiData) => {
return apiData.map((item) => ({
value: item.id.toString(),
label: item.name,
children: item.children ? convertToTreeData(item.children) : [],
}));
};
// const convertToTreeData = (apiData) => {
// return apiData.map((item) => ({
// value: item.id.toString(),
// label: item.name,
// children: item.children ? convertToTreeData(item.children) : [],
// }));
// };
// 查询表单数据
const searchForm = ref({
......@@ -115,7 +130,7 @@ const searchItems = [
{
type: "input",
prop: "name",
label: "角色名称:",
label: "",
placeholder: "请输入角色名称",
clearable: true,
span: 8,
......@@ -135,6 +150,11 @@ const tableColumns = [
label: "角色名称",
minWidth: 100,
},
{
prop: "key",
label: "角色标识",
minWidth: 100,
},
{
prop: "createdAt",
label: "创建时间",
......@@ -164,6 +184,7 @@ const editIndex = ref(-1);
// 用户表单数据
const roleForm = ref({
name: "",
key: "",
menus: [],
});
......@@ -187,19 +208,19 @@ const formItems = computed(() => [
rules: [{ required: true, message: "请输入角色名称", trigger: "blur" }],
},
{
type: "tree",
type: "input",
prop: "key",
label: "角色标识",
placeholder: "请输入角色英文标识",
span: 24,
required: true,
rules: [{ required: true, message: "请输入角色英文标识", trigger: "blur" }],
},
{
type: "slot",
prop: "menus",
slotName: "menus",
label: "菜单配置",
placeholder: "请选择菜单配置",
data: treeData.value,
clearable: true,
filterable: true,
checkStrictly: true,
renderAfterExpand: false,
showCheckbox: false,
multiple: true,
collapseTags: true,
maxCollapseTags: 2,
span: 24,
},
]);
......@@ -242,11 +263,16 @@ const handleNodeClick = (data, node, element) => {
// 新增用户
const handleAdd = () => {
isEdit.value = false;
dialogTitle.value = "新增用户";
dialogTitle.value = "新增角色";
roleForm.value = {
name: "",
key: "",
menus: [],
};
// 清空树的已选项
if (treeRef.value && treeRef.value.setCheckedKeys) {
treeRef.value.setCheckedKeys([]);
}
dialogVisible.value = true;
};
let currentID = ref();
......@@ -254,7 +280,7 @@ let currentRow = ref();
// 编辑
const handleEdit = (row, index) => {
isEdit.value = true;
dialogTitle.value = "编辑用户";
dialogTitle.value = "编辑角色";
editIndex.value = index;
currentRow.value = row;
proxy.$post({
......@@ -263,6 +289,16 @@ const handleEdit = (row, index) => {
callback: (data) => {
roleForm.value = { ...data };
currentID.value = data.id;
// 如果返回了已选菜单,延后到下一个 DOM 更新周期再设置树的选中项(确保 tree 已渲染)
if (data.menus && treeRef.value && treeRef.value.setCheckedKeys) {
nextTick(() => {
try {
treeRef.value.setCheckedKeys(data.menus);
} catch (e) {
console.warn('setCheckedKeys failed:', e);
}
});
}
},
error: (err) => {
ElMessage.error("编辑失败:", err);
......@@ -296,12 +332,14 @@ const handleDelete = async (row, index) => {
};
const handleFormSubmit = (formData) => {
// 在提交前确保同步树的选中项到 roleForm
hanldeSubmit();
if (isEdit.value) {
// 编辑用户
// 编辑用户 - 以 roleForm.value 为准,附带 id
const updateUser = {
...formData,
...roleForm.value,
id: currentID.value,
menus: Array.isArray(formData.menus) ? formData.menus : [],
};
proxy.$post({
url: "/api/user/role/updateRole",
......@@ -316,9 +354,9 @@ const handleFormSubmit = (formData) => {
},
});
} else {
// 新增角色
// 新增角色 - 以 roleForm.value 为准,包含 menus
const newUser = {
...formData,
...roleForm.value,
};
proxy.$post({
url: "/api/user/role/createRole",
......@@ -344,18 +382,25 @@ const handleDialogClose = () => {
};
const hanldeSubmit = () => {
// 获取当前复选框选中的节点信息
const checkedNodes = tree.value?.getCheckedNodes();
const halfCheckedNodes = tree.value?.getHalfCheckedNodes();
// 使用 treeRef 获取当前勾选节点
const treeInst = treeRef.value;
if (!treeInst) return;
const checkedNodes = treeInst.getCheckedNodes ? treeInst.getCheckedNodes() : [];
const halfCheckedNodes = treeInst.getHalfCheckedNodes
? treeInst.getHalfCheckedNodes()
: [];
// 获取选中节点的ID
const checkedIds = checkedNodes ? checkedNodes.map((node) => node.id) : [];
const halfCheckedIds = halfCheckedNodes
? halfCheckedNodes.map((node) => node.id)
: [];
// 合并所有选中的节点ID(完全选中和半选中的)
const allSelectedIds = [...checkedIds, ...halfCheckedIds];
const allSelectedIds = Array.from(new Set([...checkedIds, ...halfCheckedIds]));
// 把选中的菜单 id 写回表单数据,供提交使用
roleForm.value.menus = allSelectedIds;
return allSelectedIds;
};
// 表格数据
const loadTableData = () => {
......@@ -381,13 +426,13 @@ const loadTableData = () => {
const handleTreeData = () => {
proxy.$post({
url: "/api/user/depart/treeDepart",
url: "/api/user/menu/treeMenu",
data: {
page: currentPage.value,
pageSize: pageSize.value,
},
callback: (data) => {
treeData.value = convertToTreeData(data);
treeData.value = data;
},
error: (err) => {
ElMessage.error("加载数据失败");
......
<template>
<div class="user-manage" v-loading="loading">
<div class="search-form">
<!-- <div class="search-form">
<commonForm
v-model="searchForm"
:config="searchConfig"
......@@ -8,7 +8,7 @@
@submit="handleSearch"
@reset="handleReset"
/>
</div>
</div> -->
<div class="table-container">
<common-table
:autoHeight="true"
......@@ -19,11 +19,20 @@
:total="total"
:current-page="currentPage"
:page-size="pageSize"
title="用户管理"
title=""
:border="true"
@size-change="handleSizeChange"
@current-page-change="handleCurrentPageChange"
>
<template #header-left>
<commonForm
v-model="searchForm"
:config="searchConfig"
:items="searchItems"
@submit="handleSearch"
@reset="handleReset"
/>
</template>
<template #header-actions>
<el-button type="primary" @click="handleAdd">
<!-- <el-icon><Plus /></el-icon> -->
......@@ -83,13 +92,13 @@ const loading = ref(false);
// 数据转换函数
const convertToTreeData = (apiData) => {
return apiData.map((item) => ({
value: item.id.toString(),
label: item.name,
children: item.children ? convertToTreeData(item.children) : [],
}));
};
// const convertToTreeData = (apiData) => {
// return apiData.map((item) => ({
// value: item.id.toString(),
// label: item.name,
// children: item.children ? convertToTreeData(item.children) : [],
// }));
// };
// 查询表单数据
const searchForm = ref({
......@@ -111,19 +120,11 @@ const searchItems = [
{
type: "input",
prop: "name",
label: "用户姓名:",
placeholder: "请输入用户姓名",
clearable: true,
span: 8,
},
{
type: "input",
prop: "mobile",
label: "手机号码:",
placeholder: "请输入手机号码",
label: "",
placeholder: "请输入关键字查询",
clearable: true,
span: 8,
},
span: 12,
}
];
// 表格数据
......@@ -140,6 +141,11 @@ const tableColumns = [
minWidth: 100,
showOverflowTooltip: true,
},
{
prop: "mobile",
label: "手机号码",
minWidth: 100,
},
{
prop: "departs",
label: "所属部门",
......@@ -158,11 +164,6 @@ const tableColumns = [
minWidth: 100,
showOverflowTooltip: true,
},
{
prop: "mobile",
label: "手机号码",
minWidth: 100,
},
{
prop: "createdAt",
label: "创建时间",
......@@ -216,7 +217,8 @@ const loadDepartmentData = () => {
url: "/api/user/depart/treeDepart",
data: {},
callback: (data) => {
departmentData.value = convertToTreeData(data);
// departmentData.value = convertToTreeData(data);
departmentData.value = data;
},
error: (err) => {},
});
......@@ -228,7 +230,7 @@ const loadPositionsData = () => {
url: "/api/user/position/listPosition",
data: {},
callback: (data) => {
positionsData.value = convertToTreeData(data.rows);
positionsData.value = data.rows;
},
error: (err) => {},
});
......@@ -243,7 +245,7 @@ const loadRolesData = () => {
pageSize: 10,
},
callback: (data) => {
rolesData.value = convertToTreeData(data.rows);
rolesData.value = data.rows;
},
error: (err) => {},
});
......@@ -257,7 +259,7 @@ const formItems = computed(() => [
label: "用户姓名",
placeholder: "请输入用户姓名",
// required: true,
span: 12,
span: 24,
// rules: [{ required: true, message: "请输入用户姓名", trigger: "blur" }],
},
{
......@@ -269,12 +271,12 @@ const formItems = computed(() => [
clearable: true,
filterable: true,
checkStrictly: true,
renderAfterExpand: false,
renderAfterExpand: true,
showCheckbox: false,
multiple: true,
multiple: false,
collapseTags: true,
maxCollapseTags: 2,
span: 12,
span: 24,
},
{
type: "tree",
......@@ -290,7 +292,7 @@ const formItems = computed(() => [
multiple: true,
collapseTags: true,
maxCollapseTags: 2,
span: 12,
span: 24,
},
{
type: "tree",
......@@ -306,23 +308,23 @@ const formItems = computed(() => [
multiple: true,
collapseTags: true,
maxCollapseTags: 2,
span: 12,
span: 24,
},
{
type: "input",
prop: "mobile",
label: "手机号码",
placeholder: "请输入手机号码",
span: 12,
span: 24,
},
{
type: "radio",
prop: "enable",
label: "状态",
span: 12,
span: 24,
options: [
{ label: "启用", value: "0" },
{ label: "停用", value: "1" },
{ label: "启用", value: 0 },
{ label: "停用", value: 1 },
],
},
]);
......@@ -529,7 +531,7 @@ onMounted(() => {
<style scoped lang="less">
.user-manage {
padding: 20px;
padding: 8px 8px 20px 8px;
background: rgba(157, 188, 218, 0.1);
height: 100%;
display: flex;
......@@ -548,8 +550,10 @@ onMounted(() => {
.table-container {
background: rgba(255, 255, 255, 0.9);
border-radius: 8px;
padding: 20px;
padding: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding-left: 0px;
padding-top: 2px;
}
}
</style>
export default {
baseUrl: "http://10.40.8.9:3000"
baseUrl: "http://localhost:3000"
};
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment