明树Git Lab

Commit 7ffc99c8 authored by zfp1's avatar zfp1

update

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