mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-03 00:41:15 +00:00
### 摘要
- 菜单表与接口增加终端维度 `client`(`pc` | `app`),与规范「PC 桌面 / APP 移动端」一致。不改已有授权逻辑。
- 管理端动态路由仅下发 `client=pc`;菜单管理页通过 Tab + 查询参数 `menu_client` 分端维护。
- 种子数据 `sys_menu.json` 为全量 PC 菜单补 `client: pc`,并内置 5 条 APP 顶级目录(首页/同事/打卡/消息/我的),图标使用项目 `src/assets/icons` 内已有名称。
### 后端
- `MenuModel` 增加 `client` 列(默认 `pc`)。
- Schema:`MenuCreateSchema` / `MenuOutSchema` 含 `client`;`MenuQueryParam` 增加 `menu_client` 过滤。
- `menu_request_validator` 校验 `client ∈ {pc,app}`;父子终端一致校验在 `MenuService` 创建/更新中执行。
- `get_current_user_info` 菜单树与角色菜单 ID 仅保留 PC 端,避免 APP 菜单进入 Vue Router。
### 前端
- 菜单管理:PC/APP Tab、`menu_client` 列表参数、表格/详情/表单「终端」、子级继承父级 `client`。
- API 类型与 `MenuClientEnum`;`permission.store` 路由 `meta.client`(可选扩展)。
### 数据库(已有库)
-postgres
```
ALTER TABLE sys_menu ADD COLUMN IF NOT EXISTS client VARCHAR(20) NOT NULL DEFAULT 'pc';
COMMENT ON COLUMN sys_menu.client IS '终端(pc:管理端桌面 app:移动端)';
```
-mysql
```
ALTER TABLE sys_menu ADD COLUMN client VARCHAR(20) NOT NULL DEFAULT 'pc' COMMENT '终端(pc/app)';
```
### 兼容与注意
- 角色授权抽屉仍调用**不带** `menu_client` 的菜单树,可同时勾选 PC 与 APP。不改已有逻辑。
- 仅改种子 JSON 不影响**已有数据**库;已建库需执行 DDL,APP 示例菜单需在库中已存在或通过初始化导入。
This commit is contained in:
@@ -68,6 +68,13 @@ class MenuModel(ModelMixin):
|
|||||||
nullable=False,
|
nullable=False,
|
||||||
comment="是否固定标签页(True:是 False:否)",
|
comment="是否固定标签页(True:是 False:否)",
|
||||||
)
|
)
|
||||||
|
client: Mapped[str] = mapped_column(
|
||||||
|
String(20),
|
||||||
|
nullable=False,
|
||||||
|
default="pc",
|
||||||
|
server_default="pc",
|
||||||
|
comment="终端(pc:管理端桌面 app:移动端)",
|
||||||
|
)
|
||||||
|
|
||||||
# 树形结构
|
# 树形结构
|
||||||
parent_id: Mapped[int | None] = mapped_column(
|
parent_id: Mapped[int | None] = mapped_column(
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ class MenuCreateSchema(BaseModel):
|
|||||||
parent_id: int | None = Field(default=None, ge=1, description="父菜单ID")
|
parent_id: int | None = Field(default=None, ge=1, description="父菜单ID")
|
||||||
status: str = Field(default="0", description="是否启用(0:启用 1:禁用)")
|
status: str = Field(default="0", description="是否启用(0:启用 1:禁用)")
|
||||||
description: str | None = Field(default=None, max_length=255, description="描述")
|
description: str | None = Field(default=None, max_length=255, description="描述")
|
||||||
|
client: Literal["pc", "app"] = Field(
|
||||||
|
default="pc",
|
||||||
|
description="终端(pc:管理端桌面 app:移动端)",
|
||||||
|
)
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -53,6 +57,9 @@ class MenuCreateSchema(BaseModel):
|
|||||||
values[k] = (
|
values[k] = (
|
||||||
values[k].strip() or None if values[k].strip() == "" else values[k].strip()
|
values[k].strip() or None if values[k].strip() == "" else values[k].strip()
|
||||||
)
|
)
|
||||||
|
if "client" in values and isinstance(values["client"], str):
|
||||||
|
cv = values["client"].strip()
|
||||||
|
values["client"] = cv if cv in ("pc", "app") else "pc"
|
||||||
# 父ID转整型
|
# 父ID转整型
|
||||||
if "parent_id" in values and isinstance(values["parent_id"], str):
|
if "parent_id" in values and isinstance(values["parent_id"], str):
|
||||||
try:
|
try:
|
||||||
@@ -124,6 +131,10 @@ class MenuQueryParam:
|
|||||||
),
|
),
|
||||||
created_id: int | None = Query(None, description="创建人"),
|
created_id: int | None = Query(None, description="创建人"),
|
||||||
updated_id: int | None = Query(None, description="更新人"),
|
updated_id: int | None = Query(None, description="更新人"),
|
||||||
|
menu_client: Literal["pc", "app"] | None = Query(
|
||||||
|
None,
|
||||||
|
description="管理端 Tab:pc=桌面端菜单 app=移动端菜单;不传则不过滤终端",
|
||||||
|
),
|
||||||
) -> None:
|
) -> None:
|
||||||
# 模糊查询字段
|
# 模糊查询字段
|
||||||
self.name = (QueueEnum.like.value, name)
|
self.name = (QueueEnum.like.value, name)
|
||||||
@@ -151,3 +162,6 @@ class MenuQueryParam:
|
|||||||
self.created_id = (QueueEnum.eq.value, created_id)
|
self.created_id = (QueueEnum.eq.value, created_id)
|
||||||
if updated_id:
|
if updated_id:
|
||||||
self.updated_id = (QueueEnum.eq.value, updated_id)
|
self.updated_id = (QueueEnum.eq.value, updated_id)
|
||||||
|
|
||||||
|
if menu_client in ("pc", "app"):
|
||||||
|
self.client = (QueueEnum.eq.value, menu_client)
|
||||||
|
|||||||
@@ -50,6 +50,20 @@ class MenuService:
|
|||||||
else:
|
else:
|
||||||
raise CustomException(msg="菜单或链接类型下不允许新增子菜单")
|
raise CustomException(msg="菜单或链接类型下不允许新增子菜单")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def _validate_parent_child_client(
|
||||||
|
cls, auth: AuthSchema, parent_id: int | None, client: str
|
||||||
|
) -> None:
|
||||||
|
"""子菜单终端须与父菜单一致。"""
|
||||||
|
if parent_id is None:
|
||||||
|
return
|
||||||
|
parent = await MenuCRUD(auth).get_by_id_crud(id=parent_id)
|
||||||
|
if not parent:
|
||||||
|
return
|
||||||
|
p_client = getattr(parent, "client", None) or "pc"
|
||||||
|
if p_client != client:
|
||||||
|
raise CustomException(msg="子菜单终端须与父菜单一致(均为 pc 或均为 app)")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_menu_detail_service(cls, auth: AuthSchema, id: int) -> dict:
|
async def get_menu_detail_service(cls, auth: AuthSchema, id: int) -> dict:
|
||||||
"""
|
"""
|
||||||
@@ -119,6 +133,7 @@ class MenuService:
|
|||||||
raise CustomException(msg="创建失败,该菜单已存在")
|
raise CustomException(msg="创建失败,该菜单已存在")
|
||||||
|
|
||||||
await cls._validate_parent_child_type(auth, data.parent_id, data.type)
|
await cls._validate_parent_child_type(auth, data.parent_id, data.type)
|
||||||
|
await cls._validate_parent_child_client(auth, data.parent_id, data.client)
|
||||||
|
|
||||||
new_menu = await MenuCRUD(auth).create(data=data)
|
new_menu = await MenuCRUD(auth).create(data=data)
|
||||||
new_menu_dict = MenuOutSchema.model_validate(new_menu).model_dump()
|
new_menu_dict = MenuOutSchema.model_validate(new_menu).model_dump()
|
||||||
@@ -141,6 +156,7 @@ class MenuService:
|
|||||||
if not menu:
|
if not menu:
|
||||||
raise CustomException(msg="更新失败,该菜单不存在")
|
raise CustomException(msg="更新失败,该菜单不存在")
|
||||||
await cls._validate_parent_child_type(auth, data.parent_id, data.type)
|
await cls._validate_parent_child_type(auth, data.parent_id, data.type)
|
||||||
|
await cls._validate_parent_child_client(auth, data.parent_id, data.client)
|
||||||
search: dict[str, Any] = {"title": data.title}
|
search: dict[str, Any] = {"title": data.title}
|
||||||
if data.parent_id is not None:
|
if data.parent_id is not None:
|
||||||
search["parent_id"] = data.parent_id
|
search["parent_id"] = data.parent_id
|
||||||
|
|||||||
@@ -295,22 +295,25 @@ class UserService:
|
|||||||
UserOutSchema.dept_name = user.dept.name
|
UserOutSchema.dept_name = user.dept.name
|
||||||
user_dict = UserOutSchema.model_validate(user).model_dump()
|
user_dict = UserOutSchema.model_validate(user).model_dump()
|
||||||
|
|
||||||
# 获取菜单权限
|
# 获取菜单权限(PC 管理端侧栏:仅 client=pc)
|
||||||
|
_pc_only = {"client": "pc"}
|
||||||
if auth.user and auth.user.is_superuser:
|
if auth.user and auth.user.is_superuser:
|
||||||
# 使用树形结构查询,预加载children关系
|
# 使用树形结构查询,预加载children关系
|
||||||
menu_all = await MenuCRUD(auth).get_tree_list_crud(
|
menu_all = await MenuCRUD(auth).get_tree_list_crud(
|
||||||
search={"type": ("in", [1, 2, 4]), "status": "0"},
|
search={"type": ("in", [1, 2, 4]), "status": "0", **_pc_only},
|
||||||
order_by=[{"order": "asc"}],
|
order_by=[{"order": "asc"}],
|
||||||
)
|
)
|
||||||
menus = [MenuOutSchema.model_validate(menu).model_dump() for menu in menu_all]
|
menus = [MenuOutSchema.model_validate(menu).model_dump() for menu in menu_all]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# 收集用户所有角色的菜单ID,使用列表推导式优化代码
|
# 收集用户所有角色的菜单 ID(仅 PC 端菜单参与动态路由)
|
||||||
menu_ids = {
|
menu_ids = {
|
||||||
menu.id
|
menu.id
|
||||||
for role in auth.user.roles or []
|
for role in auth.user.roles or []
|
||||||
for menu in role.menus
|
for menu in role.menus
|
||||||
if menu.status == "0" and menu.type in [1, 2, 4]
|
if menu.status == "0"
|
||||||
|
and menu.type in [1, 2, 4]
|
||||||
|
and getattr(menu, "client", "pc") == "pc"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 使用树形结构查询,预加载children关系
|
# 使用树形结构查询,预加载children关系
|
||||||
@@ -318,7 +321,7 @@ class UserService:
|
|||||||
[
|
[
|
||||||
MenuOutSchema.model_validate(menu).model_dump()
|
MenuOutSchema.model_validate(menu).model_dump()
|
||||||
for menu in await MenuCRUD(auth).get_tree_list_crud(
|
for menu in await MenuCRUD(auth).get_tree_list_crud(
|
||||||
search={"id": ("in", list(menu_ids))},
|
search={"id": ("in", list(menu_ids)), **_pc_only},
|
||||||
order_by=[{"order": "asc"}],
|
order_by=[{"order": "asc"}],
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -257,6 +257,10 @@ def menu_request_validator(data: Any) -> Any:
|
|||||||
if data.type == 2 and not data.component_path:
|
if data.type == 2 and not data.component_path:
|
||||||
raise CustomException(code=RET.ERROR.code, msg="组件路径不能为空")
|
raise CustomException(code=RET.ERROR.code, msg="组件路径不能为空")
|
||||||
|
|
||||||
|
c = getattr(data, "client", None) or "pc"
|
||||||
|
if c not in ("pc", "app"):
|
||||||
|
raise CustomException(code=RET.ERROR.code, msg="终端 client 仅允许 pc 或 app")
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -58,6 +58,8 @@ export interface MenuPageQuery {
|
|||||||
status?: string;
|
status?: string;
|
||||||
created_time?: string[];
|
created_time?: string[];
|
||||||
updated_time?: string[];
|
updated_time?: string[];
|
||||||
|
/** 与后端 Query `menu_client` 一致:pc | app;菜单管理 Tab 切换时传入 */
|
||||||
|
menu_client?: "pc" | "app";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MenuTable extends BaseType {
|
export interface MenuTable extends BaseType {
|
||||||
@@ -79,6 +81,7 @@ export interface MenuTable extends BaseType {
|
|||||||
params?: { key: string; value: string }[];
|
params?: { key: string; value: string }[];
|
||||||
affix?: boolean;
|
affix?: boolean;
|
||||||
children?: MenuTable[];
|
children?: MenuTable[];
|
||||||
|
client?: "pc" | "app";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MenuForm extends BaseFormType {
|
export interface MenuForm extends BaseFormType {
|
||||||
@@ -98,6 +101,7 @@ export interface MenuForm extends BaseFormType {
|
|||||||
title?: string;
|
title?: string;
|
||||||
params?: KeyValue[];
|
params?: KeyValue[];
|
||||||
affix?: boolean;
|
affix?: boolean;
|
||||||
|
client?: "pc" | "app";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KeyValue {
|
export interface KeyValue {
|
||||||
|
|||||||
@@ -5,3 +5,9 @@ export enum MenuTypeEnum {
|
|||||||
BUTTON = 3, // 按钮
|
BUTTON = 3, // 按钮
|
||||||
EXTLINK = 4, // 外链
|
EXTLINK = 4, // 外链
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 菜单终端(与后端 sys_menu.client 一致) */
|
||||||
|
export enum MenuClientEnum {
|
||||||
|
PC = "pc",
|
||||||
|
APP = "app",
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export interface Meta {
|
|||||||
alwaysShow?: boolean;
|
alwaysShow?: boolean;
|
||||||
/** 是否隐藏(true-是 false-否) */
|
/** 是否隐藏(true-是 false-否) */
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
|
/** 菜单终端(后端 sys_menu.client,PC 动态路由仅下发 pc) */
|
||||||
|
client?: string;
|
||||||
/** ICON */
|
/** ICON */
|
||||||
icon?: string;
|
icon?: string;
|
||||||
/** 【菜单】是否开启页面缓存 */
|
/** 【菜单】是否开启页面缓存 */
|
||||||
@@ -71,6 +73,7 @@ export const generator = (routers: MenuTable[]): RouteVO[] => {
|
|||||||
meta: {
|
meta: {
|
||||||
title: item.title,
|
title: item.title,
|
||||||
icon: item.icon || undefined,
|
icon: item.icon || undefined,
|
||||||
|
client: item.client,
|
||||||
keepAlive: item.keep_alive,
|
keepAlive: item.keep_alive,
|
||||||
hidden: item.hidden,
|
hidden: item.hidden,
|
||||||
order: item.order,
|
order: item.order,
|
||||||
|
|||||||
@@ -8,6 +8,15 @@
|
|||||||
@reset-click="handleResetClick"
|
@reset-click="handleResetClick"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<el-tabs
|
||||||
|
v-model="menuClientTab"
|
||||||
|
class="menu-client-tabs px-1 mb-2"
|
||||||
|
@tab-change="handleMenuClientTabChange"
|
||||||
|
>
|
||||||
|
<el-tab-pane label="PC 桌面菜单管理" name="pc" />
|
||||||
|
<el-tab-pane label="APP 移动端菜单管理" name="app" />
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
<PageContent ref="contentRef" :content-config="contentConfig">
|
<PageContent ref="contentRef" :content-config="contentConfig">
|
||||||
<template #toolbar="{ toolbarRight, onToolbar, removeIds, cols }">
|
<template #toolbar="{ toolbarRight, onToolbar, removeIds, cols }">
|
||||||
<CrudToolbarLeft
|
<CrudToolbarLeft
|
||||||
@@ -70,6 +79,13 @@
|
|||||||
<el-tag v-if="scope.row.type === MenuTypeEnum.EXTLINK" type="info">外链</el-tag>
|
<el-tag v-if="scope.row.type === MenuTypeEnum.EXTLINK" type="info">外链</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="终端" prop="client" min-width="88" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.client === MenuClientEnum.PC" type="primary">PC</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.client === MenuClientEnum.APP" type="success">APP</el-tag>
|
||||||
|
<el-tag v-else type="info">{{ scope.row.client || "—" }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="排序" prop="order" min-width="80" />
|
<el-table-column label="排序" prop="order" min-width="80" />
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="路由名称"
|
label="路由名称"
|
||||||
@@ -219,6 +235,11 @@
|
|||||||
<el-tag v-if="detailFormData.type === MenuTypeEnum.BUTTON" type="danger">按钮</el-tag>
|
<el-tag v-if="detailFormData.type === MenuTypeEnum.BUTTON" type="danger">按钮</el-tag>
|
||||||
<el-tag v-if="detailFormData.type === MenuTypeEnum.EXTLINK" type="info">外链</el-tag>
|
<el-tag v-if="detailFormData.type === MenuTypeEnum.EXTLINK" type="info">外链</el-tag>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="终端" :span="2">
|
||||||
|
<el-tag v-if="detailFormData.client === MenuClientEnum.PC" type="primary">PC</el-tag>
|
||||||
|
<el-tag v-else-if="detailFormData.client === MenuClientEnum.APP" type="success">APP</el-tag>
|
||||||
|
<el-tag v-else type="info">{{ detailFormData.client || "—" }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="图标" :span="2">
|
<el-descriptions-item label="图标" :span="2">
|
||||||
<template #default>
|
<template #default>
|
||||||
<template v-if="detailFormData.icon && detailFormData.icon.startsWith('el-icon')">
|
<template v-if="detailFormData.icon && detailFormData.icon.startsWith('el-icon')">
|
||||||
@@ -367,6 +388,16 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="终端" prop="client">
|
||||||
|
<el-radio-group v-model="formData.client" :disabled="createParentLocked">
|
||||||
|
<el-radio :value="MenuClientEnum.PC">PC 桌面</el-radio>
|
||||||
|
<el-radio :value="MenuClientEnum.APP">APP 移动</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<el-text v-if="createParentLocked" type="info" size="small" class="block mt-1">
|
||||||
|
子级终端与父菜单一致
|
||||||
|
</el-text>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item v-if="formData.type == MenuTypeEnum.EXTLINK" label="外链地址" prop="path">
|
<el-form-item v-if="formData.type == MenuTypeEnum.EXTLINK" label="外链地址" prop="path">
|
||||||
<el-input v-model="formData.route_path" placeholder="请输入外链完整路径" />
|
<el-input v-model="formData.route_path" placeholder="请输入外链完整路径" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -631,7 +662,7 @@ import { useUserStore } from "@/store/modules/user.store";
|
|||||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||||
|
|
||||||
import MenuAPI, { MenuPageQuery, MenuForm, MenuTable } from "@/api/module_system/menu";
|
import MenuAPI, { MenuPageQuery, MenuForm, MenuTable } from "@/api/module_system/menu";
|
||||||
import { MenuTypeEnum } from "@/enums/system/menu.enum";
|
import { MenuClientEnum, MenuTypeEnum } from "@/enums/system/menu.enum";
|
||||||
import { formatTree } from "@/utils/common";
|
import { formatTree } from "@/utils/common";
|
||||||
import CrudToolbarLeft from "@/components/CURD/CrudToolbarLeft.vue";
|
import CrudToolbarLeft from "@/components/CURD/CrudToolbarLeft.vue";
|
||||||
import CrudToolbarRight from "@/components/CURD/CrudToolbarRight.vue";
|
import CrudToolbarRight from "@/components/CURD/CrudToolbarRight.vue";
|
||||||
@@ -645,6 +676,15 @@ const appStore = useAppStore();
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const { searchRef, contentRef, handleQueryClick, handleResetClick, refreshList } = useCrudList();
|
const { searchRef, contentRef, handleQueryClick, handleResetClick, refreshList } = useCrudList();
|
||||||
|
|
||||||
|
/** 菜单管理:PC / APP 分栏(与接口 menu_client 一致) */
|
||||||
|
const menuClientTab = ref<"pc" | "app">("pc");
|
||||||
|
|
||||||
|
function handleMenuClientTabChange(name: string | number) {
|
||||||
|
menuClientTab.value = name === "app" ? "app" : "pc";
|
||||||
|
refreshList();
|
||||||
|
}
|
||||||
|
|
||||||
const dataFormRef = ref();
|
const dataFormRef = ref();
|
||||||
const submitLoading = ref(false);
|
const submitLoading = ref(false);
|
||||||
|
|
||||||
@@ -714,6 +754,7 @@ const formData = reactive<MenuForm>({
|
|||||||
affix: false,
|
affix: false,
|
||||||
status: "0",
|
status: "0",
|
||||||
description: undefined,
|
description: undefined,
|
||||||
|
client: MenuClientEnum.PC,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 弹窗状态
|
// 弹窗状态
|
||||||
@@ -810,7 +851,10 @@ const contentConfig = reactive<IContentConfig<MenuPageQuery>>({
|
|||||||
defaultToolbar: ["refresh", "filter"],
|
defaultToolbar: ["refresh", "filter"],
|
||||||
pagination: false,
|
pagination: false,
|
||||||
indexAction: async (params) => {
|
indexAction: async (params) => {
|
||||||
const res = await MenuAPI.listMenu(params as MenuPageQuery);
|
const res = await MenuAPI.listMenu({
|
||||||
|
...(params as MenuPageQuery),
|
||||||
|
menu_client: menuClientTab.value,
|
||||||
|
});
|
||||||
const tree = res.data.data || [];
|
const tree = res.data.data || [];
|
||||||
fullMenuTree.value = tree;
|
fullMenuTree.value = tree;
|
||||||
menuOptions.value = formatTree(filterMenuTypes(tree));
|
menuOptions.value = formatTree(filterMenuTypes(tree));
|
||||||
@@ -869,6 +913,7 @@ const rules = reactive({
|
|||||||
hidden: [{ required: true, message: "请选择是否隐藏", trigger: "change" }],
|
hidden: [{ required: true, message: "请选择是否隐藏", trigger: "change" }],
|
||||||
always_show: [{ required: true, message: "请选择始终显示", trigger: "change" }],
|
always_show: [{ required: true, message: "请选择始终显示", trigger: "change" }],
|
||||||
status: [{ required: true, message: "请选择状态", trigger: "change" }],
|
status: [{ required: true, message: "请选择状态", trigger: "change" }],
|
||||||
|
client: [{ required: true, message: "请选择终端", trigger: "change" }],
|
||||||
redirect: [
|
redirect: [
|
||||||
{
|
{
|
||||||
validator: (_rule: unknown, value: string | undefined, callback: (e?: Error) => void) => {
|
validator: (_rule: unknown, value: string | undefined, callback: (e?: Error) => void) => {
|
||||||
@@ -909,6 +954,7 @@ const initialFormData: MenuForm = {
|
|||||||
affix: false,
|
affix: false,
|
||||||
status: "0",
|
status: "0",
|
||||||
description: undefined,
|
description: undefined,
|
||||||
|
client: MenuClientEnum.PC,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置表单
|
// 重置表单
|
||||||
@@ -955,12 +1001,15 @@ async function handleOpenDialog(
|
|||||||
Object.assign(formData, initialFormData);
|
Object.assign(formData, initialFormData);
|
||||||
if (parentRow?.id != null) {
|
if (parentRow?.id != null) {
|
||||||
formData.parent_id = parentRow.id;
|
formData.parent_id = parentRow.id;
|
||||||
|
formData.client = (parentRow.client as MenuClientEnum) || menuClientTab.value;
|
||||||
if (parentRow.type === MenuTypeEnum.MENU) {
|
if (parentRow.type === MenuTypeEnum.MENU) {
|
||||||
createParentLocked.value = true;
|
createParentLocked.value = true;
|
||||||
formData.type = MenuTypeEnum.BUTTON;
|
formData.type = MenuTypeEnum.BUTTON;
|
||||||
} else if (parentRow.type === MenuTypeEnum.CATALOG) {
|
} else if (parentRow.type === MenuTypeEnum.CATALOG) {
|
||||||
formData.type = MenuTypeEnum.MENU;
|
formData.type = MenuTypeEnum.MENU;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
formData.client = menuClientTab.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialogVisible.visible = true;
|
dialogVisible.visible = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user