feat: 租户套餐的菜单支持微调
This commit is contained in:
@@ -98,6 +98,23 @@ const TenantAPI = {
|
|||||||
params: { status },
|
params: { status },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 获取租户菜单ID集合 */
|
||||||
|
getTenantMenuIds(tenantId: number) {
|
||||||
|
return request<any, number[]>({
|
||||||
|
url: `${TENANT_BASE_URL}/${tenantId}/menuIds`,
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 更新租户菜单 */
|
||||||
|
updateTenantMenus(tenantId: number, menuIds: number[]) {
|
||||||
|
return request({
|
||||||
|
url: `${TENANT_BASE_URL}/${tenantId}/menus`,
|
||||||
|
method: "put",
|
||||||
|
data: menuIds,
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TenantAPI;
|
export default TenantAPI;
|
||||||
|
|||||||
@@ -96,14 +96,14 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="!isPlatformTenantId(scope.row.id)"
|
v-if="!isPlatformTenantId(scope.row.id)"
|
||||||
v-hasPerm="['sys:tenant:update']"
|
v-hasPerm="['sys:tenant:assign']"
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
link
|
link
|
||||||
icon="menu"
|
icon="menu"
|
||||||
@click="handleOpenPlanMenuDialog(scope.row)"
|
@click="handleOpenTenantMenuDialog(scope.row)"
|
||||||
>
|
>
|
||||||
方案菜单
|
租户菜单
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-hasPerm="['sys:tenant:update']"
|
v-hasPerm="['sys:tenant:update']"
|
||||||
@@ -230,10 +230,10 @@
|
|||||||
|
|
||||||
<!-- 方案菜单配置 -->
|
<!-- 方案菜单配置 -->
|
||||||
<el-drawer
|
<el-drawer
|
||||||
v-model="planMenuDialogVisible"
|
v-model="tenantMenuDialogVisible"
|
||||||
:title="'【' + checkedPlan.name + '】方案菜单配置'"
|
:title="'【' + checkedTenant.name + '】租户菜单配置'"
|
||||||
size="600px"
|
size="600px"
|
||||||
@close="handleClosePlanMenuDialog"
|
@close="handleCloseTenantMenuDialog"
|
||||||
>
|
>
|
||||||
<div class="flex-x-between">
|
<div class="flex-x-between">
|
||||||
<el-input v-model="menuKeywords" clearable class="w-[150px]" placeholder="菜单名称">
|
<el-input v-model="menuKeywords" clearable class="w-[150px]" placeholder="菜单名称">
|
||||||
@@ -282,13 +282,13 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button
|
<el-button
|
||||||
v-hasPerm="['sys:tenant-plan:assign']"
|
v-hasPerm="['sys:tenant:assign']"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handlePlanMenuSubmit"
|
@click="handleTenantMenuSubmit"
|
||||||
>
|
>
|
||||||
确定
|
确定
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="planMenuDialogVisible = false">取消</el-button>
|
<el-button @click="tenantMenuDialogVisible = false">取消</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
@@ -308,7 +308,13 @@ import { hasPerm } from "@/utils/auth";
|
|||||||
import TenantAPI from "@/api/system/tenant";
|
import TenantAPI from "@/api/system/tenant";
|
||||||
import TenantPlanAPI from "@/api/system/tenant-plan";
|
import TenantPlanAPI from "@/api/system/tenant-plan";
|
||||||
import MenuAPI from "@/api/system/menu";
|
import MenuAPI from "@/api/system/menu";
|
||||||
import type { TenantCreateForm, TenantForm, TenantQueryParams, TenantItem } from "@/types/api";
|
import type {
|
||||||
|
OptionItem,
|
||||||
|
TenantCreateForm,
|
||||||
|
TenantForm,
|
||||||
|
TenantQueryParams,
|
||||||
|
TenantItem,
|
||||||
|
} from "@/types/api";
|
||||||
import { MenuScopeEnum } from "@/enums/business";
|
import { MenuScopeEnum } from "@/enums/business";
|
||||||
import { isPlatformTenantId } from "@/utils/tenant";
|
import { isPlatformTenantId } from "@/utils/tenant";
|
||||||
|
|
||||||
@@ -335,8 +341,8 @@ const dialog = reactive({
|
|||||||
visible: false,
|
visible: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const planMenuDialogVisible = ref(false);
|
const tenantMenuDialogVisible = ref(false);
|
||||||
const checkedPlan = ref<{ id?: number; name?: string }>({});
|
const checkedTenant = ref<{ id?: number; name?: string; planId?: number }>({});
|
||||||
const menuKeywords = ref("");
|
const menuKeywords = ref("");
|
||||||
const menuExpanded = ref(true);
|
const menuExpanded = ref(true);
|
||||||
const menuParentChildLinked = ref(true);
|
const menuParentChildLinked = ref(true);
|
||||||
@@ -409,8 +415,10 @@ function fetchData() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleOpenPlanMenuDialog(row: TenantItem) {
|
async function handleOpenTenantMenuDialog(row: TenantItem) {
|
||||||
if (isPlatformTenantId(row.id)) {
|
const tenantId = row.id;
|
||||||
|
if (tenantId == null || tenantId === "") return;
|
||||||
|
if (isPlatformTenantId(tenantId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const planId = row.planId;
|
const planId = row.planId;
|
||||||
@@ -419,29 +427,48 @@ async function handleOpenPlanMenuDialog(row: TenantItem) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
planMenuDialogVisible.value = true;
|
tenantMenuDialogVisible.value = true;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
checkedPlan.value = { id: planId, name: resolvePlanLabel(planId) };
|
checkedTenant.value = {
|
||||||
|
id: Number(tenantId),
|
||||||
|
name: row.name || String(tenantId),
|
||||||
|
planId,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 套餐菜单只允许配置业务菜单
|
const [menuOptions, planMenuIds, menuIds] = await Promise.all([
|
||||||
const menuOptions = await MenuAPI.getOptions(false, MenuScopeEnum.TENANT);
|
MenuAPI.getOptions(false, MenuScopeEnum.TENANT),
|
||||||
menuPermOptions.value = menuOptions;
|
TenantPlanAPI.getPlanMenuIds(planId),
|
||||||
const menuIds = await TenantPlanAPI.getPlanMenuIds(planId);
|
TenantAPI.getTenantMenuIds(Number(tenantId)),
|
||||||
|
]);
|
||||||
|
const normalizedPlanMenuIds = planMenuIds
|
||||||
|
.map((menuId) => Number(menuId))
|
||||||
|
.filter((menuId) => !Number.isNaN(menuId));
|
||||||
|
const allowedMenuIdSet = new Set(normalizedPlanMenuIds);
|
||||||
|
menuPermOptions.value = allowedMenuIdSet.size
|
||||||
|
? filterMenuOptionsByIds(menuOptions, allowedMenuIdSet)
|
||||||
|
: menuOptions;
|
||||||
|
const normalizedMenuIds = menuIds
|
||||||
|
.map((menuId) => Number(menuId))
|
||||||
|
.filter((menuId) => !Number.isNaN(menuId));
|
||||||
await nextTick();
|
await nextTick();
|
||||||
menuTreeRef.value?.setCheckedKeys([], false);
|
menuTreeRef.value?.setCheckedKeys([], false);
|
||||||
menuIds.forEach((menuId) => menuTreeRef.value?.setChecked(menuId, true, false));
|
const checkedMenuIds = allowedMenuIdSet.size
|
||||||
|
? normalizedMenuIds.filter((menuId) => allowedMenuIdSet.has(menuId))
|
||||||
|
: normalizedMenuIds;
|
||||||
|
checkedMenuIds.forEach((menuId) => menuTreeRef.value?.setChecked(menuId, true, false));
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClosePlanMenuDialog() {
|
function handleCloseTenantMenuDialog() {
|
||||||
planMenuDialogVisible.value = false;
|
tenantMenuDialogVisible.value = false;
|
||||||
menuKeywords.value = "";
|
menuKeywords.value = "";
|
||||||
menuExpanded.value = true;
|
menuExpanded.value = true;
|
||||||
menuParentChildLinked.value = true;
|
menuParentChildLinked.value = true;
|
||||||
menuTreeRef.value?.setCheckedKeys([], false);
|
menuTreeRef.value?.setCheckedKeys([], false);
|
||||||
|
checkedTenant.value = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleMenuTree() {
|
function toggleMenuTree() {
|
||||||
@@ -470,9 +497,28 @@ function handleMenuFilter(value: string, data: { [key: string]: any }) {
|
|||||||
return data.label.includes(value);
|
return data.label.includes(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handlePlanMenuSubmit() {
|
function filterMenuOptionsByIds(
|
||||||
const planId = checkedPlan.value.id;
|
options: OptionItem[],
|
||||||
if (!planId) return;
|
allowedMenuIdSet: Set<number>
|
||||||
|
): OptionItem[] {
|
||||||
|
return options.reduce<OptionItem[]>((acc, option) => {
|
||||||
|
const children = option.children
|
||||||
|
? filterMenuOptionsByIds(option.children, allowedMenuIdSet)
|
||||||
|
: [];
|
||||||
|
const allowed = allowedMenuIdSet.has(Number(option.value));
|
||||||
|
if (allowed || children.length > 0) {
|
||||||
|
acc.push({
|
||||||
|
...option,
|
||||||
|
children: children.length > 0 ? children : undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleTenantMenuSubmit() {
|
||||||
|
const tenantId = checkedTenant.value.id;
|
||||||
|
if (!tenantId) return;
|
||||||
|
|
||||||
const checkedMenuIds: number[] = menuTreeRef
|
const checkedMenuIds: number[] = menuTreeRef
|
||||||
.value!.getCheckedNodes(false, true)
|
.value!.getCheckedNodes(false, true)
|
||||||
@@ -480,9 +526,11 @@ async function handlePlanMenuSubmit() {
|
|||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
await TenantPlanAPI.updatePlanMenus(planId, checkedMenuIds);
|
await TenantAPI.updateTenantMenus(tenantId, checkedMenuIds);
|
||||||
ElMessage.success("方案菜单配置成功");
|
ElMessage.success("租户菜单配置成功");
|
||||||
planMenuDialogVisible.value = false;
|
tenantMenuDialogVisible.value = false;
|
||||||
|
} catch {
|
||||||
|
ElMessage.error("租户菜单配置失败");
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user