Quellcode durchsuchen

fix: 代码生成和字典

fan vor 2 Jahren
Ursprung
Commit
733bbeba38

+ 17 - 8
src/views/infra/gen/data.ts

@@ -1,5 +1,5 @@
 import { BasicColumn, FormSchema } from '/@/components/Table';
-import { sysDictQueryList } from '/@/api/sys/sysDictApi';
+import { sysDictQueryTree } from '/@/api/sys/sysDictApi';
 export const columns: BasicColumn[] = [
   {
     title: '业务名',
@@ -267,15 +267,24 @@ export const columnsEdit: BasicColumn[] = [
     dataIndex: 'dictTypeCode',
     edit: true,
     editable: true,
-    editComponent: 'ApiSelect',
+    editComponent: 'ApiTreeSelect',
     editComponentProps: {
-      api: sysDictQueryList,
-      editIconHidden: true,
-      showSearch: true,
+      // api: sysDictQueryList,
+      // editIconHidden: true,
+      // showSearch: true,
+      // resultField: 'data',
+      // extraLabelField: 'dictName',
+      // labelField: 'dictCode',
+      // valueField: 'dictCode',
+      api: sysDictQueryTree,
+      params: { root: true },
       resultField: 'data',
-      extraLabelField: 'dictName',
-      labelField: 'dictCode',
-      valueField: 'dictCode',
+      fieldNames: {
+        label: 'dictName',
+        key: 'dictCode',
+        value: 'dictCode',
+      },
+      getPopupContainer: () => document.body,
     },
   },
 ];

+ 2 - 2
src/views/infra/gen/step/basic.vue

@@ -312,7 +312,7 @@
   // 上级
   const moduleChange = async () => {
     const menuTree = await sysMenuQueryTree({
-      menuType: 'dir',
+      menuType: ['dir'],
     });
     menuTreeData.value = [
       {
@@ -419,7 +419,7 @@
 
   .form-row {
     background-color: var(--item-hover-bg);
-    margin-left: 0px !important;
+    margin-left: 0 !important;
   }
 
   .form-row-con {

+ 6 - 3
src/views/sys/sysDict/category/FormModal.vue

@@ -36,10 +36,13 @@
     await resetFields();
     setModalProps({ confirmLoading: false });
     isUpdate.value = !!data?.isUpdate;
-    dictType.value = data?.dictType;
-    rowId.value = data.record.id;
-    const resData = data.record;
+    dictType.value = data.dictType;
+    rowId.value = data.record?.id || '';
+    const resData = data.record || {};
     resData.dictType = dictType.value;
+    if (!isUpdate.value) {
+      resData.parentId = data.parentId;
+    }
     await setFieldsValue({
       ...resData,
     });

+ 22 - 5
src/views/sys/sysDict/category/data.ts

@@ -15,8 +15,13 @@ export const columns: BasicColumn[] = [
     dataIndex: 'dictType',
   },
   {
-    title: '颜色',
-    dataIndex: 'color',
+    title: '字体颜色',
+    dataIndex: 'fontColor',
+    width: 120,
+  },
+  {
+    title: '背景颜色',
+    dataIndex: 'bgColor',
     width: 120,
   },
   {
@@ -115,13 +120,25 @@ export const dataFormSchema: FormSchema[] = [
     },
   },
   {
-    field: 'color',
-    label: '颜色',
+    field: 'bgColor',
+    label: '背景颜色',
+    component: 'FormColorPicker',
+    componentProps: ({ formModel }) => {
+      return {
+        onChange: e => {
+          formModel.bgColor = e;
+        },
+      };
+    },
+  },
+  {
+    field: 'fontColor',
+    label: '字体颜色',
     component: 'FormColorPicker',
     componentProps: ({ formModel }) => {
       return {
         onChange: e => {
-          formModel.color = e;
+          formModel.fontColor = e;
         },
       };
     },

+ 14 - 5
src/views/sys/sysDict/category/index.vue

@@ -2,7 +2,7 @@
   <a-row>
     <a-col :span="5">
       <a-tree
-        v-if="treeData.length > 0"
+        v-if="treeData.length"
         v-model:expandedKeys="defaultExpandedKeys"
         :tree-data="treeData"
         :field-names="treeFieldNames"
@@ -18,9 +18,14 @@
               {{ formatDictValue(sysDictTypeOptions, record.dictType) }}
             </Tag>
           </template>
-          <template v-if="column.key === 'color'">
-            <Tag :color="record.color || '#000'">
-              {{ record.color }}
+          <template v-if="column.key === 'fontColor'">
+            <Tag :color="record.fontColor || '#000'">
+              {{ record.fontColor }}
+            </Tag>
+          </template>
+          <template v-if="column.key === 'bgColor'">
+            <Tag :color="record.bgColor || '#000'">
+              {{ record.bgColor }}
             </Tag>
           </template>
           <template v-if="column.key === 'action'">
@@ -57,7 +62,7 @@
           >
         </template>
       </BasicTable>
-      <FormModal @register="registerModal" @success="handleSuccess" />
+      <FormModal @register="registerModal" @success="handleSuccess" :destroyOnClose="true" />
     </a-col>
   </a-row>
 </template>
@@ -156,12 +161,14 @@
   function handleCreate() {
     openModal(true, {
       dictType: props.type,
+      parentId: parentId.value,
       isUpdate: false,
     });
   }
 
   // 编辑按钮事件
   function handleEdit(record: Recordable) {
+    console.log('🚀 ~ file: index.vue:165 ~ handleEdit ~ record:', record);
     openModal(true, {
       record,
       dictType: record.dictType || '1',
@@ -174,11 +181,13 @@
     console.log(record);
     await sysDictRemove([record.id]);
     createMessage.success('删除成功!');
+    await getTreeData();
     await reload();
   }
 
   // 弹窗回调事件
   async function handleSuccess() {
+    await getTreeData();
     await reload();
   }
 

+ 24 - 0
src/views/sys/sysDict/old/index copy.vue

@@ -0,0 +1,24 @@
+<template>
+  <PageWrapper dense contentFullHeight contentClass="flex">
+    <SysDictTable class="w-1/2" @dict-change="handleDictChange" />
+    <SysDictItemTable class="w-1/2" :dictId="dictId" />
+  </PageWrapper>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { PageWrapper } from '/@/components/Page';
+  import SysDictTable from './sysDictTable/index.vue';
+  import SysDictItemTable from './sysDictItemTable/index.vue';
+  // import { filterSub, getSub } from '/@/utils/websocket';
+
+  defineOptions({
+    name: 'sysDict',
+  });
+
+  const dictId = ref();
+  // 字典选中事件
+  function handleDictChange(value) {
+    if (!value) return;
+    dictId.value = value;
+  }
+</script>

+ 61 - 0
src/views/sys/sysDict/old/sysDictItemTable/FormModal.vue

@@ -0,0 +1,61 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { dataFormSchema } from './data';
+
+  import { sysDictItemAdd, sysDictItemEdit } from '/@/api/sys/sysDictItemApi';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增字典项' : '编辑字典项'));
+  const isUpdate = ref(false);
+  const rowId = ref();
+  const dictId = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    labelWidth: 100,
+    schemas: dataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    dictId.value = data?.dictId;
+    rowId.value = data.record.id;
+    const resData = data.record;
+    await setFieldsValue({
+      ...resData,
+    });
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      !unref(isUpdate)
+        ? await sysDictItemAdd({ ...values, id: rowId.value, dictId: dictId.value })
+        : await sysDictItemEdit({ ...values, id: rowId.value, dictId: dictId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeModal();
+      emit('success', {
+        isUpdate: unref(isUpdate),
+        values: { ...values, id: rowId.value, dictId: dictId.value },
+      });
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 115 - 0
src/views/sys/sysDict/old/sysDictItemTable/data.ts

@@ -0,0 +1,115 @@
+import { BasicColumn, FormSchema } from '/@/components/Table';
+import { validateStr } from '/@/utils/validate';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '字典项名称',
+    dataIndex: 'label',
+  },
+  {
+    title: '字典项编码',
+    dataIndex: 'value',
+    width: 120,
+  },
+  {
+    title: '颜色',
+    dataIndex: 'color',
+    width: 120,
+  },
+  {
+    title: '排序',
+    dataIndex: 'sort',
+    width: 80,
+  },
+  {
+    title: '备注',
+    dataIndex: 'remark',
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'dictItemName',
+    label: '字典项名称',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入字典项名称',
+    },
+  },
+  {
+    field: 'dictItemCode',
+    label: '字典项编码',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入字典项编码',
+    },
+  },
+];
+
+export const dataFormSchema: FormSchema[] = [
+  {
+    field: 'label',
+    label: '字典项名称',
+    component: 'Input',
+    required: true,
+    componentProps: {
+      placeholder: '请输入字典项名称',
+    },
+  },
+  {
+    field: 'value',
+    label: '字典项编码',
+    component: 'Input',
+    required: true,
+    componentProps: {
+      placeholder: '请输入字典项编码',
+    },
+    dynamicRules: () => {
+      return [
+        {
+          required: true,
+          validator: async (_, value) => {
+            if (!value) {
+              return Promise.reject('字典项编码不能为空');
+            }
+            if (validateStr(value)) {
+              return Promise.reject('字典项编码为字母或数字组成');
+            }
+            return Promise.resolve();
+          },
+        },
+      ];
+    },
+  },
+  {
+    field: 'color',
+    label: '颜色',
+    component: 'FormColorPicker',
+    componentProps: ({ formModel }) => {
+      return {
+        onChange: e => {
+          formModel.color = e;
+        },
+      };
+    },
+  },
+  {
+    field: 'sort',
+    label: '排序',
+    component: 'InputNumber',
+    required: true,
+    defaultValue: '1',
+    componentProps: {
+      placeholder: '请输入排序',
+      min: 1,
+    },
+  },
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'InputTextArea',
+    componentProps: {
+      placeholder: '请输入备注',
+    },
+  },
+];

+ 163 - 0
src/views/sys/sysDict/old/sysDictItemTable/index.vue

@@ -0,0 +1,163 @@
+<template>
+  <div style="margin-top: 16px">
+    <BasicTable @register="registerTable">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'color'">
+          <Tag :color="record.color || '#000'">
+            {{ record.color }}
+          </Tag>
+        </template>
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                auth: ['sys:dictItem:edit'],
+                icon: 'icon-edit|iconfont',
+                tooltip: '编辑',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                auth: ['sys:dictItem:remove'],
+                icon: 'icon-delete|iconfont',
+                tooltip: '删除',
+                color: 'error',
+                popConfirm: {
+                  title: '是否确认删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+      <template #toolbar>
+        <a-button
+          v-auth="['sys:dictItem:add']"
+          v-show="!!dictId"
+          type="primary"
+          @click="handleCreate"
+          preIcon="icon-plus|iconfont"
+          >新增</a-button
+        >
+      </template>
+    </BasicTable>
+    <FormModal @register="registerModal" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { Tag } from 'ant-design-vue';
+  import { nextTick, onBeforeMount, ref, watch } from 'vue';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import FormModal from './FormModal.vue';
+  import { columns, searchFormSchema } from './data';
+  import { useMessage } from '/@/hooks/web/useMessage';
+
+  import { sysDictItemQueryList, sysDictItemRemove } from '/@/api/sys/sysDictItemApi';
+  import { listDictModel } from '/@/api/common';
+
+  const props = defineProps({
+    dictId: { type: String },
+  });
+
+  const sysStatusOptions = ref([]);
+  onBeforeMount(async () => {
+    sysStatusOptions.value = await listDictModel({ dictCode: 'sys_status' });
+  });
+
+  // const { createMessage, createConfirm } = useMessage();
+  const { createMessage } = useMessage();
+  const [registerModal, { openModal }] = useModal();
+  const [registerTable, { reload }] = useTable({
+    title: '字典项列表',
+    titleHelpMessage: '请先选中字典,再操作字典项',
+    api: sysDictItemQueryList,
+    rowKey: 'id',
+    columns,
+    formConfig: {
+      labelWidth: 120,
+      schemas: searchFormSchema,
+      autoSubmitOnEnter: true,
+      resetButtonOptions: {
+        preIcon: 'ant-design:delete-outlined',
+      },
+      submitButtonOptions: {
+        preIcon: 'ant-design:search-outlined',
+      },
+    },
+    immediate: false,
+    showIndexColumn: false,
+    useSearchForm: false,
+    showTableSetting: true,
+    bordered: true,
+    actionColumn: {
+      auth: ['sys:dictItem:edit', 'sys:dictItem:remove'],
+      width: 80,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+  });
+
+  // 新增按钮事件
+  function handleCreate() {
+    openModal(true, {
+      isUpdate: false,
+      dictId: props.dictId,
+    });
+  }
+
+  // 编辑按钮事件
+  function handleEdit(record: Recordable) {
+    console.log(record);
+    openModal(true, {
+      record,
+      isUpdate: true,
+      dictId: props.dictId,
+    });
+  }
+
+  // 删除按钮事件
+  async function handleDelete(record: Recordable) {
+    console.log('🚀 ~ file: index.vue:141 ~ handleDelete ~ record', record);
+    await sysDictItemRemove([record.id]);
+    createMessage.success('删除成功!');
+    await reload();
+  }
+
+  // 导出按钮事件
+  // async function handleExport() {
+  //   createConfirm({
+  //     iconType: 'warning',
+  //     title: '提示',
+  //     content: '确认导出?',
+  //     onOk: async () => {
+  //       const params = Object.assign({}, getForm().getFieldsValue(), { dictId: props.dictId });
+  //       const filepath = await exportList(params);
+  //       downloadFile(filepath);
+  //     },
+  //   });
+  // }
+
+  // 弹窗回调事件
+  function handleSuccess({ isUpdate, values }) {
+    console.log(isUpdate);
+    console.log(values);
+    reload();
+  }
+
+  // 表格请求之前,对参数进行处理
+  function handleBeforeFetch(params) {
+    return { ...params, dictId: props.dictId, orders: [{ field: 'sort', direction: 'ASC' }] };
+  }
+
+  watch(
+    () => props.dictId,
+    () => {
+      nextTick(() => {
+        reload();
+      });
+    },
+  );
+</script>

+ 57 - 0
src/views/sys/sysDict/old/sysDictTable/FormModal.vue

@@ -0,0 +1,57 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { dataFormSchema } from './data';
+
+  import { sysDictAdd, sysDictEdit } from '/@/api/sys/sysDictApi';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增字典' : '编辑字典'));
+  const isUpdate = ref(false);
+  const rowId = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    labelWidth: 100,
+    schemas: dataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    rowId.value = data.record.id;
+    const resData = data.record;
+    resData.disable = String(resData.disable);
+    await setFieldsValue({
+      ...resData,
+    });
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      !unref(isUpdate)
+        ? await sysDictAdd({ ...values })
+        : await sysDictEdit({ ...values, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeModal();
+      emit('success', { isUpdate: unref(isUpdate), values: { ...values, id: rowId.value } });
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 128 - 0
src/views/sys/sysDict/old/sysDictTable/data.ts

@@ -0,0 +1,128 @@
+import { BasicColumn, FormSchema } from '/@/components/Table';
+import { listDictModel } from '/@/api/common';
+import { validateStr } from '/@/utils/validate';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '字典名称',
+    dataIndex: 'dictName',
+  },
+  {
+    title: '字典编码',
+    dataIndex: 'dictCode',
+  },
+  {
+    title: '字典类型',
+    dataIndex: 'dictType',
+  },
+  {
+    title: '状态',
+    dataIndex: 'disable',
+    width: 80,
+  },
+  {
+    title: '备注',
+    dataIndex: 'remark',
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'dictName',
+    label: '字典名称',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入字典名称',
+    },
+  },
+  {
+    field: 'dictCode',
+    label: '字典编码',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入字典编码',
+    },
+  },
+  {
+    field: 'dictType',
+    label: '字典类型',
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'sys_dict_type',
+      },
+    },
+    defaultValue: '1',
+  },
+];
+
+export const dataFormSchema: FormSchema[] = [
+  {
+    field: 'dictName',
+    label: '字典名称',
+    component: 'Input',
+    required: true,
+    componentProps: {
+      placeholder: '请输入字典名称',
+    },
+  },
+  {
+    field: 'dictCode',
+    label: '字典编码',
+    component: 'Input',
+    required: true,
+    componentProps: {
+      placeholder: '请输入字典编码',
+    },
+    dynamicRules: () => {
+      return [
+        {
+          required: true,
+          validator: async (_, value) => {
+            if (!value) {
+              return Promise.reject('字典项编码不能为空');
+            }
+            if (validateStr(value)) {
+              return Promise.reject('字典项编码为字母或数字组成');
+            }
+            return Promise.resolve();
+          },
+        },
+      ];
+    },
+  },
+  {
+    field: 'dictType',
+    label: '字典类型',
+    component: 'ApiSelect',
+    required: true,
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'sys_dict_type',
+      },
+    },
+  },
+  {
+    field: 'disable',
+    label: '状态',
+    component: 'ApiRadioGroup',
+    required: true,
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'sys_disable_type',
+      },
+    },
+    defaultValue: '0',
+  },
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'InputTextArea',
+    componentProps: {
+      placeholder: '请输入备注',
+    },
+  },
+];

