新增目录和权限
11
类别: 
vortmall前端Admin开发

1. 创建页面文件

Vort-Admin/src/views 下新建 test 文件夹,在 test 文件夹下新增 Index.vue(主页面)和 Form.vue(新增/编辑/详情弹窗)两个页面文件。

Index.vue(主页面)

image.png

Form.vue(新增/编辑/详情弹窗)

image.png


2. 配置路由

2.1 新增路由文件

Vort-Admin/src/router/routes/admin 目录下新增 test.ts 路由文件。

示例结构(按需调整 title/icon/name/path):

import Layout from "@/layouts/base/Index.vue";

export default {
    path: "/test",
    name: "test",
    component: Layout,
    meta: { title: "测试" },
    children: [
        {
            path: "",
            name: "testDir",
            meta: { title: "测试一级目录", icon: "menu/xxx" },
            redirect: "/test/list",
            children: [
                {
                    path: "list",
                    name: "testManage",
                    meta: { title: "测试二级目录" },
                    component: () => import("@/views/test/Index.vue")
                }
            ]
        }
    ]
};

2.2 注册路由模块

Vort-Admin/src/router/routes/admin/index.ts 中引入并加入导出数组。

image.png


3. 配置菜单权限

进入:设置账号权限菜单管理(管理后台 Tab)。

image.png

菜单会根据接口返回的菜单树渲染,唯一编号(code) 需要与前端路由的 name 一致(且只能输入英文字母)。

3.1 新增顶级目录(测试总目录)

点击 添加菜单,填写:

  • 菜单名称:测试
  • 唯一编号:test
  • ICO编码:menu/xxx
  • 是否显示:是
  • 排序:按需

image.png

3.2 新增一级目录

在“测试”这一行点击 添加子菜单,填写:

  • 菜单名称:测试一级目录
  • 唯一编号:testDir
  • ICO编码:menu/xxx
  • 是否显示:是
  • 排序:按需

3.3 新增二级菜单(落地页面)

在“测试一级目录”这一行点击 添加子菜单,填写:

  • 菜单名称:测试二级目录
  • 唯一编号:testManage
  • 是否显示:是
  • 排序:按需

保存后刷新页面,菜单栏会根据当前用户权限自动生成对应目录与页面入口。


4. 如何打开测试页面的详情(新增/编辑/详情弹窗)

Vort-Admin/src/views/test/Index.vue 中通过 DialogForm 打开 Form.vue

  • 新增:params={ act: 'add' }
  • 编辑/详情:params={ act: 'detail', id: row.id }

4.1 Index.vue 示例代码

<template>
    <div class="container">
        <div class="content-wrapper flex flex-wrap flex-col gap-4">
            <vort-card class="vort-surface" :title="pageTitle">
                <SearchToolbar :on-search="onSearchSubmit" :on-reset="resetParams">
                    <SearchForm>
                        <!-- 搜索项 -->
                        <SearchFormItem label="关键词">
                            <vort-input v-model="filterParams.keyword" placeholder="请输入关键词" />
                        </SearchFormItem>
                        <SearchFormActions />
                    </SearchForm>

                    <!-- 操作按钮区域 -->
                    <template #actions>
                        <div class="flex gap-2 flex-wrap">
                            <!-- 新增按钮 -->
                            <DialogForm
                                :component="TestForm"
                                :params="{ act: 'add' }"
                                is-drawer
                                title="新增测试"
                                width="580px"
                                @ok="loadData"
                            >
                                <vort-button variant="primary">新增</vort-button>
                            </DialogForm>
                        </div>
                    </template>
                </SearchToolbar>
            </vort-card>

            <!-- 数据表格 -->
            <vort-card class="vort-surface">
                <div class="vort-table-wrapper">
                    <vort-table :data-source="listData" :loading="loading" row-key="id" size="large">
                        <vort-table-column label="ID" prop="id" :width="80" />
                        <vort-table-column label="名称" prop="name" />
                        <vort-table-column label="创建时间" prop="createTime" />

                        <!-- 操作列 -->
                        <vort-table-column :width="180" fixed="right" label="操作">
                            <template #default="{ row }">
                                <TableActions type="text" :divider="true">
                                    <!-- 编辑 -->
                                    <TableActionsItem>
                                        <DialogForm
                                            :component="TestForm"
                                            :params="{ act: 'detail', id: row.id }"
                                            is-drawer
                                            title="编辑测试"
                                            width="580px"
                                            @ok="loadData"
                                        >
                                            <a class="btn-link">编辑</a>
                                        </DialogForm>
                                    </TableActionsItem>

                                    <!-- 删除 -->
                                    <TableActionsItem>
                                        <DeleteRecord
                                            :params="{ id: row.id }"
                                            :request-api="testApi.del"
                                            :on-success="loadData"
                                        >
                                            <a class="btn-link">删除</a>
                                        </DeleteRecord>
                                    </TableActionsItem>
                                </TableActions>
                            </template>
                        </vort-table-column>
                    </vort-table>

                    <!-- 分页 -->
                    <div v-if="showPagination" class="vort-pagination-wrapper">
                        <vort-pagination
                            v-model:current="filterParams.page"
                            v-model:page-size="filterParams.size"
                            :total="total"
                            show-total-info
                            show-size-changer
                            show-quick-jumper
                            @change="loadData"
                        />
                    </div>
                </div>
            </vort-card>
        </div>
    </div>
