給后端同事培训 vben 列表表格(增、删、改、查)业务 对话框 表单提交 的基本使用

列表示例

vben的列表封装的组件还是挺好用的,自带列展示(可拖动移位)、序号列、勾选列、刷新、密度、高度自适应等功能。
主要感觉就是高度自适应、查询表单、工具栏slot、翻页、接口数据usetable这些挺好用的方便列表增删改查的开发

在这里插入图片描述

文件目录 

-- user
  -- index.vue
  -- user.data.ts
  -- UserModal.vue

一、用户管理 父组件index.vue 与 Schema 文件数据

  1. 静态Schema文件 user.data.ts
import { BasicColumn } from '/@/components/Table';                        // 导入 列表 BasicColumn 类型
import { FormSchema } from '/@/components/Form/index';                    // 导入 表单 FormSchema 类型
import { h } from 'vue';                                                  // 导入 vue h 渲染函数
import { Tag } from 'ant-design-vue';                                     // 导入ant-design-vue  Tag 组件

// 导出 列表 表头 JSON 
export const columns: BasicColumn[] = [
  {
    title: '登录名',
    dataIndex: 'userName',
  },
  {
    title: '昵称',
    dataIndex: 'nickName',
    width: 150,
    align: 'center',
  },
  {
    title: '用户状态',
    dataIndex: 'status',
    customRender: ({ record }) => {
      const status = record.status;
      let color = '';
      let text = '--';
      if (status === 1) {
        color = 'success';
        text = '启用';
      } else if (status === 2) {
        color = 'warning';
        text = '锁定';
      } else {
        color = 'error';
        text = '禁用';
      }

      return h(Tag, { color: color }, () => text);
    },
    align: 'center',
  },
  {
    title: '用户类型',
    dataIndex: 'userType',
    width: 150,
    align: 'center',
  },
  {
    title: '注册时间',
    dataIndex: 'createTime',
    width: 180,
    align: 'center',
  },
  {
    title: '描述',
    dataIndex: 'userDesc',
    align: 'center',
  },
];
// 导出 列表 搜索表单 JSON 
export const searchFormSchema: FormSchema[] = [
  {
    field: 'userName',
    label: '用户姓名',
    component: 'Input',
    colProps: { span: 6 },
  },
];
// 导出 用户对话框  用户基本表单 JSON 
// schema 内组件的可选类型
// export type ComponentType =
//   | 'Input'
//   | 'InputGroup'
//   | 'InputPassword'
//   | 'InputSearch'
//   | 'InputTextArea'
//   | 'InputNumber'
//   | 'InputCountDown'
//   | 'Select'
//   | 'ApiSelect'
//   | 'TreeSelect'
//   | 'RadioButtonGroup'
//   | 'RadioGroup'
//   | 'Checkbox'
//   | 'CheckboxGroup'
//   | 'AutoComplete'
//   | 'Cascader'
//   | 'DatePicker'
//   | 'MonthPicker'
//   | 'RangePicker'
//   | 'WeekPicker'
//   | 'TimePicker'
//   | 'Switch'
//   | 'StrengthMeter'
//   | 'Upload'
//   | 'IconPicker'
//   | 'Render'
//   | 'Slider'
//   | 'Rate'
//   | 'Divider'; // v2.7.2新增


export const formSchema: FormSchema[] = [
  {
    field: 'userId',
    label: '', 
    component: 'Input',                                   // 表单使用组件
    show: false,                                          // 不展示  最近表单校验并获取值时 还是可以获取  ifshow 则相反 值不可以获取
  },
  {
    field: 'userType',
    label: '用户类型',
    component: 'RadioGroup',
    defaultValue: 'normal',
    componentProps: {
      options: [
        { label: '超级管理员', value: 'super' },
        { label: '普通管理员', value: 'normal' },
      ],
    },
    show: false,
  },
  {
    field: 'nickName',
    label: '昵称',
    component: 'Input',
    componentProps: {
      placeholder: '请输入昵称',
    },
    required: true,                                       // 是否必须
  },
  {
    field: 'userName',
    label: '登录名',
    component: 'Input',
    required: true,
  },
  {
    field: 'password',
    label: '登录密码',
    component: 'InputPassword',
  },
  {
    field: 'passwordConfirm',
    label: '再次确认密码',
    component: 'InputPassword',
  },
  {
    field: 'status',
    label: '状态',
    component: 'RadioGroup',
    defaultValue: '1',
    componentProps: {
      options: [
        { label: '正常', value: '1' },
        { label: '禁用', value: '0' },
      ],
    },
  },
  {
    field: 'userDesc',
    label: '描述',
    component: 'InputTextArea',
  },
];
// 导出 用户对话框  用户授权表单 JSON 
export const authFormSchema: FormSchema[] = [
  {
    field: 'userId',
    label: '',
    component: 'Input',
    show: false,
  },
  {
    field: 'authorityIds',
    label: '',
    component: 'TreeSelect',
    slot: 'menu', // 自定义表单组件 
    colProps: { span: 24 },
  },
];

  1. 父组件 index.vue 的ts逻辑

