Forráskód Böngészése

!68 Vue3 重构:基础设施 -> 代码生成
Merge pull request !68 from xiaowuye/dev

芋道源码 2 éve
szülő
commit
95b7eb9856

+ 11 - 11
src/api/infra/codegen/index.ts

@@ -2,56 +2,56 @@ import request from '@/config/axios'
 import type { CodegenUpdateReqVO, CodegenCreateListReqVO } from './types'
 
 // 查询列表代码生成表定义
-export const getCodegenTablePageApi = (params) => {
+export const getCodegenTablePage = (params) => {
   return request.get({ url: '/infra/codegen/table/page', params })
 }
 
 // 查询详情代码生成表定义
-export const getCodegenTableApi = (id: number) => {
+export const getCodegenTable = (id: number) => {
   return request.get({ url: '/infra/codegen/detail?tableId=' + id })
 }
 
 // 新增代码生成表定义
-export const createCodegenTableApi = (data: CodegenCreateListReqVO) => {
+export const createCodegenTable = (data: CodegenCreateListReqVO) => {
   return request.post({ url: '/infra/codegen/create', data })
 }
 
 // 修改代码生成表定义
-export const updateCodegenTableApi = (data: CodegenUpdateReqVO) => {
+export const updateCodegenTable = (data: CodegenUpdateReqVO) => {
   return request.put({ url: '/infra/codegen/update', data })
 }
 
 // 基于数据库的表结构,同步数据库的表和字段定义
-export const syncCodegenFromDBApi = (id: number) => {
+export const syncCodegenFromDB = (id: number) => {
   return request.put({ url: '/infra/codegen/sync-from-db?tableId=' + id })
 }
 
 // 基于 SQL 建表语句,同步数据库的表和字段定义
-export const syncCodegenFromSQLApi = (id: number, sql: string) => {
+export const syncCodegenFromSQL = (id: number, sql: string) => {
   return request.put({ url: '/infra/codegen/sync-from-sql?tableId=' + id + '&sql=' + sql })
 }
 
 // 预览生成代码
-export const previewCodegenApi = (id: number) => {
+export const previewCodegen = (id: number) => {
   return request.get({ url: '/infra/codegen/preview?tableId=' + id })
 }
 
 // 下载生成代码
-export const downloadCodegenApi = (id: number) => {
+export const downloadCodegen = (id: number) => {
   return request.download({ url: '/infra/codegen/download?tableId=' + id })
 }
 
 // 获得表定义
-export const getSchemaTableListApi = (params) => {
+export const getSchemaTableList = (params) => {
   return request.get({ url: '/infra/codegen/db/table/list', params })
 }
 
 // 基于数据库的表结构,创建代码生成器的表定义
-export const createCodegenListApi = (data) => {
+export const createCodegenList = (data) => {
   return request.post({ url: '/infra/codegen/create-list', data })
 }
 
 // 删除代码生成表定义
-export const deleteCodegenTableApi = (id: number) => {
+export const deleteCodegenTable = (id: number) => {
   return request.delete({ url: '/infra/codegen/delete?tableId=' + id })
 }

+ 1 - 1
src/api/infra/codegen/types.ts

@@ -52,7 +52,7 @@ export type CodegenPreviewVO = {
   code: string
 }
 export type CodegenUpdateReqVO = {
-  table: CodegenTableVO
+  table: CodegenTableVO | any
   columns: CodegenColumnVO[]
 }
 export type CodegenCreateListReqVO = {

+ 2 - 1
src/locales/zh-CN.ts

@@ -298,7 +298,8 @@ export default {
     typeUpdate: '字典类型编辑',
     dataCreate: '字典数据新增',
     dataUpdate: '字典数据编辑',
-    fileUpload: '上传文件'
+    fileUpload: '上传文件',
+    back: '返回'
   },
   dialog: {
     dialog: '弹窗',

+ 51 - 44
src/views/infra/codegen/EditTable.vue

@@ -1,67 +1,74 @@
 <template>
-  <ContentWrap>
-    <ContentDetailWrap :title="title" @back="push('/infra/codegen')">
-      <el-tabs v-model="activeName">
-        <el-tab-pane label="基本信息" name="basicInfo">
-          <BasicInfoForm ref="basicInfoRef" :basicInfo="tableCurrentRow" />
-        </el-tab-pane>
-        <el-tab-pane label="字段信息" name="cloum">
-          <CloumInfoForm ref="cloumInfoRef" :info="cloumCurrentRow" />
-        </el-tab-pane>
-      </el-tabs>
-      <template #right>
-        <XButton
-          type="primary"
-          :title="t('action.save')"
-          :loading="loading"
-          @click="submitForm()"
-        />
-      </template>
-    </ContentDetailWrap>
-  </ContentWrap>
+  <content-wrap v-loading="loading">
+    <el-tabs v-model="activeName">
+      <el-tab-pane label="基本信息" name="basicInfo">
+        <basic-info-form ref="basicInfoRef" :table="formData.table" />
+      </el-tab-pane>
+      <el-tab-pane label="字段信息" name="colum">
+        <colum-info-form ref="columInfoRef" :columns="formData.columns" />
+      </el-tab-pane>
+      <el-tab-pane label="生成信息" name="generateInfo">
+        <generate-info-form ref="generateInfoRef" :table="formData.table" />
+      </el-tab-pane>
+    </el-tabs>
+    <el-form label-width="100px">
+      <el-form-item style="text-align: center; margin-left: -100px; margin-top: 10px">
+        <el-button type="primary" @click="submitForm" :loading="submitLoading">
+          {{ t('action.save') }}
+        </el-button>
+        <el-button @click="close">{{ t('action.back') }}</el-button>
+      </el-form-item>
+    </el-form>
+  </content-wrap>
 </template>
 <script setup lang="ts">
-import { BasicInfoForm, CloumInfoForm } from './components'
-import { getCodegenTableApi, updateCodegenTableApi } from '@/api/infra/codegen'
-import { CodegenTableVO, CodegenColumnVO, CodegenUpdateReqVO } from '@/api/infra/codegen/types'
+import { BasicInfoForm, ColumInfoForm, GenerateInfoForm } from './components'
+import * as CodegenApi from '@/api/infra/codegen'
+import ContentWrap from '@/components/ContentWrap/src/ContentWrap.vue'
+import { useTagsViewStore } from '@/store/modules/tagsView'
+import { CodegenUpdateReqVO } from '@/api/infra/codegen/types'
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
-const { push } = useRouter()
+const { push, currentRoute } = useRouter()
 const { query } = useRoute()
+const { delView } = useTagsViewStore()
 const loading = ref(false)
-const title = ref('代码生成')
+const submitLoading = ref(false)
 const activeName = ref('basicInfo')
-const cloumInfoRef = ref(null)
-const tableCurrentRow = ref<CodegenTableVO>()
-const cloumCurrentRow = ref<CodegenColumnVO[]>([])
 const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>()
+const columInfoRef = ref<ComponentRef<typeof ColumInfoForm>>()
+const generateInfoRef = ref<ComponentRef<typeof GenerateInfoForm>>()
+const formData = ref<CodegenUpdateReqVO>({
+  table: {},
+  columns: []
+})
 
-const getList = async () => {
+const getDetail = async () => {
   const id = query.id as unknown as number
   if (id) {
+    loading.value = true
     // 获取表详细信息
-    const res = await getCodegenTableApi(id)
-    title.value = '修改[ ' + res.table.tableName + ' ]生成配置'
-    tableCurrentRow.value = res.table
-    cloumCurrentRow.value = res.columns
+    formData.value = await CodegenApi.getCodegenTable(id)
+    loading.value = false
   }
 }
 const submitForm = async () => {
-  const basicInfo = unref(basicInfoRef)
-  const basicForm = await basicInfo?.elFormRef?.validate()?.catch(() => {})
-  if (basicForm) {
-    const basicInfoData = (await basicInfo?.getFormData()) as CodegenTableVO
-    const genTable: CodegenUpdateReqVO = {
-      table: basicInfoData,
-      columns: cloumCurrentRow.value
-    }
-    await updateCodegenTableApi(genTable)
+  if (!unref(formData)) return
+  try {
+    await unref(basicInfoRef)?.validate()
+    await unref(generateInfoRef)?.validate()
+    await CodegenApi.updateCodegenTable(unref(formData))
     message.success(t('common.updateSuccess'))
     push('/infra/codegen')
-  }
+  } catch {}
+}
+/** 关闭按钮 */
+const close = () => {
+  delView(unref(currentRoute))
+  push('/infra/codegen')
 }
 onMounted(() => {
-  getList()
+  getDetail()
 })
 </script>

+ 65 - 155
src/views/infra/codegen/components/BasicInfoForm.vue

@@ -1,183 +1,93 @@
 <template>
-  <Form :rules="rules" @register="register" />
+  <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
+    <el-row>
+      <el-col :span="12">
+        <el-form-item label="表名称" prop="tableName">
+          <el-input placeholder="请输入仓库名称" v-model="formData.tableName" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="表描述" prop="tableComment">
+          <el-input placeholder="请输入" v-model="formData.tableComment" />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="12">
+        <el-form-item prop="className">
+          <template #label>
+            <span>
+              实体类名称
+              <el-tooltip
+                content="默认去除表名的前缀。如果存在重复,则需要手动添加前缀,避免 MyBatis 报 Alias 重复的问题。"
+                placement="top"
+              >
+                <Icon icon="ep:question-filled" class="" />
+              </el-tooltip>
+            </span>
+          </template>
+
+          <el-input placeholder="请输入" v-model="formData.className" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="作者" prop="author">
+          <el-input placeholder="请输入" v-model="formData.author" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="24">
+        <el-form-item label="备注" prop="remark">
+          <el-input type="textarea" :rows="3" v-model="formData.remark" />
+        </el-form-item>
+      </el-col>
+    </el-row>
+  </el-form>
 </template>
 <script setup lang="ts">
-import { useForm } from '@/hooks/web/useForm'
-import { FormSchema } from '@/types/form'
 import { CodegenTableVO } from '@/api/infra/codegen/types'
-import { getIntDictOptions } from '@/utils/dict'
-import { getSimpleMenusList } from '@/api/system/menu'
-import { handleTree, defaultProps } from '@/utils/tree'
 import { PropType } from 'vue'
 
+const emits = defineEmits(['update:basicInfo'])
 const props = defineProps({
-  basicInfo: {
+  table: {
     type: Object as PropType<Nullable<CodegenTableVO>>,
     default: () => null
   }
 })
 
-const templateTypeOptions = getIntDictOptions(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE)
-const sceneOptions = getIntDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE)
-const menuOptions = ref<any>([]) // 树形结构
-const getTree = async () => {
-  const res = await getSimpleMenusList()
-  menuOptions.value = handleTree(res)
-}
+const formRef = ref()
+const formData = ref({
+  tableName: '',
+  tableComment: '',
+  className: '',
+  author: '',
+  remark: ''
+})
 
 const rules = reactive({
   tableName: [required],
   tableComment: [required],
   className: [required],
-  author: [required],
-  templateType: [required],
-  scene: [required],
-  moduleName: [required],
-  businessName: [required],
-  businessPackage: [required],
-  classComment: [required]
-})
-const schema = reactive<FormSchema[]>([
-  {
-    label: '上级菜单',
-    field: 'parentMenuId',
-    component: 'TreeSelect',
-    componentProps: {
-      data: menuOptions,
-      props: defaultProps,
-      checkStrictly: true,
-      nodeKey: 'id'
-    },
-    labelMessage: '分配到指定菜单下,例如 系统管理',
-    colProps: {
-      span: 24
-    }
-  },
-  {
-    label: '表名称',
-    field: 'tableName',
-    component: 'Input',
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '表描述',
-    field: 'tableComment',
-    component: 'Input',
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '实体类名称',
-    field: 'className',
-    component: 'Input',
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '类名称',
-    field: 'className',
-    component: 'Input',
-    labelMessage: '类名称(首字母大写),例如SysUser、SysMenu、SysDictData 等等',
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '生成模板',
-    field: 'templateType',
-    component: 'Select',
-    componentProps: {
-      options: templateTypeOptions
-    },
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '生成场景',
-    field: 'scene',
-    component: 'Select',
-    componentProps: {
-      options: sceneOptions
-    },
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '模块名',
-    field: 'moduleName',
-    component: 'Input',
-    labelMessage: '模块名,即一级目录,例如 system、infra、tool 等等',
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '业务名',
-    field: 'businessName',
-    component: 'Input',
-    labelMessage: '业务名,即二级目录,例如 user、permission、dict 等等',
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '类描述',
-    field: 'classComment',
-    component: 'Input',
-    labelMessage: '用作类描述,例如 用户',
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '作者',
-    field: 'author',
-    component: 'Input',
-    colProps: {
-      span: 12
-    }
-  },
-  {
-    label: '备注',
-    field: 'remark',
-    component: 'Input',
-    componentProps: {
-      type: 'textarea',
-      rows: 4
-    },
-    colProps: {
-      span: 24
-    }
-  }
-])
-const { register, methods, elFormRef } = useForm({
-  schema
+  author: [required]
 })
+
 watch(
-  () => props.basicInfo,
-  (basicInfo) => {
-    if (!basicInfo) return
-    const { setValues } = methods
-    setValues(basicInfo)
+  () => props.table,
+  (table) => {
+    if (!table) return
+    formData.value = table
   },
   {
     deep: true,
     immediate: true
   }
 )
-// ========== 初始化 ==========
-onMounted(async () => {
-  await getTree()
-})
-
+watch(
+  () => formData.value,
+  (val) => {
+    emits('update:basicInfo', val)
+  }
+)
 defineExpose({
-  elFormRef,
-  getFormData: methods.getFormData
+  validate: async () => unref(formRef)?.validate()
 })
 </script>

+ 0 - 137
src/views/infra/codegen/components/CloumInfoForm.vue

@@ -1,137 +0,0 @@
-<template>
-  <vxe-table
-    ref="dragTable"
-    border
-    :data="info"
-    max-height="600"
-    stripe
-    class="xtable-scrollbar"
-    :column-config="{ resizable: true }"
-  >
-    <vxe-column title="字段列名" field="columnName" fixed="left" width="10%" />
-    <vxe-colgroup title="基础属性">
-      <vxe-column title="字段描述" field="columnComment" width="10%">
-        <template #default="{ row }">
-          <vxe-input v-model="row.columnComment" placeholder="请输入字段描述" />
-        </template>
-      </vxe-column>
-      <vxe-column title="物理类型" field="dataType" width="10%" />
-      <vxe-column title="Java类型" width="10%" field="javaType">
-        <template #default="{ row }">
-          <vxe-select v-model="row.javaType" placeholder="请选择Java类型">
-            <vxe-option label="Long" value="Long" />
-            <vxe-option label="String" value="String" />
-            <vxe-option label="Integer" value="Integer" />
-            <vxe-option label="Double" value="Double" />
-            <vxe-option label="BigDecimal" value="BigDecimal" />
-            <vxe-option label="LocalDateTime" value="LocalDateTime" />
-            <vxe-option label="Boolean" value="Boolean" />
-          </vxe-select>
-        </template>
-      </vxe-column>
-      <vxe-column title="java属性" width="8%" field="javaField">
-        <template #default="{ row }">
-          <vxe-input v-model="row.javaField" placeholder="请输入java属性" />
-        </template>
-      </vxe-column>
-    </vxe-colgroup>
-    <vxe-colgroup title="增删改查">
-      <vxe-column title="插入" width="40px" field="createOperation">
-        <template #default="{ row }">
-          <vxe-checkbox true-label="true" false-label="false" v-model="row.createOperation" />
-        </template>
-      </vxe-column>
-      <vxe-column title="编辑" width="40px" field="updateOperation">
-        <template #default="{ row }">
-          <vxe-checkbox true-label="true" false-label="false" v-model="row.updateOperation" />
-        </template>
-      </vxe-column>
-      <vxe-column title="列表" width="40px" field="listOperationResult">
-        <template #default="{ row }">
-          <vxe-checkbox true-label="true" false-label="false" v-model="row.listOperationResult" />
-        </template>
-      </vxe-column>
-      <vxe-column title="查询" width="40px" field="listOperation">
-        <template #default="{ row }">
-          <vxe-checkbox true-label="true" false-label="false" v-model="row.listOperation" />
-        </template>
-      </vxe-column>
-      <vxe-column title="允许空" width="40px" field="nullable">
-        <template #default="{ row }">
-          <vxe-checkbox true-label="true" false-label="false" v-model="row.nullable" />
-        </template>
-      </vxe-column>
-      <vxe-column title="查询方式" width="60px" field="listOperationCondition">
-        <template #default="{ row }">
-          <vxe-select v-model="row.listOperationCondition" placeholder="请选择查询方式">
-            <vxe-option label="=" value="=" />
-            <vxe-option label="!=" value="!=" />
-            <vxe-option label=">" value=">" />
-            <vxe-option label=">=" value=">=" />
-            <vxe-option label="<" value="<>" />
-            <vxe-option label="<=" value="<=" />
-            <vxe-option label="LIKE" value="LIKE" />
-            <vxe-option label="BETWEEN" value="BETWEEN" />
-          </vxe-select>
-        </template>
-      </vxe-column>
-    </vxe-colgroup>
-    <vxe-column title="显示类型" width="10%" field="htmlType">
-      <template #default="{ row }">
-        <vxe-select v-model="row.htmlType" placeholder="请选择显示类型">
-          <vxe-option label="文本框" value="input" />
-          <vxe-option label="文本域" value="textarea" />
-          <vxe-option label="下拉框" value="select" />
-          <vxe-option label="单选框" value="radio" />
-          <vxe-option label="复选框" value="checkbox" />
-          <vxe-option label="日期控件" value="datetime" />
-          <vxe-option label="图片上传" value="imageUpload" />
-          <vxe-option label="文件上传" value="fileUpload" />
-          <vxe-option label="富文本控件" value="editor" />
-        </vxe-select>
-      </template>
-    </vxe-column>
-    <vxe-column title="字典类型" width="10%" field="dictType">
-      <template #default="{ row }">
-        <vxe-select v-model="row.dictType" clearable filterable placeholder="请选择字典类型">
-          <vxe-option
-            v-for="dict in dictOptions"
-            :key="dict.id"
-            :label="dict.name"
-            :value="dict.type"
-          />
-        </vxe-select>
-      </template>
-    </vxe-column>
-    <vxe-column title="示例" field="example">
-      <template #default="{ row }">
-        <vxe-input v-model="row.example" placeholder="请输入示例" />
-      </template>
-    </vxe-column>
-  </vxe-table>
-</template>
-<script setup lang="ts">
-import { PropType } from 'vue'
-import { DictTypeVO } from '@/api/system/dict/types'
-import { CodegenColumnVO } from '@/api/infra/codegen/types'
-import { listSimpleDictType } from '@/api/system/dict/dict.type'
-
-const props = defineProps({
-  info: {
-    type: Array as unknown as PropType<CodegenColumnVO[]>,
-    default: () => null
-  }
-})
-/** 查询字典下拉列表 */
-const dictOptions = ref<DictTypeVO[]>()
-const getDictOptions = async () => {
-  const res = await listSimpleDictType()
-  dictOptions.value = res
-}
-onMounted(async () => {
-  await getDictOptions()
-})
-defineExpose({
-  info: props.info
-})
-</script>

+ 157 - 0
src/views/infra/codegen/components/ColumInfoForm.vue

@@ -0,0 +1,157 @@
+<template>
+  <el-table ref="dragTable" :data="formData" row-key="columnId" :max-height="tableHeight">
+    <el-table-column
+      label="字段列名"
+      prop="columnName"
+      min-width="10%"
+      :show-overflow-tooltip="true"
+    />
+    <el-table-column label="字段描述" min-width="10%">
+      <template #default="scope">
+        <el-input v-model="scope.row.columnComment" />
+      </template>
+    </el-table-column>
+    <el-table-column
+      label="物理类型"
+      prop="dataType"
+      min-width="10%"
+      :show-overflow-tooltip="true"
+    />
+    <el-table-column label="Java类型" min-width="11%">
+      <template #default="scope">
+        <el-select v-model="scope.row.javaType">
+          <el-option label="Long" value="Long" />
+          <el-option label="String" value="String" />
+          <el-option label="Integer" value="Integer" />
+          <el-option label="Double" value="Double" />
+          <el-option label="BigDecimal" value="BigDecimal" />
+          <el-option label="LocalDateTime" value="LocalDateTime" />
+          <el-option label="Boolean" value="Boolean" />
+        </el-select>
+      </template>
+    </el-table-column>
+    <el-table-column label="java属性" min-width="10%">
+      <template #default="scope">
+        <el-input v-model="scope.row.javaField" />
+      </template>
+    </el-table-column>
+    <el-table-column label="插入" min-width="4%">
+      <template #default="scope">
+        <el-checkbox true-label="true" false-label="false" v-model="scope.row.createOperation" />
+      </template>
+    </el-table-column>
+    <el-table-column label="编辑" min-width="4%">
+      <template #default="scope">
+        <el-checkbox true-label="true" false-label="false" v-model="scope.row.updateOperation" />
+      </template>
+    </el-table-column>
+    <el-table-column label="列表" min-width="4%">
+      <template #default="scope">
+        <el-checkbox
+          true-label="true"
+          false-label="false"
+          v-model="scope.row.listOperationResult"
+        />
+      </template>
+    </el-table-column>
+    <el-table-column label="查询" min-width="4%">
+      <template #default="scope">
+        <el-checkbox true-label="true" false-label="false" v-model="scope.row.listOperation" />
+      </template>
+    </el-table-column>
+    <el-table-column label="查询方式" min-width="10%">
+      <template #default="scope">
+        <el-select v-model="scope.row.listOperationCondition">
+          <el-option label="=" value="=" />
+          <el-option label="!=" value="!=" />
+          <el-option label=">" value=">" />
+          <el-option label=">=" value=">=" />
+          <el-option label="<" value="<>" />
+          <el-option label="<=" value="<=" />
+          <el-option label="LIKE" value="LIKE" />
+          <el-option label="BETWEEN" value="BETWEEN" />
+        </el-select>
+      </template>
+    </el-table-column>
+    <el-table-column label="允许空" min-width="5%">
+      <template #default="scope">
+        <el-checkbox true-label="true" false-label="false" v-model="scope.row.nullable" />
+      </template>
+    </el-table-column>
+    <el-table-column label="显示类型" min-width="12%">
+      <template #default="scope">
+        <el-select v-model="scope.row.htmlType">
+          <el-option label="文本框" value="input" />
+          <el-option label="文本域" value="textarea" />
+          <el-option label="下拉框" value="select" />
+          <el-option label="单选框" value="radio" />
+          <el-option label="复选框" value="checkbox" />
+          <el-option label="日期控件" value="datetime" />
+          <el-option label="图片上传" value="imageUpload" />
+          <el-option label="文件上传" value="fileUpload" />
+          <el-option label="富文本控件" value="editor" />
+        </el-select>
+      </template>
+    </el-table-column>
+    <el-table-column label="字典类型" min-width="12%">
+      <template #default="scope">
+        <el-select v-model="scope.row.dictType" clearable filterable placeholder="请选择">
+          <el-option
+            v-for="dict in dictOptions"
+            :key="dict.id"
+            :label="dict.name"
+            :value="dict.type"
+          />
+        </el-select>
+      </template>
+    </el-table-column>
+    <el-table-column label="示例" min-width="10%">
+      <template #default="scope">
+        <el-input v-model="scope.row.example" />
+      </template>
+    </el-table-column>
+  </el-table>
+</template>
+<script setup lang="ts">
+import { PropType } from 'vue'
+import { CodegenColumnVO } from '@/api/infra/codegen/types'
+import { DictTypeVO, listSimpleDictType } from '@/api/system/dict/dict.type'
+
+const emits = defineEmits(['update:columns'])
+const props = defineProps({
+  columns: {
+    type: Array as unknown as PropType<CodegenColumnVO[]>,
+    default: () => null
+  }
+})
+
+const formData = ref<CodegenColumnVO[]>([])
+const tableHeight = document.documentElement.scrollHeight - 350 + 'px'
+
+/** 查询字典下拉列表 */
+const dictOptions = ref<DictTypeVO[]>()
+const getDictOptions = async () => {
+  dictOptions.value = await listSimpleDictType()
+}
+onMounted(async () => {
+  await getDictOptions()
+})
+
+watch(
+  () => props.columns,
+  (columns) => {
+    if (!columns) return
+    formData.value = columns
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+watch(
+  () => formData.value,
+  (val) => {
+    emits('update:columns', val)
+  }
+)
+</script>

+ 379 - 0
src/views/infra/codegen/components/GenerateInfoForm.vue

@@ -0,0 +1,379 @@
+<template>
+  <el-form ref="formRef" :model="formData" :rules="rules" label-width="150px">
+    <el-row>
+      <el-col :span="12">
+        <el-form-item prop="templateType" label="生成模板">
+          <el-select v-model="formData.templateType" @change="tplSelectChange">
+            <el-option
+              v-for="dict in getDictOptions(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE)"
+              :key="parseInt(dict.value)"
+              :label="dict.label"
+              :value="parseInt(dict.value)"
+            />
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item prop="scene" label="生成场景">
+          <el-select v-model="formData.scene">
+            <el-option
+              v-for="dict in getDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE)"
+              :key="parseInt(dict.value)"
+              :label="dict.label"
+              :value="parseInt(dict.value)"
+            />
+          </el-select>
+        </el-form-item>
+      </el-col>
+
+      <!--      <el-col :span="12">-->
+      <!--        <el-form-item prop="packageName">-->
+      <!--          <span slot="label">-->
+      <!--            生成包路径-->
+      <!--            <el-tooltip content="生成在哪个java包下,例如 com.ruoyi.system" placement="top">-->
+      <!--              <i class="el-icon-question"></i>-->
+      <!--            </el-tooltip>-->
+      <!--          </span>-->
+      <!--          <el-input v-model="formData.packageName" />-->
+      <!--        </el-form-item>-->
+      <!--      </el-col>-->
+
+      <el-col :span="12">
+        <el-form-item prop="moduleName">
+          <template #label>
+            <span>
+              模块名
+              <el-tooltip
+                content="模块名,即一级目录,例如 system、infra、tool 等等"
+                placement="top"
+              >
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-input v-model="formData.moduleName" />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="12">
+        <el-form-item prop="businessName">
+          <template #label>
+            <span>
+              业务名
+              <el-tooltip
+                content="业务名,即二级目录,例如 user、permission、dict 等等"
+                placement="top"
+              >
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-input v-model="formData.businessName" />
+        </el-form-item>
+      </el-col>
+
+      <!--      <el-col :span="12">-->
+      <!--        <el-form-item prop="businessPackage">-->
+      <!--          <span slot="label">-->
+      <!--            业务包-->
+      <!--            <el-tooltip content="业务包,自定义二级目录。例如说,我们希望将 dictType 和 dictData 归类成 dict 业务" placement="top">-->
+      <!--              <i class="el-icon-question"></i>-->
+      <!--            </el-tooltip>-->
+      <!--          </span>-->
+      <!--          <el-input v-model="formData.businessPackage" />-->
+      <!--        </el-form-item>-->
+      <!--      </el-col>-->
+
+      <el-col :span="12">
+        <el-form-item prop="className">
+          <template #label>
+            <span>
+              类名称
+              <el-tooltip
+                content="类名称(首字母大写),例如SysUser、SysMenu、SysDictData 等等"
+                placement="top"
+              >
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-input v-model="formData.className" />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="12">
+        <el-form-item prop="classComment">
+          <template #label>
+            <span>
+              类描述
+              <el-tooltip content="用作类描述,例如 用户" placement="top">
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-input v-model="formData.classComment" />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="12">
+        <el-form-item>
+          <template #label>
+            <span>
+              上级菜单
+              <el-tooltip content="分配到指定菜单下,例如 系统管理" placement="top">
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-tree-select
+            v-model="formData.parentMenuId"
+            placeholder="请选择系统菜单"
+            node-key="id"
+            check-strictly
+            :data="menus"
+            :props="menuTreeProps"
+          />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="24" v-if="formData.genType === '1'">
+        <el-form-item prop="genPath">
+          <template #label>
+            <span>
+              自定义路径
+              <el-tooltip
+                content="填写磁盘绝对路径,若不填写,则生成到当前Web项目下"
+                placement="top"
+              >
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-input v-model="formData.genPath">
+            <template #append>
+              <el-dropdown>
+                <el-button type="primary">
+                  最近路径快速选择
+                  <i class="el-icon-arrow-down el-icon--right"></i>
+                </el-button>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item @click="formData.genPath = '/'">
+                      恢复默认的生成基础路径
+                    </el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </template>
+          </el-input>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row v-show="formData.tplCategory === 'tree'">
+      <h4 class="form-header">其他信息</h4>
+      <el-col :span="12">
+        <el-form-item>
+          <template #label>
+            <span>
+              树编码字段
+              <el-tooltip content="树显示的编码字段名, 如:dept_id" placement="top">
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-select v-model="formData.treeCode" placeholder="请选择">
+            <el-option
+              v-for="(column, index) in formData.columns"
+              :key="index"
+              :label="column.columnName + ':' + column.columnComment"
+              :value="column.columnName"
+            />
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item>
+          <template #label>
+            <span>
+              树父编码字段
+              <el-tooltip content="树显示的父编码字段名, 如:parent_Id" placement="top">
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-select v-model="formData.treeParentCode" placeholder="请选择">
+            <el-option
+              v-for="(column, index) in formData.columns"
+              :key="index"
+              :label="column.columnName + ':' + column.columnComment"
+              :value="column.columnName"
+            />
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item>
+          <template #label>
+            <span>
+              树名称字段
+              <el-tooltip content="树节点的显示名称字段名, 如:dept_name" placement="top">
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+
+          <el-select v-model="formData.treeName" placeholder="请选择">
+            <el-option
+              v-for="(column, index) in formData.columns"
+              :key="index"
+              :label="column.columnName + ':' + column.columnComment"
+              :value="column.columnName"
+            />
+          </el-select>
+        </el-form-item>
+      </el-col>
+    </el-row>
+    <el-row v-show="formData.tplCategory === 'sub'">
+      <h4 class="form-header">关联信息</h4>
+      <el-col :span="12">
+        <el-form-item>
+          <template #label>
+            <span>
+              关联子表的表名
+              <el-tooltip content="关联子表的表名, 如:sys_user" placement="top">
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-select v-model="formData.subTableName" placeholder="请选择" @change="subSelectChange">
+            <el-option
+              v-for="(table0, index) in tables"
+              :key="index"
+              :label="table0.tableName + ':' + table0.tableComment"
+              :value="table0.tableName"
+            />
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item>
+          <template #label>
+            <span>
+              子表关联的外键名
+              <el-tooltip content="子表关联的外键名, 如:user_id" placement="top">
+                <Icon icon="ep:question-filled" />
+              </el-tooltip>
+            </span>
+          </template>
+          <el-select v-model="formData.subTableFkName" placeholder="请选择">
+            <el-option
+              v-for="(column, index) in subColumns"
+              :key="index"
+              :label="column.columnName + ':' + column.columnComment"
+              :value="column.columnName"
+            />
+          </el-select>
+        </el-form-item>
+      </el-col>
+    </el-row>
+  </el-form>
+</template>
+<script setup lang="ts">
+import { CodegenTableVO } from '@/api/infra/codegen/types'
+import * as MenuApi from '@/api/system/menu'
+import { PropType } from 'vue'
+import { getDictOptions, DICT_TYPE } from '@/utils/dict'
+import { handleTree } from '@/utils/tree'
+
+const message = useMessage() // 消息弹窗
+const emits = defineEmits(['update:basicInfo'])
+const props = defineProps({
+  table: {
+    type: Object as PropType<Nullable<CodegenTableVO>>,
+    default: () => null
+  }
+})
+
+const formRef = ref()
+const formData = ref({
+  templateType: null,
+  scene: null,
+  moduleName: '',
+  businessName: '',
+  className: '',
+  classComment: '',
+  parentMenuId: null,
+  genPath: '',
+  treeCode: '',
+  treeParentCode: '',
+  treeName: '',
+  tplCategory: '',
+  subTableName: '',
+  subTableFkName: '',
+  genType: ''
+})
+
+const rules = reactive({
+  templateType: [required],
+  scene: [required],
+  moduleName: [required],
+  businessName: [required],
+  businessPackage: [required],
+  className: [required],
+  classComment: [required]
+})
+
+const tables = ref([])
+const subColumns = ref([])
+const menus = ref<any[]>([])
+const menuTreeProps = {
+  label: 'name'
+}
+
+/** 选择子表名触发 */
+const subSelectChange = () => {
+  formData.value.subTableFkName = ''
+}
+/** 选择生成模板触发 */
+const tplSelectChange = (value) => {
+  if (value !== 1) {
+    // TODO 芋艿:暂时不考虑支持树形结构
+    message.error(
+      '暂时不考虑支持【树形】和【主子表】的代码生成。原因是:导致 vm 模板过于复杂,不利于胖友二次开发'
+    )
+    return false
+  }
+  if (value !== 'sub') {
+    formData.value.subTableName = ''
+    formData.value.subTableFkName = ''
+  }
+}
+
+watch(
+  () => props.table,
+  (table) => {
+    if (!table) return
+    formData.value = table as any
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+watch(
+  () => formData.value,
+  (val) => {
+    emits('update:basicInfo', val)
+  }
+)
+onMounted(async () => {
+  try {
+    const resp = await MenuApi.getSimpleMenusList()
+    menus.value = handleTree(resp)
+  } catch {}
+})
+defineExpose({
+  validate: async () => unref(formRef)?.validate()
+})
+</script>

+ 60 - 50
src/views/infra/codegen/components/ImportTable.vue

@@ -1,9 +1,8 @@
 <template>
-  <!-- 导入表 -->
-  <XModal title="导入表" v-model="visible">
-    <el-form :model="queryParams" ref="queryRef" :inline="true">
+  <Dialog :title="modelTitle" v-model="modelVisible">
+    <el-form :model="queryParams" ref="queryFormRef" :inline="true">
       <el-form-item label="数据源" prop="dataSourceConfigId">
-        <el-select v-model="queryParams.dataSourceConfigId" placeholder="请选择数据源" clearable>
+        <el-select v-model="queryParams.dataSourceConfigId" placeholder="请选择数据源">
           <el-option
             v-for="config in dataSourceConfigs"
             :key="config.id"
@@ -13,62 +12,75 @@
         </el-select>
       </el-form-item>
       <el-form-item label="表名称" prop="name">
-        <el-input v-model="queryParams.name" placeholder="请输入表名称" clearable />
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入表名称"
+          clearable
+          @keyup.enter="handleQuery"
+        />
       </el-form-item>
       <el-form-item label="表描述" prop="comment">
-        <el-input v-model="queryParams.comment" placeholder="请输入表描述" clearable />
+        <el-input
+          v-model="queryParams.comment"
+          placeholder="请输入表描述"
+          clearable
+          @keyup.enter="handleQuery"
+        />
       </el-form-item>
       <el-form-item>
-        <XButton
-          type="primary"
-          preIcon="ep:search"
-          :title="t('common.query')"
-          @click="handleQuery()"
-        />
-        <XButton preIcon="ep:refresh-right" :title="t('common.reset')" @click="resetQuery()" />
+        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
       </el-form-item>
     </el-form>
-    <vxe-table
-      ref="xTable"
-      :data="dbTableList"
-      v-loading="dbLoading"
-      :checkbox-config="{ highlight: true, range: true }"
-      height="260px"
-      class="xtable-scrollbar"
-    >
-      <vxe-column type="checkbox" width="60" />
-      <vxe-column field="name" title="表名称" />
-      <vxe-column field="comment" title="表描述" />
-    </vxe-table>
+
+    <el-row>
+      <el-table
+        v-loading="dbLoading"
+        @row-click="clickRow"
+        ref="tableRef"
+        :data="dbTableList"
+        @selection-change="handleSelectionChange"
+        height="260px"
+      >
+        <el-table-column type="selection" width="55" />
+        <el-table-column prop="name" label="表名称" :show-overflow-tooltip="true" />
+        <el-table-column prop="comment" label="表描述" :show-overflow-tooltip="true" />
+      </el-table>
+    </el-row>
+
     <template #footer>
-      <XButton type="primary" :title="t('action.import')" @click="handleImportTable()" />
-      <XButton :title="t('dialog.close')" @click="handleClose()" />
+      <div class="dialog-footer">
+        <el-button @click="handleImportTable" type="primary" :disabled="tables.length === 0">{{
+          t('action.import')
+        }}</el-button>
+        <el-button @click="handleClose">{{ t('dialog.close') }}</el-button>
+      </div>
     </template>
-  </XModal>
+  </Dialog>
 </template>
 <script setup lang="ts">
-import { VxeTableInstance } from 'vxe-table'
 import type { DatabaseTableVO } from '@/api/infra/codegen/types'
-import { getSchemaTableListApi, createCodegenListApi } from '@/api/infra/codegen'
+import * as CodegenApi from '@/api/infra/codegen'
 import { getDataSourceConfigList, DataSourceConfigVO } from '@/api/infra/dataSourceConfig'
+import { ElTable } from 'element-plus'
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
 const emit = defineEmits(['ok'])
-// ======== 显示页面 ========
-const visible = ref(false)
+
+const modelVisible = ref(false) // 弹窗的是否展示
+const modelTitle = ref('导入表') // 弹窗的标题
 const dbLoading = ref(true)
 const queryParams = reactive({
   name: undefined,
   comment: undefined,
-  dataSourceConfigId: 0 as number | undefined
+  dataSourceConfigId: 0
 })
 const dataSourceConfigs = ref<DataSourceConfigVO[]>([])
 const show = async () => {
-  const res = await getDataSourceConfigList()
-  dataSourceConfigs.value = res
+  dataSourceConfigs.value = await getDataSourceConfigList()
   queryParams.dataSourceConfigId = dataSourceConfigs.value[0].id as number
-  visible.value = true
+  modelVisible.value = true
   await getList()
 }
 /** 查询表数据 */
@@ -77,8 +89,7 @@ const dbTableList = ref<DatabaseTableVO[]>([])
 /** 查询表数据 */
 const getList = async () => {
   dbLoading.value = true
-  const res = await getSchemaTableListApi(queryParams)
-  dbTableList.value = res
+  dbTableList.value = await CodegenApi.getSchemaTableList(queryParams)
   dbLoading.value = false
 }
 // 查询操作
@@ -89,23 +100,22 @@ const handleQuery = async () => {
 const resetQuery = async () => {
   queryParams.name = undefined
   queryParams.comment = undefined
-  queryParams.dataSourceConfigId = 0
+  queryParams.dataSourceConfigId = dataSourceConfigs.value[0].id as number
   await getList()
 }
-const xTable = ref<VxeTableInstance>()
+const tableRef = ref<typeof ElTable>()
 /** 多选框选中数据 */
 const tables = ref<string[]>([])
-
+const clickRow = (row) => {
+  unref(tableRef)?.toggleRowSelection(row)
+}
+// 多选框选中数据
+const handleSelectionChange = (selection) => {
+  tables.value = selection.map((item) => item.name)
+}
 /** 导入按钮操作 */
 const handleImportTable = async () => {
-  if (xTable.value?.getCheckboxRecords().length === 0) {
-    message.error('请选择要导入的表')
-    return
-  }
-  xTable.value?.getCheckboxRecords().forEach((item) => {
-    tables.value.push(item.name)
-  })
-  await createCodegenListApi({
+  await CodegenApi.createCodegenList({
     dataSourceConfigId: queryParams.dataSourceConfigId,
     tableNames: tables.value
   })
@@ -114,7 +124,7 @@ const handleImportTable = async () => {
   handleClose()
 }
 const handleClose = () => {
-  visible.value = false
+  modelVisible.value = false
   tables.value = []
 }
 defineExpose({

+ 42 - 23
src/views/infra/codegen/components/Preview.vue

@@ -1,5 +1,11 @@
 <template>
-  <XModal title="预览" v-model="preview.open">
+  <Dialog
+    :title="modelTitle"
+    v-model="modelVisible"
+    align-center
+    width="60%"
+    class="app-infra-codegen-preview-container"
+  >
     <div class="flex">
       <el-card class="w-1/4" :gutter="12" shadow="hover">
         <el-scrollbar height="calc(100vh - 88px - 40px - 50px)">
@@ -10,6 +16,7 @@
             :expand-on-click-node="false"
             highlight-current
             @node-click="handleNodeClick"
+            default-expand-all
           />
         </el-scrollbar>
       </el-card>
@@ -21,38 +28,34 @@
             :name="item.filePath"
             :key="item.filePath"
           >
-            <XTextButton style="float: right" :title="t('common.copy')" @click="copy(item.code)" />
+            <el-button text type="primary" class="float-right" @click="copy(item.code)">
+              {{ t('common.copy') }}
+            </el-button>
             <pre>{{ item.code }}</pre>
           </el-tab-pane>
         </el-tabs>
       </el-card>
     </div>
-  </XModal>
+  </Dialog>
 </template>
 <script setup lang="ts">
 import { useClipboard } from '@vueuse/core'
 import { handleTree2 } from '@/utils/tree'
-import { previewCodegenApi } from '@/api/infra/codegen'
-import { CodegenTableVO, CodegenPreviewVO } from '@/api/infra/codegen/types'
+import * as CodegenApi from '@/api/infra/codegen'
+import { CodegenPreviewVO } from '@/api/infra/codegen/types'
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
+
+const modelVisible = ref(false) // 弹窗的是否展示
+const modelTitle = ref('代码预览') // 弹窗的标题
 // ======== 显示页面 ========
 const preview = reactive({
-  open: false,
-  titel: '代码预览',
   fileTree: [],
   activeName: ''
 })
 const previewCodegen = ref<CodegenPreviewVO[]>()
-const show = async (row: CodegenTableVO) => {
-  const res = await previewCodegenApi(row.id)
-  let file = handleFiles(res)
-  previewCodegen.value = res
-  preview.fileTree = handleTree2(file, 'id', 'parentId', 'children', '/')
-  preview.activeName = res[0].filePath
-  preview.open = true
-}
+
 const handleNodeClick = async (data, node) => {
   if (node && !node.isLeaf) {
     return false
@@ -132,14 +135,30 @@ const copy = async (text: string) => {
   const { copy, copied, isSupported } = useClipboard({ source: text })
   if (!isSupported) {
     message.error(t('common.copyError'))
-  } else {
-    await copy()
-    if (unref(copied)) {
-      message.success(t('common.copySuccess'))
-    }
+    return
+  }
+  await copy()
+  if (unref(copied)) {
+    message.success(t('common.copySuccess'))
   }
 }
-defineExpose({
-  show
-})
+
+/** 打开弹窗 */
+const openModal = async (id: number) => {
+  modelVisible.value = true
+  const res = await CodegenApi.previewCodegen(id)
+  let file = handleFiles(res)
+  previewCodegen.value = res
+  preview.fileTree = handleTree2(file, 'id', 'parentId', 'children', '/')
+  preview.activeName = res[0].filePath
+}
+defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗
 </script>
+<style lang="scss">
+.app-infra-codegen-preview-container {
+  .el-scrollbar .el-scrollbar__wrap .el-scrollbar__view {
+    white-space: nowrap;
+    display: inline-block;
+  }
+}
+</style>

+ 3 - 2
src/views/infra/codegen/components/index.ts

@@ -1,5 +1,6 @@
 import BasicInfoForm from './BasicInfoForm.vue'
-import CloumInfoForm from './CloumInfoForm.vue'
+import ColumInfoForm from './ColumInfoForm.vue'
+import GenerateInfoForm from './GenerateInfoForm.vue'
 import ImportTable from './ImportTable.vue'
 import Preview from './Preview.vue'
-export { BasicInfoForm, CloumInfoForm, ImportTable, Preview }
+export { BasicInfoForm, ColumInfoForm, GenerateInfoForm, ImportTable, Preview }

+ 196 - 65
src/views/infra/codegen/index.vue

@@ -1,56 +1,124 @@
 <template>
-  <ContentWrap>
-    <!-- 列表 -->
-    <XTable @register="registerTable">
-      <template #toolbar_buttons>
-        <!-- 操作:导入 -->
-        <XButton
-          type="primary"
-          preIcon="ep:zoom-in"
-          :title="t('action.import')"
-          v-hasPermi="['infra:codegen:create']"
-          @click="openImportTable()"
+  <!-- 搜索 -->
+  <content-wrap>
+    <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
+      <el-form-item label="表名称" prop="tableName">
+        <el-input
+          v-model="queryParams.tableName"
+          placeholder="请输入表名称"
+          clearable
+          @keyup.enter="handleQuery"
         />
-      </template>
-      <template #actionbtns_default="{ row }">
-        <!-- 操作:预览 -->
-        <XTextButton
-          preIcon="ep:view"
-          :title="t('action.preview')"
-          v-hasPermi="['infra:codegen:query']"
-          @click="handlePreview(row)"
+      </el-form-item>
+      <el-form-item label="表描述" prop="tableComment">
+        <el-input
+          v-model="queryParams.tableComment"
+          placeholder="请输入表描述"
+          clearable
+          @keyup.enter="handleQuery"
         />
-        <!-- 操作:编辑 -->
-        <XTextButton
-          preIcon="ep:edit"
-          :title="t('action.edit')"
-          v-hasPermi="['infra:codegen:update']"
-          @click="handleUpdate(row.id)"
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="queryParams.createTime"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          type="daterange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
         />
-        <!-- 操作:删除 -->
-        <XTextButton
-          preIcon="ep:delete"
-          :title="t('action.del')"
-          v-hasPermi="['infra:codegen:delete']"
-          @click="deleteData(row.id)"
-        />
-        <!-- 操作:同步 -->
-        <XTextButton
-          preIcon="ep:refresh"
-          :title="t('action.sync')"
-          v-hasPermi="['infra:codegen:update']"
-          @click="handleSynchDb(row)"
-        />
-        <!-- 操作:生成 -->
-        <XTextButton
-          preIcon="ep:download"
-          :title="t('action.generate')"
-          v-hasPermi="['infra:codegen:download']"
-          @click="handleGenTable(row)"
-        />
-      </template>
-    </XTable>
-  </ContentWrap>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" />搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
+        <el-button type="primary" v-hasPermi="['infra:codegen:create']" @click="openImportTable()">
+          <Icon icon="ep:zoom-in" class="mr-5px" />{{ t('action.import') }}
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </content-wrap>
+
+  <!-- 列表 -->
+  <content-wrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="数据源" align="center" :formatter="dataSourceConfigNameFormat" />
+      <el-table-column label="表名称" align="center" prop="tableName" width="200" />
+      <el-table-column
+        label="表描述"
+        align="center"
+        prop="tableComment"
+        :show-overflow-tooltip="true"
+        width="120"
+      />
+      <el-table-column label="实体" align="center" prop="className" width="200" />
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        width="180"
+        :formatter="dateFormatter"
+      />
+      <el-table-column
+        label="更新时间"
+        align="center"
+        prop="createTime"
+        width="180"
+        :formatter="dateFormatter"
+      />
+      <el-table-column
+        label="操作"
+        align="center"
+        width="300px"
+        class-name="small-padding fixed-width"
+      >
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="handlePreview(scope.row)"
+            v-hasPermi="['infra:codegen:preview']"
+            >预览</el-button
+          >
+          <el-button
+            link
+            type="primary"
+            @click="handleUpdate(scope.row.id)"
+            v-hasPermi="['infra:codegen:update']"
+            >编辑</el-button
+          >
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['infra:codegen:delete']"
+            >删除</el-button
+          >
+          <el-button
+            link
+            type="primary"
+            @click="handleSynchDb(scope.row)"
+            v-hasPermi="['infra:codegen:update']"
+            >同步</el-button
+          >
+          <el-button
+            link
+            type="primary"
+            @click="handleGenTable(scope.row)"
+            v-hasPermi="['infra:codegen:download']"
+            >生成代码</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </content-wrap>
+
   <!-- 弹窗:导入表 -->
   <ImportTable ref="importRef" @ok="reload()" />
   <!-- 弹窗:预览代码 -->
@@ -59,19 +127,70 @@
 <script setup lang="ts" name="Codegen">
 import download from '@/utils/download'
 import * as CodegenApi from '@/api/infra/codegen'
+import * as DataSourceConfigApi from '@/api/infra/dataSourceConfig'
 import { CodegenTableVO } from '@/api/infra/codegen/types'
-import { allSchemas } from './codegen.data'
 import { ImportTable, Preview } from './components'
+import ContentWrap from '@/components/ContentWrap/src/ContentWrap.vue'
+import { DataSourceConfigVO } from '@/api/infra/dataSourceConfig'
+import { dateFormatter } from '@/utils/formatTime'
 
-const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
 const { push } = useRouter() // 路由跳转
-// 列表相关的变量
-const [registerTable, { reload, deleteData }] = useXTable({
-  allSchemas: allSchemas,
-  getListApi: CodegenApi.getCodegenTablePageApi,
-  deleteApi: CodegenApi.deleteCodegenTableApi
+
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  tableName: undefined,
+  tableComment: undefined,
+  createTime: []
 })
+const queryFormRef = ref() // 搜索的表单
+
+const dataSourceConfigs = ref<DataSourceConfigVO[]>([]) // 数据源列表
+
+/** 查询参数列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await CodegenApi.getCodegenTablePage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 初始化 **/
+onMounted(async () => {
+  getList()
+  dataSourceConfigs.value = await DataSourceConfigApi.getDataSourceConfigList()
+})
+
+// 数据源配置的名字
+const dataSourceConfigNameFormat = (row) => {
+  for (const config of dataSourceConfigs.value) {
+    if (row.dataSourceConfigId === config.id) {
+      return config.name
+    }
+  }
+  return '未知【' + row.leaderUserId + '】'
+}
 
 // 导入操作
 const importRef = ref()
@@ -81,27 +200,39 @@ const openImportTable = () => {
 // 预览操作
 const previewRef = ref()
 const handlePreview = (row: CodegenTableVO) => {
-  previewRef.value.show(row)
+  previewRef.value.openModal(row.id)
 }
 // 编辑操作
 const handleUpdate = (rowId: number) => {
   push('/codegen/edit?id=' + rowId)
 }
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await CodegenApi.deleteCodegenTable(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
 // 同步操作
-const handleSynchDb = (row: CodegenTableVO) => {
+const handleSynchDb = async (row: CodegenTableVO) => {
   // 基于 DB 同步
   const tableName = row.tableName
-  message
-    .confirm('确认要强制同步' + tableName + '表结构吗?', t('common.reminder'))
-    .then(async () => {
-      await CodegenApi.syncCodegenFromDBApi(row.id)
-      message.success('同步成功')
-    })
+  try {
+    await message.confirm('确认要强制同步' + tableName + '表结构吗?', t('common.reminder'))
+    await CodegenApi.syncCodegenFromDB(row.id)
+    message.success('同步成功')
+  } catch {}
 }
 
 // 生成代码操作
 const handleGenTable = async (row: CodegenTableVO) => {
-  const res = await CodegenApi.downloadCodegenApi(row.id)
+  const res = await CodegenApi.downloadCodegen(row.id)
   download.zip(res, 'codegen-' + row.className + '.zip')
 }
 </script>