1.0 version init

This commit is contained in:
hupeng
2023-05-17 16:12:56 +08:00
commit 1d670f209f
594 changed files with 62569 additions and 0 deletions

View File

@ -0,0 +1,81 @@
<template>
<ContentWrap v-loading="formLoading">
<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>
<el-form-item style="float: right">
<el-button :loading="formLoading" type="primary" @click="submitForm">保存</el-button>
<el-button @click="close">返回</el-button>
</el-form-item>
</el-form>
</ContentWrap>
</template>
<script lang="ts" name="InfraCodegenEditTable" setup>
import { useTagsViewStore } from '@/store/modules/tagsView'
import { BasicInfoForm, ColumInfoForm, GenerateInfoForm } from './components'
import * as CodegenApi from '@/api/infra/codegen'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const { push, currentRoute } = useRouter() // 路由
const { query } = useRoute() // 查询参数
const { delView } = useTagsViewStore() // 视图操作
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const activeName = ref('colum') // Tag 激活的窗口
const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>()
const columInfoRef = ref<ComponentRef<typeof ColumInfoForm>>()
const generateInfoRef = ref<ComponentRef<typeof GenerateInfoForm>>()
const formData = ref<CodegenApi.CodegenUpdateReqVO>({
table: {},
columns: []
})
/** 获得详情 */
const getDetail = async () => {
const id = query.id as unknown as number
if (!id) {
return
}
formLoading.value = true
try {
formData.value = await CodegenApi.getCodegenTable(id)
} finally {
formLoading.value = false
}
}
/** 提交按钮 */
const submitForm = async () => {
// 参数校验
if (!unref(formData)) return
await unref(basicInfoRef)?.validate()
await unref(generateInfoRef)?.validate()
try {
// 提交请求
await CodegenApi.updateCodegenTable(formData.value)
message.success(t('common.updateSuccess'))
close()
} catch {}
}
/** 关闭按钮 */
const close = () => {
delView(unref(currentRoute))
push('/infra/codegen')
}
/** 初始化 */
onMounted(() => {
getDetail()
})
</script>

View File