</template>

<script lang="ts" setup>
// ===================== 组件导入 =====================
import {
    SearchToolbar,
    SearchForm,
    SearchFormItem,
    SearchFormActions,
    DialogForm,
    TableActions,
    TableActionsItem,
    DeleteRecord
} from "@/components/vort-biz";
import TestForm from "./Form.vue";

// ===================== API & 类型导入 =====================
import { testApi } from "@/api/modules/test";
import type { TestListParams, TestItem } from "@/types/modules/test";

// ===================== Hooks =====================
import { useCrudPage, usePageTitle } from "@/hooks";

const { pageTitle } = usePageTitle();

// ===================== CRUD 页面 Hook =====================
const { listData, loading, total, filterParams, showPagination, loadData, onSearchSubmit, resetParams } = useCrudPage<
    TestItem,
    TestListParams
>({
    api: testApi.getList,
    idKey: "id",
    defaultParams: {
        page: 1,
        size: 20,
        keyword: ""
    }
});

// ===================== 初始化 =====================
loadData();
</script>

Vort-Admin/src/views/test/Form.vue 中按需实现:

  • act=detail:根据 id 获取详情并回填
  • 提交:根据 act 调用 create/update 接口,成功后关闭弹窗并触发父页面 @ok 刷新列表

4.2 Form.vue 示例代码

<template>
    <vort-spin :spinning="loading">
        <vort-form
            ref="formRef"
            :model="formState as Record<string, unknown>"
            :rules="formRules"
            label-width="100px"
            :disabled="isReadonly"
        >
            <vort-form-item label="名称" name="name" required>
                <vort-input v-model="formState.name" placeholder="请输入名称" />
            </vort-form-item>

            <vort-form-item label="描述" name="description">
                <vort-textarea v-model="formState.description" :rows="3" placeholder="请输入描述" />
            </vort-form-item>

            <vort-form-item label="排序" name="sortOrder">
                <vort-input-number v-model="formState.sortOrder" />
            </vort-form-item>
        </vort-form>
    </vort-spin>
</template>

<script lang="ts" setup>
import { shallowRef } from "vue";
import { z } from "zod";

// ===================== Hooks =====================
import { useDialogForm, type DialogFormProps, type CommonFormParams } from "@/hooks";

// ===================== API & 类型 =====================
import { testApi } from "@/api/modules/test";
import type { TestFormData } from "@/types/modules/test";

// ===================== Props =====================
const props = defineProps<DialogFormProps<CommonFormParams>>();

// ===================== 表单配置 =====================
const formRef = shallowRef();

// 表单验证规则(使用 zod)
const formRules = z.object({
    name: z.string().trim().min(1, "名称不能为空").max(50, "名称最多50个字符"),
    description: z.string().optional(),
    sortOrder: z.number().optional()
});

// ===================== 使用 Hook =====================
const { loading, formState, isReadonly, onSubmit } = useDialogForm<TestFormData>({
    props,
    formRef,
    // 表单默认值
    defaultFormState: {
        name: "",
        description: "",
        sortOrder: 50
    },
    // 获取详情接口
    detailApi: (params) => testApi.getDetail("detail", params),
    // 提交接口(新增/编辑)
    submitApi: (operation, data) =>
        testApi.update(operation, {
            ...data,
            id: props.params?.id
        })
});

// ===================== 暴露方法 =====================
// 必须暴露 onFormSubmit 方法,DialogForm 会调用此方法提交表单
defineExpose({
    onFormSubmit: onSubmit
});
</script>

4.3 核心要点说明

要点说明
DialogForm 组件用于包裹触发按钮,点击后自动打开弹窗/抽屉并加载 Form 组件
params 属性传递给 Form 组件的参数,act 为操作类型(add/detail),id 为数据 ID
@ok 事件Form 提交成功后触发,通常用于刷新列表
useDialogForm Hook封装了加载详情、表单状态管理、提交等通用逻辑
defineExpose必须暴露 onFormSubmit 方法,DialogForm 点击确定时会调用此方法
评论 0
/ 1000
0
0
收藏