主要是vue引入 列表组件 对话框组件 的注册 和 列表增加、删除、编辑业务的逻辑

<script lang="ts">
import { defineComponent, computed } from 'vue';                              // 导入模块化 VUE  defineComponent,computed API
import { BasicTable, useTable, TableAction } from '/@/components/Table';      // 导入 vben封装的列表 BasicTable,TableAction 内置组件  useTable API
import { useModal } from '/@/components/Modal';                               // 导入 vben封装的对话框 useModal API
import UserModal from './UserModal.vue';                                      // 导入自己的基本业务对话框组件
import { columns, searchFormSchema } from './user.data';                      // 导入自定义 columns 表头 searchFormSchema 搜索表单  JSON 数据

import { queryUserPage, userRemove } from '/@/api/system/user';               // 导入 请求接口
import { useMessage } from '/@/hooks/web/useMessage';                         // 导入vben 消息 useMessage aPI
import { useUserStore } from '/@/store/modules/user';                         // 导入 由pinia编写 用户业务本地store库  useUserStore API
import { usePermission } from '/@/hooks/web/usePermission';                   //  导入 由pinia编写 权限业务本地store库  useUserStore API
export default defineComponent({                                              // defineComponent() 在定义 Vue 组件时提供类型推导的辅助函数
  name: 'UserManagement',                                                     // 组件名称
  components: { BasicTable, TableAction, UserModal },                         // 在UserManagement本组件注册 BasicTable, TableAction, UserModal, 组件
  setup() {                                                                   //  setup() 钩子是在组件中使用组合式 API 的入口
    const { createMessage: msg } = useMessage();                              //  消息api注册
    const userStore = useUserStore();                                         //  用户api注册
    const userinfo = computed(() => {                                         // 计算器属性获取用户信息
      return userStore.getUserInfo || {};
    });
    const { hasPermission } = usePermission()                                 // 注册 按钮权限 
    const [registerTable, { reload }] = useTable({                            // 注册 列表
      title: '用户管理',                                                       // 表格名称
      api: queryUserPage,                                                     // 通过api接口请求获取数据
      columns,                                                                // 表头
      formConfig: {                                                           // 搜索表单配置
        labelWidth: 80,
        schemas: searchFormSchema,                                            // 搜索 searchFormSchema JOSN
        showAdvancedButton: true,                                             // 搜索项过多是否展开收起
      },
      useSearchForm: true,
      showTableSetting: true,
      bordered: false,
      striped: false,
      showIndexColumn: false,
      actionColumn: { // 列表操作按钮
        width: 150,
        title: '操作',
        dataIndex: 'action',
        fixed: 'right',
      },
    });
    // 注册弹窗
    const [registerModal, { openModal }] = useModal();
    // 注册弹窗-修改密码
    const [registerPwdModal, { openModal: openPwdModal }] = useModal();
    // 添加
    function handleCreate() {
      openModal(true, {  // 使用对话框openModal API 打开对话框
        isUpdate: false, // { isUpdate: true } 打开对话框传递的参数 例: isUpdate: true  
      });
    }
    // 编辑
    function handleEdit(record: Recordable) {
      openModal(true, {
        record,
        isUpdate: true,
      });
    }
    // 删除
    async function handleDelete(record: Recordable) {
      await userRemove({ userId: record.userId });
      handleSuccess();
    }
    // 成功
    function handleSuccess() {
      msg.success('操作成功');
      reload();
    }
    // template html 需要使用的 数据 function 在这里暴露
    return {
      userinfo,
      hasPermission,
      registerTable,
      registerModal,
      registerPwdModal,
      handleCreate,
      handleDelete,
      handleEdit,
      handleSuccess,
    };
  },
});
</script>

  1. 父组件 index.vue 的template html部分
    主要是列表组件和自定义对话框的使用
<template>
  <div>
    <!-- 因为使用useTable提供的 api 必须将 register 传入组件的 onRegister -->
    <BasicTable @register="registerTable">  
     <!-- 表格工具栏 按钮 通常业务按钮, 例如:用户新增 不涉及列表数据 -->
      <template #toolbar>
        <a-button type="primary" @click="handleCreate"> 用户新增 </a-button>
      </template>
      <!-- 列表内部每一行操作 -->
      <template #bodyCell="{ column, record }">
        <template v-if="column.key === 'action'"> 
          <TableAction
            :actions="[
              {
                icon: 'clarity:note-edit-line',                                              // 图标
                label: '编辑',                                            // 操作名称
                onClick: handleEdit.bind(null, record),                   // 点击事件
                disabled: record.userType == 'super',                     // 通过业务逻辑 判读是否禁用
              },
            ]"
            :dropDownActions="[                                           // 操作过多时使用下拉
              {
                // icon: 'ant-design:delete-outlined',
                label: '删除',
                color: 'error',
                popConfirm: {
                  title: '是否确认删除',
                  confirm: handleDelete.bind(null, record),
                },
              },
            ]"
          />
        </template>
      </template>
    </BasicTable>
    <!-- 注册用户对话框 同理 register 用于注册 useModal,如果需要使用 useModal 提供的 api,必须将 register 传入组件的 onRegister -->
    <UserModal @register="registerModal" @success="handleSuccess" /> 
  </div>