+ 220 - 0
src/views/sys/sysDict/old/sysDictTable/index.vue

@@ -0,0 +1,220 @@
+<template>
+  <div>
+    <BasicTable
+      @register="registerTable"
+      @selection-change="handleSelectionChange"
+      @row-click="handleRowClick"
+      @row-dbClick="handleRowDbClick"
+    >
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'disable'">
+          <Tag :color="formatDictColor(sysDisableType, record.disable)">
+            {{ formatDictValue(sysDisableType, record.disable) }}
+          </Tag>
+        </template>
+        <template v-if="column.key === 'dictType'">
+          <Tag :color="formatDictColor(sysDictTypeOptions, record.dictType)">
+            {{ formatDictValue(sysDictTypeOptions, record.dictType) }}
+          </Tag>
+        </template>
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                auth: ['sys:dict:edit'],
+                icon: 'icon-edit|iconfont',
+                tooltip: '编辑',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                auth: ['sys:dict:remove'],
+                icon: 'icon-delete|iconfont',
+                tooltip: '删除',
+                color: 'error',
+                popConfirm: {
+                  title: '是否确认删除',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+      <template #toolbar>
+        <a-button
+          v-auth="['sys:dict:add']"
+          type="primary"
+          @click="handleCreate"
+          preIcon="icon-plus|iconfont"
+          >新增</a-button
+        >
+      </template>
+    </BasicTable>
+    <FormModal @register="registerModal" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { onBeforeMount, ref } from 'vue';
+  import { Tag } from 'ant-design-vue';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import FormModal from './FormModal.vue';
+  import { columns, searchFormSchema } from './data';
+  import { sysDictQueryPage, sysDictRemove } from '/@/api/sys/sysDictApi';
+  import { formatDictColor, formatDictValue } from '/@/utils';
+  import { listDictModel } from '/@/api/common';
+
+  const emit = defineEmits(['dict-change']);
+
+  const sysDictTypeOptions = ref([]);
+  const sysDisableType = ref();
+  onBeforeMount(async () => {
+    const dictTypeStatus = await listDictModel({ dictCode: 'sys_dict_type' });
+    sysDisableType.value = await listDictModel({ dictCode: 'sys_disable_type' });
+    sysDictTypeOptions.value = dictTypeStatus || [];
+  });
+
+  const { createMessage } = useMessage();
+  const [registerModal, { openModal }] = useModal();
+  const [registerTable, { reload, getDataSource, getSelectRowKeys, setSelectedRowKeys }] = useTable(
+    {
+      title: '字典列表',
+      api: sysDictQueryPage,
+      rowKey: 'id',
+      columns,
+      rowSelection: { type: 'radio' },
+      clickToRowSelect: true,
+      formConfig: {
+        labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        baseColProps: { span: 12 },
+        resetButtonOptions: {
+          preIcon: 'icon-delete|iconfont',
+        },
+        submitButtonOptions: {
+          preIcon: 'icon-search|iconfont',
+        },
+      },
+      showIndexColumn: false,
+      useSearchForm: true,
+      showTableSetting: true,
+      bordered: true,
+      actionColumn: {
+        auth: ['sys:dict:edit', 'sys:dict:remove'],
+        width: 80,
+        title: '操作',
+        dataIndex: 'action',
+      },
+      afterFetch: handleAfterFetch,
+    },
+  );
+
+  // 新增按钮事件
+  function handleCreate() {
+    openModal(true, {
+      isUpdate: false,
+    });
+  }
+
+  // 编辑按钮事件
+  function handleEdit(record: Recordable) {
+    console.log(record);
+    openModal(true, {
+      record,
+      isUpdate: true,
+    });
+  }
+
+  // 删除按钮事件
+  async function handleDelete(record: Recordable) {
+    console.log(record);
+    await sysDictRemove([record.id]);
+    createMessage.success('删除成功!');
+    await reload();
+    // after delete, select first row
+    const list = getDataSource();
+    if (list.length > 0) {
+      setSelectedRowKeys([list[0].id]);
+    } else {
+      setSelectedRowKeys([]);
+    }
+    console.log('handleDelete');
+    emitDictChange();
+  }
+
+  // 导出按钮事件
+  // async function handleExport() {
+  //   createConfirm({
+  //     iconType: 'warning',
+  //     title: '提示',
+  //     content: '确认导出?',
+  //     onOk: async () => {
+  //       const params = getForm().getFieldsValue();
+  //       const filepath = await exportList(params);
+  //       downloadFile(filepath);
+  //     },
+  //   });
+  // }
+
+  // 弹窗回调事件
+  async function handleSuccess({ isUpdate, values }) {
+    console.log(isUpdate);
+    console.log(values);
+    await reload();
+    if (isUpdate) {
+      // after update, select updated row
+      setSelectedRowKeys([values.id]);
+    } else {
+      // after create, select first row
+      const list = getDataSource();
+      if (list.length > 0) {
+        setSelectedRowKeys([list[0].id]);
+      } else {
+        setSelectedRowKeys([]);
+      }
+    }
+    console.log('handleSuccess', isUpdate ? 'update' : 'create');
+    emitDictChange();
+  }
+
+  // 表格请求之后,对返回值进行处理
+  function handleAfterFetch(data) {
+    // after fetch, select first row
+    if (data.length > 0) {
+      setSelectedRowKeys([data[0].id]);
+    } else {
+      setSelectedRowKeys([]);
+    }
+    console.log('handleAfterFetch', data);
+    emitDictChange();
+  }
+
+  // 表格行点击事件
+  function handleRowClick(record: Recordable) {
+    console.log('handleRowClick', record);
+    setSelectedRowKeys([record.id]);
+    emitDictChange();
+  }
+
+  // 表格行双击事件
+  function handleRowDbClick(record: Recordable) {
+    console.log('handleRowDbClick', record);
+    setSelectedRowKeys([record.id]);
+    emitDictChange();
+  }
+
+  // 表格行选中事件
+  function handleSelectionChange({ keys, rows }) {
+    console.log('handleSelectionChange', keys, rows);
+    emitDictChange();
+  }
+
+  // 字典变化事件
+  function emitDictChange() {
+    const selectedKeys = getSelectRowKeys();
+    console.log(selectedKeys);
+    emit('dict-change', selectedKeys.length > 0 ? selectedKeys[0] : '');
+  }
+</script>