优化tenant模式
This commit is contained in:
parent
b8761ad3e4
commit
108ec50978
@ -7,6 +7,13 @@ export function getModulesList() {
|
||||
});
|
||||
}
|
||||
|
||||
export function getTenantList() {
|
||||
return request({
|
||||
url: '/admin/modules/getTenantList',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function getModuleDetail(id) {
|
||||
return request({
|
||||
url: `/admin/modules/${id}`,
|
||||
|
||||
@ -224,7 +224,7 @@ const handleCommand = async (command) => {
|
||||
// 遍历 localStorage
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (key && key.startsWith('menu_cache_')) {
|
||||
if (key && key.startsWith('menu_')) {
|
||||
menuCacheKeys.push(key);
|
||||
}
|
||||
}
|
||||
@ -237,7 +237,7 @@ const handleCommand = async (command) => {
|
||||
const sessionMenuCacheKeys: string[] = [];
|
||||
for (let i = 0; i < sessionStorage.length; i++) {
|
||||
const key = sessionStorage.key(i);
|
||||
if (key && key.startsWith('menu_cache_')) {
|
||||
if (key && key.startsWith('menu_')) {
|
||||
sessionMenuCacheKeys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ export const useMenuStore = defineStore('menu', () => {
|
||||
try {
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
|
||||
const loginType = userInfo.type || 'user';
|
||||
const roleId = userInfo.role || 0;
|
||||
const roleId = userInfo.group_id || 0;
|
||||
return `menu_cache_${loginType}_${roleId}`;
|
||||
} catch (e) {
|
||||
return 'menu_cache_default';
|
||||
@ -113,7 +113,7 @@ export const useMenuStore = defineStore('menu', () => {
|
||||
try {
|
||||
const userInfo = getUserInfo();
|
||||
const loginType = userInfo.type || 'user';
|
||||
const roleId = userInfo.role || 0;
|
||||
const roleId = userInfo.group_id || 0;
|
||||
|
||||
let res;
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="category-manager">
|
||||
<div class="category-list" v-loading="loading">
|
||||
<div v-if="filteredCategories.length === 0" class="empty-state"></div>
|
||||
<div v-if="filteredCategories.length === 0" class="empty-state">
|
||||
<el-empty description="暂无文章分类" />
|
||||
</div>
|
||||
|
||||
<div v-else class="category-tree">
|
||||
<category-node
|
||||
|
||||
112
src/views/basicSettings/tenants/components/adduser.vue
Normal file
112
src/views/basicSettings/tenants/components/adduser.vue
Normal file
@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" title="添加用户" width="600px" @closed="handleClosed"
|
||||
destroy-on-close>
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px" v-loading="loading"
|
||||
style="padding: 20px">
|
||||
<el-form-item label="用户名" prop="account">
|
||||
<el-input v-model="formData.account" placeholder="请输入用户名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input v-model="formData.password" type="password" placeholder="请输入密码" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入昵称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="formData.phone" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="1">启用</el-radio>
|
||||
<el-radio :label="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submitForm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { addUser } from '@/api/user';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const formRef = ref();
|
||||
const currentTenantId = ref<number | null>(null);
|
||||
|
||||
const formData = reactive({
|
||||
account: '',
|
||||
password: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
status: 1
|
||||
});
|
||||
|
||||
const rules = {
|
||||
account: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '请输入密码', trigger: 'blur', min: 6 }],
|
||||
phone: [
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
|
||||
]
|
||||
};
|
||||
|
||||
const open = (tenantId: number) => {
|
||||
visible.value = true;
|
||||
currentTenantId.value = tenantId;
|
||||
Object.assign(formData, {
|
||||
account: '',
|
||||
password: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
status: 1
|
||||
});
|
||||
};
|
||||
|
||||
const submitForm = async () => {
|
||||
if (!formRef.value) return;
|
||||
|
||||
await formRef.value.validate();
|
||||
|
||||
submitting.value = true;
|
||||
|
||||
try {
|
||||
const submitData = {
|
||||
...formData,
|
||||
tenant_id: currentTenantId.value
|
||||
};
|
||||
const res = await addUser(submitData);
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('添加成功');
|
||||
visible.value = false;
|
||||
emit('success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交失败', error);
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleClosed = () => {
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
@ -62,9 +62,10 @@
|
||||
<el-table-column label="操作" width="280" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<!-- <el-button size="small" @click="handleQualification(scope.row)">资质</el-button> -->
|
||||
<el-button size="small" @click="handlePreview(scope.row)">查看</el-button>
|
||||
<el-button size="small" @click="editRef.open(scope.row.id)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||
<el-button text size="small" @click="handleAddUser(scope.row)">增加用户</el-button>
|
||||
<el-button text size="small" @click="handlePreview(scope.row)">查看</el-button>
|
||||
<el-button text size="small" @click="editRef.open(scope.row.id)">编辑</el-button>
|
||||
<el-button text size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -72,6 +73,7 @@
|
||||
<EditModal ref="editRef" @success="refresh" />
|
||||
<DetailDrawer ref="detailRef" />
|
||||
<Qualification ref="qualificationRef" />
|
||||
<AddUser ref="addUserRef" />
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-bar">
|
||||
@ -88,6 +90,7 @@ import { useRouter } from 'vue-router';
|
||||
import EditModal from './components/edit.vue';
|
||||
import DetailDrawer from './components/detail.vue';
|
||||
import Qualification from './components/qualification.vue';
|
||||
import AddUser from './components/adduser.vue';
|
||||
|
||||
const total = ref(0);
|
||||
const page = ref(1);
|
||||
@ -98,6 +101,7 @@ const tenants = ref([]);
|
||||
|
||||
const editRef = ref();
|
||||
const detailRef = ref();
|
||||
const addUserRef = ref();
|
||||
const qualificationRef = ref();
|
||||
|
||||
// 删除
|
||||
@ -119,6 +123,11 @@ const handlePreview = (row: any) => {
|
||||
detailRef.value.open(row.id);
|
||||
};
|
||||
|
||||
//增加用户
|
||||
const handleAddUser = (row: any) => {
|
||||
addUserRef.value.open(row.id);
|
||||
};
|
||||
|
||||
// 资质
|
||||
const handleQualification = (row: any) => {
|
||||
qualificationRef.value.open(row.id);
|
||||
|
||||
@ -146,7 +146,7 @@ import {
|
||||
Moon,
|
||||
} from "@element-plus/icons-vue";
|
||||
|
||||
import { getModulesList } from "@/api/modules";
|
||||
import { getTenantList } from "@/api/modules";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useMenuStore } from "@/stores/menu";
|
||||
|
||||
@ -271,7 +271,7 @@ async function handleLogout() {
|
||||
// 加载模块列表
|
||||
async function loadModules() {
|
||||
try {
|
||||
const res = await getModulesList();
|
||||
const res = await getTenantList();
|
||||
if (res.code === 200) {
|
||||
const list = res.data?.list || [];
|
||||
const filteredList = list
|
||||
|
||||
@ -33,32 +33,48 @@
|
||||
<el-divider></el-divider>
|
||||
|
||||
<!-- 树形表格 -->
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="menuTree"
|
||||
style="width: 100%"
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="menuTree"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
border
|
||||
v-loading="loading"
|
||||
element-loading-text="正在加载..."
|
||||
:tree-props="{
|
||||
children: 'children',
|
||||
hasChildren: 'hasChildren'
|
||||
hasChildren: 'hasChildren',
|
||||
}"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
>
|
||||
<el-table-column prop="title" label="菜单名称" width="200">
|
||||
<template #default="scope">
|
||||
<div class="menu-item">
|
||||
<i v-if="scope.row.icon" :class="scope.row.icon" class="menu-icon"></i>
|
||||
<i
|
||||
v-if="scope.row.icon"
|
||||
:class="scope.row.icon"
|
||||
class="menu-icon"
|
||||
></i>
|
||||
<span>{{ scope.row.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="id"
|
||||
width="60"
|
||||
label="ID"
|
||||
align="center"
|
||||
></el-table-column>
|
||||
|
||||
<el-table-column prop="path" label="路由地址"></el-table-column>
|
||||
|
||||
<el-table-column prop="MenuType" label="菜单类型" width="120" align="center">
|
||||
<el-table-column
|
||||
prop="MenuType"
|
||||
label="菜单类型"
|
||||
width="120"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-tag :type="getMenuTypeTagType(scope.row.type)">
|
||||
{{ getMenuTypeTitle(scope.row.type) }}
|
||||
@ -66,24 +82,34 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="sort" label="排序" width="80" align="center"></el-table-column>
|
||||
<el-table-column
|
||||
prop="sort"
|
||||
label="排序"
|
||||
width="80"
|
||||
align="center"
|
||||
></el-table-column>
|
||||
|
||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="1"
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
@click.stop
|
||||
:disabled="!scope.row.id"
|
||||
:disabled="!scope.row.id"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="280" fixed="right" align="center">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text @click.stop="handleAddSubMenu(scope.row)" :disabled="scope.row.type === 3">
|
||||
<el-button
|
||||
size="small"
|
||||
text
|
||||
@click.stop="handleAddSubMenu(scope.row)"
|
||||
:disabled="scope.row.type === 3"
|
||||
>
|
||||
<el-icon>
|
||||
<CirclePlus />
|
||||
</el-icon>
|
||||
@ -97,7 +123,12 @@
|
||||
<span>编辑</span>
|
||||
</el-button>
|
||||
|
||||
<el-button size="small" text type="danger" @click.stop="handleDeleteMenu(scope.row)">
|
||||
<el-button
|
||||
size="small"
|
||||
text
|
||||
type="danger"
|
||||
@click.stop="handleDeleteMenu(scope.row)"
|
||||
>
|
||||
<el-icon>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
@ -108,16 +139,37 @@
|
||||
</el-table>
|
||||
|
||||
<!-- 引入编辑组件 -->
|
||||
<MenuEdit v-model:visible="dialogVisible" :menu="dialogMenu" :parent-menu-options="parentMenuOptions"
|
||||
:dialog-type="dialogType" :parent-title="dialogParentTitle" @save="handleMenuSave" @cancel="handleMenuCancel" />
|
||||
<MenuEdit
|
||||
v-model:visible="dialogVisible"
|
||||
:menu="dialogMenu"
|
||||
:parent-menu-options="parentMenuOptions"
|
||||
:dialog-type="dialogType"
|
||||
:parent-title="dialogParentTitle"
|
||||
@save="handleMenuSave"
|
||||
@cancel="handleMenuCancel"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import { ElMessage, ElMessageBox, ElForm } from "element-plus";
|
||||
import { Plus, CirclePlus, Edit, Delete, Refresh, FolderOpened, Folder } from "@element-plus/icons-vue";
|
||||
import { getAllMenus, updateMenuStatus, createMenu, updateMenu, deleteMenu } from "@/api/menu";
|
||||
import {
|
||||
Plus,
|
||||
CirclePlus,
|
||||
Edit,
|
||||
Delete,
|
||||
Refresh,
|
||||
FolderOpened,
|
||||
Folder,
|
||||
} from "@element-plus/icons-vue";
|
||||
import {
|
||||
getAllMenus,
|
||||
updateMenuStatus,
|
||||
createMenu,
|
||||
updateMenu,
|
||||
deleteMenu,
|
||||
} from "@/api/menu";
|
||||
import MenuEdit from "./components/edit.vue";
|
||||
|
||||
// 定义菜单数据类型
|
||||
@ -146,10 +198,8 @@ const tableRef = ref<any>(null);
|
||||
// 对话框相关变量
|
||||
const dialogVisible = ref(false);
|
||||
const dialogMenu = ref<Partial<Menu> | null>(null);
|
||||
const dialogType = ref<'add' | 'edit' | 'addSub'>('add');
|
||||
const dialogParentTitle = ref('');
|
||||
|
||||
|
||||
const dialogType = ref<"add" | "edit" | "addSub">("add");
|
||||
const dialogParentTitle = ref("");
|
||||
|
||||
// 父级菜单选项
|
||||
const parentMenuOptions = ref<Menu[]>([]);
|
||||
@ -196,9 +246,9 @@ async function refresh() {
|
||||
loading.value = true;
|
||||
try {
|
||||
await fetchMenus();
|
||||
ElMessage.success('刷新成功');
|
||||
ElMessage.success("刷新成功");
|
||||
} catch (error) {
|
||||
ElMessage.error('刷新失败');
|
||||
ElMessage.error("刷新失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@ -241,10 +291,10 @@ function collapseAll() {
|
||||
// 处理行点击事件 - 展开/折叠树形结构
|
||||
const handleRowClick = (row: Menu) => {
|
||||
if (!tableRef.value) return;
|
||||
|
||||
|
||||
// 检查该行是否有子节点
|
||||
const hasChildren = row.children && row.children.length > 0;
|
||||
|
||||
|
||||
if (hasChildren) {
|
||||
// 切换展开/折叠状态(toggleRowExpansion 会自动切换当前状态)
|
||||
tableRef.value.toggleRowExpansion(row);
|
||||
@ -283,30 +333,28 @@ const handleStatusChange = async (menu: Menu) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 添加子菜单
|
||||
const handleAddSubMenu = (parentMenu: Menu) => {
|
||||
dialogType.value = 'addSub';
|
||||
dialogType.value = "addSub";
|
||||
dialogParentTitle.value = parentMenu.title;
|
||||
dialogMenu.value = {
|
||||
id: 0,
|
||||
pid: parentMenu.id,
|
||||
title: '',
|
||||
path: '',
|
||||
component_path: '',
|
||||
icon: '',
|
||||
title: "",
|
||||
path: "",
|
||||
component_path: "",
|
||||
icon: "",
|
||||
sort: 0,
|
||||
status: 1,
|
||||
type: parentMenu.type === 2 ? 1 : parentMenu.type,
|
||||
permission: '',
|
||||
permission: "",
|
||||
};
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑菜单
|
||||
const handleEditMenu = (menu: Menu) => {
|
||||
dialogType.value = 'edit';
|
||||
dialogType.value = "edit";
|
||||
dialogMenu.value = { ...menu };
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
@ -320,7 +368,7 @@ const handleDeleteMenu = (menu: Menu) => {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}
|
||||
},
|
||||
).then(async () => {
|
||||
try {
|
||||
const result = await deleteMenu(menu.id);
|
||||
@ -338,7 +386,7 @@ const handleDeleteMenu = (menu: Menu) => {
|
||||
|
||||
// 添加菜单
|
||||
const handleAddMenu = () => {
|
||||
dialogType.value = 'add';
|
||||
dialogType.value = "add";
|
||||
dialogMenu.value = null;
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
@ -355,9 +403,9 @@ const handleMenuSave = async (menu: Partial<Menu>) => {
|
||||
if (Array.isArray(pidValue)) {
|
||||
pidValue = pidValue.length > 0 ? pidValue[pidValue.length - 1] : null;
|
||||
}
|
||||
|
||||
|
||||
// 强制转换为整数
|
||||
if (pidValue === null || pidValue === undefined || pidValue === '') {
|
||||
if (pidValue === null || pidValue === undefined || pidValue === "") {
|
||||
payload.pid = 0;
|
||||
} else {
|
||||
const parsedPid = parseInt(String(pidValue), 10);
|
||||
@ -370,20 +418,22 @@ const handleMenuSave = async (menu: Partial<Menu>) => {
|
||||
|
||||
// 最终验证:确保 payload.pid 是数字类型,不是数组
|
||||
if (Array.isArray(payload.pid)) {
|
||||
payload.pid = Array.isArray(payload.pid) && payload.pid.length > 0
|
||||
? parseInt(String(payload.pid[payload.pid.length - 1]), 10) || 0
|
||||
: 0;
|
||||
payload.pid =
|
||||
Array.isArray(payload.pid) && payload.pid.length > 0
|
||||
? parseInt(String(payload.pid[payload.pid.length - 1]), 10) || 0
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
// 确保是数字类型
|
||||
if (typeof payload.pid !== 'number') {
|
||||
if (typeof payload.pid !== "number") {
|
||||
payload.pid = parseInt(String(payload.pid), 10) || 0;
|
||||
}
|
||||
|
||||
if (menu.id === 0) {
|
||||
// 新增菜单
|
||||
const result = await createMenu(payload as Menu);
|
||||
if (result.code === 200) { // 修改这里,检查 code 而不是 success
|
||||
if (result.code === 200) {
|
||||
// 修改这里,检查 code 而不是 success
|
||||
ElMessage.success(result.msg || "菜单添加成功");
|
||||
dialogVisible.value = false;
|
||||
await fetchMenus();
|
||||
@ -393,7 +443,8 @@ const handleMenuSave = async (menu: Partial<Menu>) => {
|
||||
} else {
|
||||
// 编辑菜单
|
||||
const result = await updateMenu(menu.id!, payload as Menu);
|
||||
if (result.code === 200) { // 修改这里,检查 code 而不是 success
|
||||
if (result.code === 200) {
|
||||
// 修改这里,检查 code 而不是 success
|
||||
ElMessage.success(result.msg || "更新成功");
|
||||
dialogVisible.value = false;
|
||||
await fetchMenus();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user