</template>

对话框、表单示例

在这里插入图片描述

  1. 静态表单Schema数据 还是从 user.data.ts 文件导入

二、子组件UserModal.vue

  1. 子组件UserModal.vue 的ts逻辑
<script lang="ts">
  import { defineComponent, ref, computed, unref, reactive, toRefs } from 'vue';    // 导入模块化 VUE  defineComponent,ref, computed, unref, reactive, toRefs API
  import { Checkbox, Tree, Spin, Empty, Tag } from 'ant-design-vue';                // 导入 ant-design-vue 组件库  Checkbox, Tree, Spin, Empty, Tag 组件 
  import { BasicModal, useModalInner } from '/@/components/Modal';                  // 导入 vben BasicModal, useModalInner 对话框内置组件 API
  import { BasicForm, useForm } from '/@/components/Form/index';                    // 导入 vben BasicForm, useForm 表单内置组件 API
  import { formSchema, authFormSchema } from './user.data';                         // 导入 静态表单Schema数据 formSchema, authFormSchema 
  import { BasicTree, TreeItem } from '/@/components/Tree';                         // 导入 vben BasicTree, TreeItem 树组件

  import { userAdd, userUpdate, userRoles, userRolesAdd } from '/@/api/company/user';// api接口请求
  import { roleAll } from '/@/api/company/role';                                     // api接口请求
  import { authorityUserGrant, authorityUser, authorityMenuDomain } from '/@/api/auth/index';// api接口请求

  import {  listConvertTreeAuth } from '/@/utils/index';                             // 公共api
  import { intersection } from 'lodash-es'; 
  // 树结构扁平化
  const treeFlat = (tree: any[]) => {
    const arr = [];
    tree.forEach((item:any) => {
      arr.push(item:any);
      if (item.children && item.children.length > 0) {
        arr.push(...treeFlat(item.children));
      }
    });
    return arr;
  };
  export default defineComponent({
    name: 'UserModal',
    components: {
      BasicModal,
      BasicForm,
      Checkbox,
      ACheckboxGroup: Checkbox.Group,
      BasicTree,
      Tree,
      Spin,
      Empty,
      Tag,
    },
    emits: ['success', 'register'],
    setup(_, { emit }) {
      // ref reactive 让数据具有响应式
      // 定义childArr存放所有子节点 
      const childArr = ref<any>([]); 
      // 父级keys
      const halfCheckedKeys = ref([]);
      const treeData = ref<TreeItem[]>([]);
      const state = reactive({// 
        roleChecked: [],
        activeKey: '1',
        roleOptions: [],
        record: {},
        menuInit: <any>[], // 用于存放初始值判断是否选择了修改节点
      });
      // 添加-编辑控件
      const isUpdate = ref(true);
      // 注册基本信息表单
      const [
        registerBaseForm,
        {
          resetFields: resetBaseFields,
          setFieldsValue: setBaseFieldsValue,
          validate: validateBase,
          updateSchema: updateBaseSchema,
        },
      ] = useForm({
        labelWidth: 100,
        baseColProps: { span: 24 },
        schemas: formSchema,
        showActionButtonGroup: false,
      });
      // 注册权限表单
      const [
        registerAuthForm,
        {
          setFieldsValue: setAuthFieldsValue,
          validate: validateAuth,
        },
      ] = useForm({
        labelWidth: 100,
        baseColProps: { span: 24 },
        schemas: authFormSchema,
        showActionButtonGroup: false,
      });
      // 注册弹窗
      const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
        state.activeKey = '1';
        resetBaseFields();
        setModalProps({ confirmLoading: false });
        isUpdate.value = !!data?.isUpdate;
        getRole();
        if (unref(isUpdate)) {
          state.record = data.record;
          updateBaseSchema([
            { field: 'userName', componentProps: { disabled: true } },
            { field: 'mobile', componentProps: { disabled: true } },
            { field: 'email', componentProps: { disabled: true } },
          ]);
          setBaseFieldsValue({
            ...data.record,
          });
        } else {
          updateBaseSchema([
            { field: 'userName', componentProps: { disabled: false } },
            { field: 'mobile', componentProps: { disabled: false } },
            { field: 'email', componentProps: { disabled: false } },
          ]);
        }
      });

      const getTitle = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));

      // 获取用户角色
      async function getRole() {
        const data = await roleAll();
        state.roleOptions = data.map((item) => {
          return {
            label: item.roleName,
            value: item.roleId,
          };
        });
      }
      // 遍历获取所有子节点
      function getChildArr(data) {
        data.forEach((v: any) => {
          if (v.children && v.children.length > 0) {
            getChildArr(v.children);
          } else {
            childArr.value.push(v.authorityId);
          }
        });
        return childArr.value;
      }
      // 选择权限-只取选中父节点key
      function handleCheckMenu(e, node) {
        halfCheckedKeys.value = node.halfCheckedKeys;
      }
      function checkChangeIS(menuInit, menuIdvalues) {
        let isChange = false;
        let toStringA = menuInit.toString();
        let toStringB = menuIdvalues.toString();
        if (toStringA !== toStringB) {
          isChange = true;
        }
        return isChange;
      }
      // 获取用户已分配角色
      async function getUserRole() {
        const data = await userRoles({ userId: state.record.userId });
        state.roleChecked = data.map((item) => {
          return item.roleId;
        });
      }
      // tab切换
      async function tabChange(key, data) {
        state.activeKey = key;
        switch (key) {
          case '1':
            break;
          case '2':
            getUserRole();
            break;
          case '3':
            const menuAll = await authorityMenuDomain();
            const menuRole = await authorityUser({ userId: state.record.userId });
            const menuData = menuRole.map((item: any) => item.authorityId);
            treeData.value = listConvertTreeAuth(menuAll.length > 0 ? menuAll : [], {
              primaryKey: 'menuId',
              parentKey: 'parentId',
              startPid: '0',
            });
            const allChildArr = getChildArr(treeData.value);
            // // 使用lodash的_.intersection方法取出相同的id并赋值给checkedKeys 处理只选了子节点没选父节点的情况
            const menuId = intersection(menuData, allChildArr);
            state.menuInit = menuId;
            setAuthFieldsValue({
              authorityIds: menuId,
              userId: state.record.userId,
            });
            break;
          default:
            break;
        }
      }
      // 表单提交
      async function handleSubmit() {
        switch (state.activeKey) {
          case '1':
            
            break;
          case '2':
            
            break;
          case '3':
           
            break;
          default:
            break;
        }
      }

      return {
        getTitle,// 使用computed 在template使用需要暴露
        isUpdate,// 使用computed 在template使用需要暴露
        childArr,// 使用ref 在template使用需要暴露
        halfCheckedKeys,// 使用ref 在template使用需要暴露
        treeData,// 使用ref 在template使用需要暴露
        handleCheckMenu,
        ...toRefs(state),// 使用reactive 在template使用需要暴露
        registerModal,
        registerBaseForm,
        registerAuthForm,
        handleSubmit,
        tabChange,
      };
    },
  });
