likes
comments
collection
share

从0开始学习 vben admin(二)

作者站长头像
站长
· 阅读数 34

前言

今天是记录学习vben的第二天,我们今天主要是来讲一下如何对接后端api接口,表格的基本用法,弹窗的用法,以及把权限调整为后端权限。

正文

api接口配置

vite.config.ts中,有关于这个api的配置,在这里可以配置您api的地址、端口等。

import { defineApplicationConfig } from '@vben/vite-config';  
  
export default defineApplicationConfig({  
    overrides: {  
        optimizeDeps: {  
            include: [  
            'echarts/core',  
            'echarts/charts',  
            'echarts/components',  
            'echarts/renderers',  
            'qrcode',  
            '@iconify/iconify',  
            'ant-design-vue/es/locale/zh_CN',  
            'ant-design-vue/es/locale/en_US',  
            ],  
        },  
        server: {  
            port: 10086, // 端口号  
            proxy: {  // api地址配置
                '/api': {  
                    target: 'http://localhost:8899/api/',  // 在这里可以配置您的地址和端口
                    changeOrigin: true,  
                    ws: true,  
                    rewrite: (path) => path.replace(new RegExp(`^/api/`), ''),  
                    // only https  
                    // secure: false  
                },  
                '/upload': {  // 上传接口地址配置
                    target: 'http://localhost:3300/upload',  
                    changeOrigin: true,  
                    ws: true,  
                    rewrite: (path) => path.replace(new RegExp(`^/upload/`), ''),  
                },  
            },  
        },  
    },  
});

在配置好以后,可以去.env.development文件配置开发环境的api接口,关闭mock模拟数据,然后重新启动一下,就能够访问自己的后端接口啦!

# Whether to open mock  这里是是否开启模拟数据,如果我们要使用自己的后台接口,则需要关闭他
VITE_USE_MOCK = false  
  
# public path  
VITE_PUBLIC_PATH = /  
  
# Basic interface address SPA  这里是请求默认的api
VITE_GLOB_API_URL=/api/  
  
# File upload address, optional  
VITE_GLOB_UPLOAD_URL=/upload/  
  
# Interface prefix  
VITE_GLOB_API_URL_PREFIX=

发起请求,看到原来的base-api变成了api就说明成功了,下面我们去src/api/sys/user.ts改一下他封装的登录接口,只需要把Api中的/去掉就可以正常使用了,如果需要将登录的字段名称修改,则需要前往src/api/sys/model/userModel.tssrc/views/sys/login/LoginForm.vue进行修改,到这里,我们讲的第一部分就结束了,如果有不懂的小伙伴,可以在评论区提问。

enum Api {  
    Login = 'login',  // 登录接口
    Logout = 'logout',  // 登出
    GetUserInfo = 'getUserInfo',  // 获取用户信息
    GetPermCode = 'getPermCode',  // 获取用户权限码
    TestRetry = 'testRetry',  
}

修改传值字段名称

// src/api/sys/model/userModel.ts 文件路径
/**  
* @description: Login interface parameters  
*/  
export interface LoginParams {  
    username: string;  // 可以修改为你想要的 比如 phone
    password: string;  
}

// src/views/sys/login/LoginForm.vue 文件路径
const formData = reactive({  
username: 'vben',  // 这里可以修改
password: '123456',  
});  
  
const { validForm } = useFormValid(formRef);  
  
//onKeyStroke('Enter', handleLogin);  
  
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN);  
  