@ -0,0 +1,149 @@
<template>
<Dialog v-model="dialogVisible" title="导入表" width="800px">
<!-- 搜索栏 -->
<el-form ref="queryFormRef" :inline="true" :model="queryParams" label-width="68px">
<el-form-item label="数据源" prop="dataSourceConfigId">
<el-select
v-model="queryParams.dataSourceConfigId"
class="!w-240px"
placeholder="请选择数据源"
>
<el-option
v-for="config in dataSourceConfigList"
:key="config.id"
:label="config.name"
:value="config.id"
/>
</el-select>
</el-form-item>
<el-form-item label="表名称" prop="name">
<el-input
v-model="queryParams.name"
class="!w-240px"
clearable
placeholder="请输入表名称"
@keyup.enter="getList"
/>
</el-form-item>
<el-form-item label="表描述" prop="comment">
<el-input
v-model="queryParams.comment"
class="!w-240px"
clearable
placeholder="请输入表描述"
@keyup.enter="getList"
/>
</el-form-item>
<el-form-item>
<el-button @click="getList">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<el-row>
<el-table
ref="tableRef"
v-loading="dbTableLoading"
:data="dbTableList"
height="260px"
@row-click="handleRowClick"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" label="表名称" prop="name" />
<el-table-column :show-overflow-tooltip="true" label="表描述" prop="comment" />
</el-table>
</el-row>
<!-- 操作 -->
<template #footer>
<el-button :disabled="tableList.length === 0" type="primary" @click="handleImportTable">
导入
</el-button>
<el-button @click="close">关闭</el-button>
</template>
</Dialog>
</template>
<script lang="ts" name="InfraCodegenImportTable" setup>
import * as CodegenApi from '@/api/infra/codegen'
import * as DataSourceConfigApi from '@/api/infra/dataSourceConfig'
import { ElTable } from 'element-plus'
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dbTableLoading = ref(true) // 数据源的加载中
const dbTableList = ref<CodegenApi.DatabaseTableVO[]>([]) // 表的列表
const queryParams = reactive({
name: undefined,
comment: undefined,
dataSourceConfigId: 0
})
const queryFormRef = ref() // 搜索的表单
const dataSourceConfigList = ref<DataSourceConfigApi.DataSourceConfigVO[]>([]) // 数据源列表
/** 查询表数据 */
const getList = async () => {
dbTableLoading.value = true
try {
dbTableList.value = await CodegenApi.getSchemaTableList(queryParams)
} finally {
dbTableLoading.value = false
}
}
/** 重置操作 */
const resetQuery = async () => {
queryParams.name = undefined
queryParams.comment = undefined
queryParams.dataSourceConfigId = dataSourceConfigList.value[0].id as number
await getList()
}
/** 打开弹窗 */
const open = async () => {
// 加载数据源的列表
dataSourceConfigList.value = await DataSourceConfigApi.getDataSourceConfigList()
queryParams.dataSourceConfigId = dataSourceConfigList.value[0].id as number
dialogVisible.value = true
// 加载表的列表
await getList()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 关闭弹窗 */
const close = () => {
dialogVisible.value = false
tableList.value = []
}
const tableRef = ref<typeof ElTable>() // 表格的 Ref
const tableList = ref<string[]>([]) // 选中的表名
/** 处理某一行的点击 */
const handleRowClick = (row) => {
unref(tableRef)?.toggleRowSelection(row)
}
/** 多选框选中数据 */
const handleSelectionChange = (selection) => {
tableList.value = selection.map((item) => item.name)
}
/** 导入按钮操作 */
const handleImportTable = async () => {
await CodegenApi.createCodegenList({
dataSourceConfigId: queryParams.dataSourceConfigId,
tableNames: tableList.value
})
message.success('导入成功')
emit('success')
close()
}
const emit = defineEmits(['success'])
</script>

View File

@ -0,0 +1,220 @@
<template>
<Dialog
v-model="dialogVisible"
align-center
class="app-infra-codegen-preview-container"
title="代码预览"
width="80%"
>
<div class="flex">
<!-- 代码目录树 -->
<el-card
v-loading="loading"
:gutter="12"
class="w-1/3"
element-loading-text="生成文件目录中..."
shadow="hover"
>
<el-scrollbar height="calc(100vh - 88px - 40px)">
<el-tree
ref="treeRef"
:data="preview.fileTree"
:expand-on-click-node="false"
default-expand-all
highlight-current
node-key="id"
@node-click="handleNodeClick"
/>
</el-scrollbar>
</el-card>
<!-- 代码 -->
<el-card
v-loading="loading"
:gutter="12"
class="w-2/3 ml-3"
element-loading-text="加载代码中..."
shadow="hover"
>
<el-tabs v-model="preview.activeName">
<el-tab-pane
v-for="item in previewCodegen"
:key="item.filePath"
:label="item.filePath.substring(item.filePath.lastIndexOf('/') + 1)"
:name="item.filePath"
>
<el-button class="float-right" text type="primary" @click="copy(item.code)">
{{ t('common.copy') }}
</el-button>
<div>
<pre><code class="hljs" v-html="highlightedCode(item)"></code></pre>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</Dialog>
</template>
<script lang="ts" name="InfraCodegenPreviewCode" setup>
import { useClipboard } from '@vueuse/core'
import { handleTree2 } from '@/utils/tree'
import * as CodegenApi from '@/api/infra/codegen'
import hljs from 'highlight.js' // 导入代码高亮文件
import 'highlight.js/styles/github.css' // 导入代码高亮样式
import java from 'highlight.js/lib/languages/java'
import xml from 'highlight.js/lib/languages/java'
import javascript from 'highlight.js/lib/languages/javascript'
import sql from 'highlight.js/lib/languages/sql'
import typescript from 'highlight.js/lib/languages/typescript'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const loading = ref(false) // 加载中的状态
const preview = reactive({
fileTree: [], // 文件树
activeName: '' // 激活的文件名
})
const previewCodegen = ref<CodegenApi.CodegenPreviewVO[]>()
/** 点击文件 */
const handleNodeClick = async (data, node) => {
if (node && !node.isLeaf) {
return false
}
preview.activeName = data.id
}
/** 生成 files 目录 **/
interface filesType {
id: string
label: string
parentId: string
}
/** 打开弹窗 */
const open = async (id: number) => {
dialogVisible.value = true
try {
loading.value = true
// 生成代码
const data = await CodegenApi.previewCodegen(id)
previewCodegen.value = data
// 处理文件
let file = handleFiles(data)
preview.fileTree = handleTree2(file, 'id', 'parentId', 'children', '/')
// 点击首个文件
preview.activeName = data[0].filePath
} finally {
loading.value = false
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 处理文件 */
const handleFiles = (datas: CodegenApi.CodegenPreviewVO[]) => {
let exists = {} // keyfile 的 idvaluetrue
let files: filesType[] = []
// 遍历每个元素
for (const data of datas) {
let paths = data.filePath.split('/')
let fullPath = '' // 从头开始的路径,用于生成 id
// 特殊处理 java 文件
if (paths[paths.length - 1].indexOf('.java') >= 0) {
let newPaths: string[] = []
for (let i = 0; i < paths.length; i++) {
let path = paths[i]
if (path !== 'java') {
newPaths.push(path)
continue
}
newPaths.push(path)
// 特殊处理中间的 package进行合并
let tmp = ''
while (i < paths.length) {
path = paths[i + 1]
if (
path === 'controller' ||
path === 'convert' ||
path === 'dal' ||
path === 'enums' ||
path === 'service' ||
path === 'vo' || // 下面三个,主要是兜底。可能考虑到有人改了包结构
path === 'mysql' ||
path === 'dataobject'
) {
break
}
tmp = tmp ? tmp + '.' + path : path
i++
}
if (tmp) {
newPaths.push(tmp)
}
}
paths = newPaths
}
// 遍历每个 path 拼接成树
for (let i = 0; i < paths.length; i++) {
// 已经添加到 files 中,则跳过
let oldFullPath = fullPath
// 下面的 replaceAll 的原因,是因为上面包处理了,导致和 tabs 不匹配,所以 replaceAll 下
fullPath = fullPath.length === 0 ? paths[i] : fullPath.replaceAll('.', '/') + '/' + paths[i]
if (exists[fullPath]) {
continue
}
// 添加到 files 中
exists[fullPath] = true
files.push({
id: fullPath,
label: paths[i],
parentId: oldFullPath || '/' // "/" 为根节点
})
}
}
return files
}
/** 复制 **/
const copy = async (text: string) => {
const { copy, copied, isSupported } = useClipboard({ source: text })
if (!isSupported) {
message.error(t('common.copyError'))
return
}
await copy()
if (unref(copied)) {
message.success(t('common.copySuccess'))
}
}
/**
* 代码高亮
*/
const highlightedCode = (item) => {
const language = item.filePath.substring(item.filePath.lastIndexOf('.') + 1)
const result = hljs.highlight(language, item.code || '', true)
return result.value || '&nbsp;'
}
/** 初始化 **/
onMounted(async () => {
// 注册代码高亮的各种语言
hljs.registerLanguage('java', java)
hljs.registerLanguage('xml', xml)
hljs.registerLanguage('html', xml)
hljs.registerLanguage('vue', xml)
hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('sql', sql)
hljs.registerLanguage('typescript', typescript)
})
</script>
<style lang="scss">
.app-infra-codegen-preview-container {
.el-scrollbar .el-scrollbar__wrap .el-scrollbar__view {
white-space: nowrap;
display: inline-block;
}
}
</style>

View File

@ -0,0 +1,85 @@
<template>
<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 v-model="formData.tableName" placeholder="请输入仓库名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="表描述" prop="tableComment">
<el-input v-model="formData.tableComment" placeholder="请输入" />
</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 class="" icon="ep:question-filled" />
</el-tooltip>
</span>
</template>
<el-input v-model="formData.className" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="作者" prop="author">
<el-input v-model="formData.author" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" :rows="3" type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script lang="ts" name="InfraCodegenBasicInfoForm" setup>
import * as CodegenApi from '@/api/infra/codegen'
import { PropType } from 'vue'
const props = defineProps({
table: {
type: Object as PropType<Nullable<CodegenApi.CodegenTableVO>>,
default: () => null
}
})
const formRef = ref()
const formData = ref({
tableName: '',
tableComment: '',
className: '',
author: '',
remark: ''
})
const rules = reactive({
tableName: [required],
tableComment: [required],
className: [required],
author: [required]
})
/** 监听 table 属性,复制给 formData 属性 */
watch(
() => props.table,
(table) => {
if (!table) return
formData.value = table
},
{
deep: true,
immediate: true
}
)
defineExpose({
validate: async () => unref(formRef)?.validate()
})
</script>

View File

@ -0,0 +1,151 @@
<template>
<el-table ref="dragTable" :data="formData" :max-height="tableHeight" row-key="columnId">
<el-table-column
:show-overflow-tooltip="true"
label="字段列名"
min-width="10%"
prop="columnName"
/>
<el-table-column label="字段描述" min-width="10%">
<template #default="scope">
<el-input v-model="scope.row.columnComment" />
</template>
</el-table-column>
<el-table-column
:show-overflow-tooltip="true"
label="物理类型"
min-width="10%"
prop="dataType"
/>
<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 v-model="scope.row.createOperation" false-label="false" true-label="true" />
</template>
</el-table-column>
<el-table-column label="编辑" min-width="4%">
<template #default="scope">
<el-checkbox v-model="scope.row.updateOperation" false-label="false" true-label="true" />
</template>
</el-table-column>
<el-table-column label="列表" min-width="4%">
<template #default="scope">
<el-checkbox
v-model="scope.row.listOperationResult"
false-label="false"
true-label="true"
/>
</template>
</el-table-column>
<el-table-column label="查询" min-width="4%">
<template #default="scope">
<el-checkbox v-model="scope.row.listOperation" false-label="false" true-label="true" />
</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 v-model="scope.row.nullable" false-label="false" true-label="true" />
</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 lang="ts" name="InfraCodegenColumInfoForm" setup>
import { PropType } from 'vue'
import * as CodegenApi from '@/api/infra/codegen'
import * as DictDataApi from '@/api/system/dict/dict.type'
const props = defineProps({
columns: {
type: Array as unknown as PropType<CodegenApi.CodegenColumnVO[]>,
default: () => null
}
})
const formData = ref<CodegenApi.CodegenColumnVO[]>([])
const tableHeight = document.documentElement.scrollHeight - 350 + 'px'
/** 查询字典下拉列表 */
const dictOptions = ref<DictDataApi.DictTypeVO[]>()
const getDictOptions = async () => {
dictOptions.value = await DictDataApi.getSimpleDictTypeList()
}
watch(
() => props.columns,
(columns) => {
if (!columns) return
formData.value = columns
},
{
deep: true,
immediate: true
}
)
onMounted(async () => {
await getDictOptions()
})
</script>

View File

@ -0,0 +1,389 @@
<template>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="150px">
<el-row>
<el-col :span="12">
<el-form-item label="生成模板" prop="templateType">
<el-select v-model="formData.templateType" @change="tplSelectChange">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="前端类型" prop="frontType">
<el-select v-model="formData.frontType">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_CODEGEN_FRONT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="生成场景" prop="scene">
<el-select v-model="formData.scene">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</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"
:data="menus"
:props="menuTreeProps"
check-strictly
node-key="id"
placeholder="请选择系统菜单"
/>
</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 v-if="formData.genType === '1'" :span="24">
<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 lang="ts" name="InfraCodegenGenerateInfoForm" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { handleTree } from '@/utils/tree'
import * as CodegenApi from '@/api/infra/codegen'
import * as MenuApi from '@/api/system/menu'
import { PropType } from 'vue'
const message = useMessage() // 消息弹窗
const props = defineProps({
table: {
type: Object as PropType<Nullable<CodegenApi.CodegenTableVO>>,
default: () => null
}
})
const formRef = ref()
const formData = ref({
templateType: null,
frontType: null,
scene: null,
moduleName: '',
businessName: '',
className: '',
classComment: '',
parentMenuId: null,
genPath: '',
treeCode: '',
treeParentCode: '',
treeName: '',
tplCategory: '',
subTableName: '',
subTableFkName: '',
genType: ''
})
const rules = reactive({
templateType: [required],
frontType: [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
}
)
onMounted(async () => {
try {
const resp = await MenuApi.getSimpleMenusList()
menus.value = handleTree(resp)
} catch {}
})
defineExpose({
validate: async () => unref(formRef)?.validate()
})
</script>

View File

@ -0,0 +1,4 @@
import BasicInfoForm from './BasicInfoForm.vue'
import ColumInfoForm from './ColumInfoForm.vue'
import GenerateInfoForm from './GenerateInfoForm.vue'
export { BasicInfoForm, ColumInfoForm, GenerateInfoForm }

View File

@ -0,0 +1,254 @@
<template>
<doc-alert title="代码生成" url="https://www.yixiang.co/new-feature/" />
<doc-alert title="单元测试" url="https://www.yixiang.co/unit-test/" />
<!-- 搜索 -->
<ContentWrap>
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px"
>
<el-form-item label="表名称" prop="tableName">
<el-input
v-model="queryParams.tableName"
class="!w-240px"
clearable
placeholder="请输入表名称"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="表描述" prop="tableComment">
<el-input
v-model="queryParams.tableComment"
class="!w-240px"
clearable
placeholder="请输入表描述"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
end-placeholder="结束日期"
start-placeholder="开始日期"
type="daterange"
value-format="YYYY-MM-dd HH:mm:ss"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button v-hasPermi="['infra:codegen:create']" type="primary" @click="openImportTable()">
<Icon class="mr-5px" icon="ep:zoom-in" />
导入
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column align="center" label="数据源">
<template #default="scope">
{{
dataSourceConfigList.find((config) => config.id === scope.row.dataSourceConfigId)?.name
}}
</template>
</el-table-column>
<el-table-column align="center" label="表名称" prop="tableName" width="200" />
<el-table-column
:show-overflow-tooltip="true"
align="center"
label="表描述"
prop="tableComment"
width="200"
/>
<el-table-column align="center" label="实体" prop="className" width="200" />
<el-table-column
:formatter="dateFormatter"
align="center"
label="创建时间"
prop="createTime"
width="180"
/>
<el-table-column
:formatter="dateFormatter"
align="center"
label="更新时间"
prop="createTime"
width="180"
/>
<el-table-column align="center" fixed="right" label="操作" width="300px">
<template #default="scope">
<el-button
v-hasPermi="['infra:codegen:preview']"
link
type="primary"
@click="handlePreview(scope.row)"
>
预览
</el-button>
<el-button
v-hasPermi="['infra:codegen:update']"
link
type="primary"
@click="handleUpdate(scope.row.id)"
>
编辑
</el-button>
<el-button
v-hasPermi="['infra:codegen:delete']"
link
type="danger"
@click="handleDelete(scope.row.id)"
>
删除
</el-button>
<el-button
v-hasPermi="['infra:codegen:update']"
link
type="primary"
@click="handleSyncDB(scope.row)"
>
同步
</el-button>
<el-button
v-hasPermi="['infra:codegen:download']"
link
type="primary"
@click="handleGenTable(scope.row)"
>
生成代码
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
</ContentWrap>
<!-- 弹窗导入表 -->
<ImportTable ref="importRef" @success="getList" />
<!-- 弹窗预览代码 -->
<PreviewCode ref="previewRef" />
</template>
<script lang="ts" name="InfraCodegen" setup>
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import * as CodegenApi from '@/api/infra/codegen'
import * as DataSourceConfigApi from '@/api/infra/dataSourceConfig'
import ImportTable from './ImportTable.vue'
import PreviewCode from './PreviewCode.vue'
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const { push } = useRouter() // 路由跳转
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 dataSourceConfigList = ref<DataSourceConfigApi.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()
}
/** 导入操作 */
const importRef = ref()
const openImportTable = () => {
importRef.value.open()
}
/** 编辑操作 */
const handleUpdate = (id: number) => {
push('/codegen/edit?id=' + id)
}
/** 预览操作 */
const previewRef = ref()
const handlePreview = (row: CodegenApi.CodegenTableVO) => {
previewRef.value.open(row.id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await CodegenApi.deleteCodegenTable(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 同步操作 */
const handleSyncDB = async (row: CodegenApi.CodegenTableVO) => {
// 基于 DB 同步
const tableName = row.tableName
try {
await message.confirm('确认要强制同步' + tableName + '表结构吗?', t('common.reminder'))
await CodegenApi.syncCodegenFromDB(row.id)
message.success('同步成功')
} catch {}
}
/** 生成代码操作 */
const handleGenTable = async (row: CodegenApi.CodegenTableVO) => {
const res = await CodegenApi.downloadCodegen(row.id)
download.zip(res, 'codegen-' + row.className + '.zip')
}
/** 初始化 **/
onMounted(async () => {
await getList()
// 加载数据源列表
dataSourceConfigList.value = await DataSourceConfigApi.getDataSourceConfigList()
})
</script>