</script>
<template>
   <!-- 用于独立的 Modal 内部调用 因为使用useModalInner提供的 api 必须将 register 传入组件的 onRegister -->
  <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
    <a-tabs v-model:activeKey="activeKey" @change="tabChange($event, _)">
      <a-tab-pane key="1" tab="基本信息">
         <!-- 因为使用useForm提供的 api 必须将 register 传入组件的 onRegister -->
        <BasicForm @register="registerBaseForm" />
      </a-tab-pane>
      <a-tab-pane key="2" :disabled="!isUpdate" tab="角色信息">
        &nbsp;&nbsp;分配角色&nbsp;&nbsp;<a-checkbox-group
          v-model:value="roleChecked"
          name="checkboxgroup"
          :options="roleOptions"
        />
      </a-tab-pane>
      <a-tab-pane key="3" :disabled="!isUpdate" tab="分配权限">
       <!-- 因为使用useForm提供的 api 必须将 register 传入组件的 onRegister -->
        <BasicForm @register="registerAuthForm" class="w-full" hash-priority="high">
         <!-- 自定义表单组件 -->
          <template #menu="{ model, field }">
            <BasicTree
              v-model:value="model[field]"
              search
              :treeData="treeData"
              :fieldNames="{
                children: 'children',
                title: 'menuName',
                key: 'authorityId',
                isLeaf: 'isLeaf',
              }"
              checkable
              toolbar
              title="权限分配"
              @check="handleCheckMenu"
              class="w-full"
            >
              <template #title="{ menuName, type }">
                {{ menuName }}
                <Tag class="router-type" :color="type === 1 ? 'green' : 'default'">
                  {{ type === 1 ? '菜单' : '按钮' }}
                </Tag>
              </template>
            </BasicTree>
          </template>
        </BasicForm>
      </a-tab-pane>
    </a-tabs>
  </BasicModal>
</template>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值