async function handleLogin() {  
    const data = await validForm();  
    if (!data) return;  
    try {  
        loading.value = true;  
        const userInfo = await userStore.login({  
            password: data.password,  
            username: data.username,  // 这里可以修改
            mode: 'none', //不要默认的错误提示  
        });  
        if (userInfo) {  
            notification.success({  
            message: t('sys.login.loginSuccessTitle'),  
            description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`,  
            duration: 3,  
            });  
        }  
    } catch (error) {  
        createErrorModal({  
            title: t('sys.api.errorTip'),  
            content: (error as unknown as Error).message || t('sys.api.networkExceptionMsg'),  
            getContainer: () => document.body.querySelector(`.${prefixCls}`) || document.body, 
        });  
        } finally {  
        loading.value = false;  
    }  
}

故意输入错误,测试是否能接通

从0开始学习 vben admin(二)

在登录后,框架会自动调用以下几个接口,在每次刷新也会调用,以保证管理员修改权限后可以实时更新。

从0开始学习 vben admin(二)

表格的使用方法

在官网有一个菜单是Table 表格 | Vben Admin (vvbin.cn),这里讲了表格组件的一些用法,今天就基于这个,做一个展示列表。如你所见,我并没有在官方给出的地方存放对应的api等文件,而是在模块文件夹中存放,这样存放,有助于我个人开发毕竟我是个后端,修改起来方便才是王道,而对我来说整个项目文件夹太多,找起来很麻烦,所以我选择了这样不太规范的开发方式,大家可以对应文件目录说明来存放你的代码。

我把他们分成了四个模块

文件夹名称用途
apiapi接口
data数据/表头 信息
modal弹窗
Xxxx.Vue主页面

从0开始学习 vben admin(二)

主页面视图

<template>  
    <div>  
        <BasicTable  
        @register="registerTable"  
        :rowSelection="{ type: 'checkbox', selectedRowKeys: checkedKeys, onChange: onSelectChange }"  
        >  
            <template #headerTop>  
            <a-alert type="info" show-icon>  
            <template #message>  
            <template v-if="checkedKeys.length > 0">  
                <span>已选中{{ checkedKeys.length }}条记录(可跨页)</span>  
                <a-button type="link" @click="checkedKeys = []" size="small">清空</a-button>  
            </template>  
            <template v-else>  
                <span>未选中任何数据</span>  
            </template>  
            </template>  
            </a-alert>  
            </template>  
            <template #paper="{ record }">  
                <TableImg :size="60" :simpleShow="true" :imgList="record.paper" />  
            </template>  
            <template #office="{ record }">  
                <TableImg :size="60" :simpleShow="true" :imgList="record.office" />  
            </template>  
            <template #front="{ record }">  
                <TableImg :size="60" :simpleShow="true" :imgList="record.front" />  
            </template>  
            <template #portrait="{ record }">  
                <TableImg :size="60" :simpleShow="true" :imgList="record.portrait" />  
            </template>  
            <template #national="{ record }">  
                <TableImg :size="60" :simpleShow="true" :imgList="record.national" />  
            </template>  
            <template #toolbar>  
                <a-button v-auth="'1005'" type="primary" @click="excekOut">导出excel</a-button>  
                <a-button v-auth="'1003'" type="primary" @click="clickAdd">新增企业管理</a-button>  
            </template>  
            <!--操作栏-->  
            <template #action="{ record }">  
            <TableAction  
            :actions="[  
                {  
                    label: '编辑',  
                    onClick: handleEdit.bind(null, record),  
                    auth: '1001',  
                },  
                {  
                    label: '删除',  
                    color: 'error',  
                    popConfirm: {  
                    title: '是否删除该数据',  
                    confirm: handleDelete.bind(null, record),  
                    },  
                    auth: '1002',  
                },  
                {  
                    label: '启用/禁用',  
                    color: 'warning',  
                    popConfirm: {  
                    title: '是否启用/禁用该数据',  
                    confirm: handleDelete.bind(null, record),  
                    },  
                    auth: '1004',  
                },  
            ]"  
            />  
            </template>  
        </BasicTable>  
    <Modal1  
        @register="register1"  
        @ok="tableReload"  
        @close="tableReload"  
        :centered="true"  
        :width="1200"  
    />  
    </div>  
</template>  
<script lang="ts">  
import { defineComponent, ref } from 'vue';  
import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table';  // 引用table组件
import { getEnterpriseColumns } from './data/enterprisetable';  
import { Alert } from 'ant-design-vue';  
import { useModal } from '/@/components/Modal';  // 引用弹窗组件
import Modal1 from './modal/EnterpriseModal.vue';  // 引入弹窗页面
import { Tag } from 'ant-design-vue';  
  
import { getEnterpriseList, deleteEnterprise } from './api/enterprise';  // 引入api接口
import AButton from '/@/components/Button/src/BasicButton.vue';  // 引入按钮
import { getToken } from '/@/utils/auth';  // 获取用户token,导出excel使用
export default defineComponent({  
    components: { TableImg, AButton, BasicTable, TableAction, AAlert: Alert, Modal1, Tag },  //声明组件

    setup() {  
        const checkedKeys = ref<Array<string | number>>([]);  
        const [register1, { openModal: openModal1 }] = useModal();  // 实例化弹窗
        const [registerTable, {
             expandAll, collapseAll, reload // 这里是声明要使用的方法
        }] = useTable({  // 配置表格
            title: '企业管理列表',  // 标题
            api: getEnterpriseList,   // 获取数据的接口
            columns: getEnterpriseColumns(),  // 表头数据
            useSearchForm: false,  // 是否开启表格搜索
            // formConfig: getUserFormConfig(),  // 表格搜索的字段
            showTableSetting: true,  // 显示表格设置工具
            tableSetting: { fullScreen: true },  // 是否显示全屏按钮
            showIndexColumn: false,  // 是否显示序列号
            rowKey: 'id',  // 行的主键
            clickToRowSelect: false,  // 是否开启点击行选中
            bordered: true,  // 是否开启边框
            actionColumn: {  // 自定义列
                width: 250,  
                title: '操作',  
                dataIndex: 'action',  
                slots: { customRender: 'action' },  // 插槽名称
            },  
        });  

        // 新增企业管理  
        function clickAdd() {  
            openModal1(true);  // 让弹窗弹出
        }  

        function onSelectChange(selectedRowKeys: (string | number)[]) {  
            console.log(selectedRowKeys);  
            checkedKeys.value = selectedRowKeys;  
        }  
        // 编辑企业管理  
        function handleEdit(record: Recordable) {  
            openModal1(true, record);  // 让弹窗弹出,为了方便我使用了同一个弹窗页面,第二个参数是传值
            // console.log('点击了编辑');  
        }  
        // 删除企业管理  
        function handleDelete(record: Recordable) {  
            deleteEnterprise(record);  
            // console.log('点击了删除 ID: ');  
            tableReload();  
        }  
        function handleOpen(record: Recordable) {  
            console.log('点击了启用' + record);  
            tableReload();  
        }  
        function excekOut() {  
            window.open('http://127.0.0.1:8899/api/codeGenerationExcelOut?token=' + getToken());  
        }  
        function tableReload() {  
            reload();  // 刷新表格数据,需要在useTable中声明
            console.log(reload());  
        }  
        return {  
            tableReload,  
            register1,  
            registerTable,  
            expandAll,  
            collapseAll,  
            clickAdd,  
            checkedKeys,  
            onSelectChange,  
            handleEdit,  
            handleDelete,  
            handleOpen,  
            excekOut,  
        };  
    },  
});  
</script>

表头配置

前面讲到表头是获取data中的数据,以下是表头的代码,同样我会将方法的用途写在注释中,这样子表头就配置完成了,下面就是获取数据

import { BasicColumn } from '/@/components/Table/src/types/table';// 引用表头设置的方法  
  
// 权限列表表头  
export function getEnterpriseColumns(): BasicColumn[] {  
    return [  
        {  
            title: '企业ID',  
            dataIndex: 'id',  
            fixed: 'left',  
            width: 90,  
        },  
        {  
            title: '企业名称',  
            dataIndex: 'name',  
        },  
        {  
            title: '企业别名',  
            dataIndex: 'aliasname',  
        },  
        {  
            title: '企业地址id',  
            dataIndex: 'address',  
            defaultHidden: true,  
        },  
        {  
            title: '企业地址',  
            dataIndex: 'address_text',  
        },  
        {  
            dataIndex: 'status',  
            title: '状态',  
        },  
    ];  
}

api

api就正常封装调用,写在useTable中的api中,如果你没有去看我的那一篇文章,那么我还是要提醒你,返回参数需要去src\settings\componentSetting.ts中更改,下面我就会讲。

表格配置

// Used to configure the general configuration of some components without modifying the components  
  
import type { SorterResult } from '../components/Table';  
  
export default {  
    // basic-table setting  
    table: {  
        // Form interface request general configuration  
        // support xxx.xxx.xxx  
        fetchSetting: {  
        // The field name of the current page passed to the background  
        pageField: 'page',  // 页数的字段名称
        // The number field name of each page displayed in the background  
        sizeField: 'pageSize',  // 每页展示数量
        // Field name of the form data returned by the interface  
        listField: 'items',  // 数据返回的字段名
        // Total number of tables returned by the interface field name  
        totalField: 'total',  // 总数据量
    },  
    // Number of pages that can be selected  
    pageSizeOptions: ['10', '50', '80', '100'],  // 表格每页展示数据选择
    // Default display quantity on one page  
    defaultPageSize: 10,  
    // Default Size  
    defaultSize: 'middle',  
    // Custom general sort function  
    defaultSortFn: (sortInfo: SorterResult) => {  
    const { field, order } = sortInfo;  
    if (field && order) {  
    return {  
        // The sort field passed to the backend you  
        field,  
        // Sorting method passed to the background asc/desc  
        order,  
    };  
    } else {  
        return {};  
    }  
    },  
    // Custom general filter function  
    defaultFilterFn: (data: Partial<Recordable<string[]>>) => {  
        return data;  
    },  
    },  
    vxeTable: {  
        table: {  
            border: true,  
            stripe: true,  
            columnConfig: {  
            resizable: true,  
            isCurrent: true,  
            isHover: true,  
        },  
        rowConfig: {  
            isCurrent: true,  
            isHover: true,  
        },  
        emptyRender: {  
            name: 'AEmpty',  
        },  
        printConfig: {},  
        exportConfig: {},  
        customConfig: {  
                storage: true,  
            },  
         },  
        grid: {  
            toolbarConfig: {  
            enabled: true,  
            export: true,  
            zoom: true,  
            print: true,  
            refresh: true,  
            custom: true,  
            },  
            pagerConfig: {  
                pageSizes: [20, 50, 100, 500],  
                pageSize: 20,  
                autoHidden: true,  
            },  
            proxyConfig: {  
                form: true,  
                props: {  
                    result: 'items',  
                    total: 'total',  
                },  
            },  
            zoomConfig: {},  
            },  
        },  
        // scrollbar setting  
        scrollbar: {  
            // Whether to use native scroll bar  
            // After opening, the menu, modal, drawer will change the pop-up scroll bar to native  
            native: false,  
        },  
};

弹窗的使用

弹窗是一个名为Modal的组件,文档是 Modal 弹窗 | Vben Admin (vvbin.cn)

在主页面的那个视图中,仔细看注释,会发现我已经将引用、声明、调用讲过了,所以这里我就说一下他其他的配置。

<template>  
    <div>  
    <!-- 在这里的 @ok方法来监听点击确认按钮 -->  
        <BasicModal  
        v-bind="$attrs"  
        @register="modalInner"  
        title="企业管理 窗口"  
        @ok="addSubmitForm"  
        @visible-change="handleVisibleChange"  
        >  
            <div class="pt-3px pr-3px">  
                <BasicForm  
                @register="registerForm"  
                :model="model"  
                api="127.0.0.1:8801/api/upload/uploadImage"  
                />  
            </div>  
        </BasicModal>  
    </div>  
</template>  
<script lang="ts">  
    import { defineComponent, ref, nextTick } from 'vue';  
    import { BasicModal, useModalInner } from '/@/components/Modal';  
    import { BasicForm, useForm } from '/@/components/Form/index';  
    import { addEnterprise, updateEnterprise } from '../api/enterprise';  
    import { getEnterpriseForm } from '../data/enterpriseform';  

    export default defineComponent({  
        components: { BasicModal, BasicForm },  
        props: {  
            userData: { type: Object },  
        },  
        setup(props) {  
            const modelRef = ref({});  
            const isUpdate = ref(false);  
            const id = ref(0);  
            const [modalInner, { closeModal }] = useModalInner((data) => {  // 这个函数是用来接值的
                if (data != null) {  // 根据接值来判断是新增还是修改
                    isUpdate.value = true;  
                }  
                id.value = data.id;  
                setFieldsValue(data);  
            });  
            const [  // 声明表单
            registerForm,  
            { getFieldsValue, setFieldsValue, resetFields, validate, updateSchema },  
            ] = useForm({  
                labelWidth: 180,  
                schemas: getEnterpriseForm(), // 获取你设置的表单字段  
                showActionButtonGroup: false,  
                actionColOptions: {  
                    span: 24,  
                },  
            });  
            function onDataReceive(data) {  
                console.log('Data Received', data);  
            }  
            // 新增企业管理  
            function addSubmitForm() {  
                // 这里是验证表单必填值  
                validate();  
                // 获取表单中的值  
                let data = getFieldsValue();  
                console.log(data);  
                if (!isUpdate.value) {  
                    // 执行添加接口,是在api中封装的接口  
                    addEnterprise(data);  
                } else {  
                    data.id = id.value;  
                    // 执行修改接口,是在api中封装的接口  
                    updateEnterprise(data);  
                }  
                // 这个方法是官网的一个重置表单的方法  
                resetFields();  
                isUpdate.value = false;  
                // 关闭弹窗  
                closeModal();  
            }  
            function handleVisibleChange(v) {  
                v && props.userData && nextTick(() => onDataReceive(props.userData));  
            }  
            return {  
                updateSchema,  
                modalInner,  
                registerForm,  
                model: modelRef,  
                handleVisibleChange,  
                addSubmitForm,  
            };  
        },  
    });  
</script>

表单配置

下面就是表单的配置,Form 表单组件 | Vben Admin (vvbin.cn)

// import { getPermissionOption } from '/@/api/sys/permission';  
import { FormSchema } from '/@/components/Form';  
import address from '/@/../public/json/address.json';  
  
export function addressJson() {  
return address;  
}  
  
// 权限表单  
export function getEnterpriseForm(): FormSchema[] {  
    return [  
        {  
            field: 'name',  // 字段名称
            component: 'Input',  // 表单组件
            label: '企业名称',  // 字段标题
            required: true,  // 是否必填
            colProps: {  
                span: 24,  // 占位长度 24 为一行
            },  
        },  
        {  
            field: 'aliasname',  
            component: 'Input',  
            label: '企业别名',  
            required: true,  
            colProps: {  
                span: 24,  
            },  
        },  
        {  
            field: 'address',  
            component: 'Cascader',  
            label: '企业地址',  
            required: true,  
            componentProps: {  
                options: address,  
            },  
            colProps: {  
                span: 24,  
            },  
        },
    ];  
}

权限配置

权限,主要可以分为两种权限模式 前端权限后端权限,具体可以去官网的 权限 查看,不过一般来看,我们通常会使用后端权限来进行管理,权限是在src/settings/projectSetting.ts,将ROUTE_MAPPING改为BACK即可,修改后重新启动会发现你在路由中的目录都不见了,这时候需要后端来提供该用户能看到的目录,具体也在权限中能看到,我们这里就不过分的重复说明了。

修改前

// Permission mode  
permissionMode: PermissionModeEnum.ROUTE_MAPPING,

修改后

// Permission mode  
permissionMode: PermissionModeEnum.BACK,

结尾

结语

总的来说,要想实现简单的增删改查是很简单的,但是要想进行一些高级的操作,就需要对vue3和#  Ant Design Vue有一些经验,vben采用了对Ant的二次封装,使得对有使用经验的开发人员来说很大的提升了开发速度,但是也对一些小白来说不太友好,所以在新手开发的时候,会出现很多问题,大家在使用表格组件如果出现了自己无法解决的问题,欢迎来评论区提问、交流,毕竟我也是踩坑很久,会尽力帮助各位掘友来解决麻烦的。

下期预告

下期我会讲一些小东西

  • 表单组件基础使用
  • 表单组件的高级用法
转载自:https://juejin.cn/post/7239951583059951672
评论
请登录