feat: codegen
parent
a7d04683b2
commit
f8d8407fb4
|
@ -37,8 +37,8 @@ export function previewCodegen(id: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下载生成代码
|
// 下载生成代码
|
||||||
export function downloadCodegen(id: number) {
|
export function downloadCodegen(data) {
|
||||||
return defHttp.download({ url: '/infra/codegen/download?tableId=' + id }, '生成代码.zip')
|
return defHttp.download({ url: '/infra/codegen/download?tableId=' + data.id }, data.tableName + '.zip')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得表定义
|
// 获得表定义
|
||||||
|
|
|
@ -239,3 +239,38 @@ export const handleTree = (data: any[], id?: string, parentId?: string, children
|
||||||
}
|
}
|
||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造树型结构数据
|
||||||
|
* @param {*} data 数据源
|
||||||
|
* @param {*} id id字段 默认 'id'
|
||||||
|
* @param {*} parentId 父节点字段 默认 'parentId'
|
||||||
|
* @param {*} children 孩子节点字段 默认 'children'
|
||||||
|
* @param {*} rootId 根Id 默认 0
|
||||||
|
*/
|
||||||
|
export const handleTree2 = (data, id, parentId, children, rootId) => {
|
||||||
|
id = id || 'id'
|
||||||
|
parentId = parentId || 'parentId'
|
||||||
|
children = children || 'children'
|
||||||
|
rootId =
|
||||||
|
rootId ||
|
||||||
|
Math.min(
|
||||||
|
...data.map((item) => {
|
||||||
|
return item[parentId]
|
||||||
|
})
|
||||||
|
) ||
|
||||||
|
0
|
||||||
|
//对源数据深度克隆
|
||||||
|
const cloneData = JSON.parse(JSON.stringify(data))
|
||||||
|
//循环所有项
|
||||||
|
const treeData = cloneData.filter((father) => {
|
||||||
|
const branchArr = cloneData.filter((child) => {
|
||||||
|
//返回每一项的子级数组
|
||||||
|
return father[id] === child[parentId]
|
||||||
|
})
|
||||||
|
branchArr.length > 0 ? (father.children = branchArr) : ''
|
||||||
|
//返回第一层
|
||||||
|
return father[parentId] === rootId
|
||||||
|
})
|
||||||
|
return treeData !== '' ? treeData : data
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<template><sapn>123</sapn></template>
|
|
@ -1,54 +0,0 @@
|
||||||
<template>
|
|
||||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
|
|
||||||
<BasicForm @register="registerForm" />
|
|
||||||
</BasicModal>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup name="ImportTable">
|
|
||||||
import { ref, computed, unref } from 'vue'
|
|
||||||
import { BasicModal, useModalInner } from '@/components/Modal'
|
|
||||||
import { BasicForm, useForm } from '@/components/Form'
|
|
||||||
import { formSchema } from './codegen.data'
|
|
||||||
import { createPost, getPost, updatePost } from '@/api/system/post'
|
|
||||||
|
|
||||||
const emit = defineEmits(['success', 'register'])
|
|
||||||
const isUpdate = ref(true)
|
|
||||||
const rowId = ref()
|
|
||||||
|
|
||||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
|
|
||||||
labelWidth: 100,
|
|
||||||
baseColProps: { span: 24 },
|
|
||||||
schemas: formSchema,
|
|
||||||
showActionButtonGroup: false,
|
|
||||||
actionColOptions: { span: 23 }
|
|
||||||
})
|
|
||||||
|
|
||||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
|
||||||
resetFields()
|
|
||||||
setModalProps({ confirmLoading: false })
|
|
||||||
isUpdate.value = !!data?.isUpdate
|
|
||||||
|
|
||||||
if (unref(isUpdate)) {
|
|
||||||
const res = await getPost(data.record.id)
|
|
||||||
rowId.value = res.id
|
|
||||||
setFieldsValue({ ...res })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增岗位' : '编辑岗位'))
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
try {
|
|
||||||
const values = await validate()
|
|
||||||
setModalProps({ confirmLoading: true })
|
|
||||||
if (unref(isUpdate)) {
|
|
||||||
await updatePost(values)
|
|
||||||
} else {
|
|
||||||
await createPost(values)
|
|
||||||
}
|
|
||||||
closeModal()
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
setModalProps({ confirmLoading: false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -6,7 +6,7 @@
|
||||||
<script lang="ts" setup name="ImportTableModal">
|
<script lang="ts" setup name="ImportTableModal">
|
||||||
import { BasicModal, useModalInner } from '@/components/Modal'
|
import { BasicModal, useModalInner } from '@/components/Modal'
|
||||||
import { BasicTable, useTable } from '@/components/Table'
|
import { BasicTable, useTable } from '@/components/Table'
|
||||||
import { importTableColumns, importTableSearchFormSchema } from './codegen.data'
|
import { importTableColumns, importTableSearchFormSchema } from '../codegen.data'
|
||||||
import { createCodegenList, getSchemaTableList } from '@/api/infra/codegen'
|
import { createCodegenList, getSchemaTableList } from '@/api/infra/codegen'
|
||||||
|
|
||||||
const emit = defineEmits(['success', 'register'])
|
const emit = defineEmits(['success', 'register'])
|
|
@ -0,0 +1,141 @@
|
||||||
|
<template>
|
||||||
|
<BasicModal v-bind="$attrs" :width="1000" @register="registerModal" title="预览代码">
|
||||||
|
<div class="flex">
|
||||||
|
<Card class="w-1/3 w-full">
|
||||||
|
<BasicTree
|
||||||
|
title="文件夹列表"
|
||||||
|
toolbar
|
||||||
|
treeWrapperClassName="h-[calc(100%-35px)] overflow-auto"
|
||||||
|
:clickRowToExpand="false"
|
||||||
|
:treeData="fileTree"
|
||||||
|
:fieldNames="{ key: 'id', title: 'label' }"
|
||||||
|
@select="handleSelect"
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
<Card class="w-2/3 w-full">
|
||||||
|
<Tabs v-model:activeKey="activeKey">
|
||||||
|
<TabPane v-for="item in previewCodes" :key="item.filePath" :tab="item.filePath.substring(item.filePath.lastIndexOf('/') + 1)">
|
||||||
|
<a-button type="link" style="float: right" @click="copy(item.code)">复制</a-button>
|
||||||
|
<pre>{{ item.code }}</pre>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup name="PreviewModal">
|
||||||
|
import { ref, unref } from 'vue'
|
||||||
|
import { Card, Tabs } from 'ant-design-vue'
|
||||||
|
import { BasicTree } from '@/components/Tree'
|
||||||
|
import { BasicModal, useModalInner } from '@/components/Modal'
|
||||||
|
import { previewCodegen } from '@/api/infra/codegen'
|
||||||
|
import { CodegenPreviewVO } from '@/api/infra/codegen/types'
|
||||||
|
import { handleTree2 } from '@/utils/tree'
|
||||||
|
import { useClipboard } from '@vueuse/core'
|
||||||
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
|
||||||
|
const TabPane = Tabs.TabPane
|
||||||
|
|
||||||
|
const { createMessage } = useMessage()
|
||||||
|
|
||||||
|
const fileTree = ref([])
|
||||||
|
const activeKey = ref('')
|
||||||
|
const previewCodes = ref<CodegenPreviewVO[]>()
|
||||||
|
|
||||||
|
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
|
||||||
|
setModalProps({ confirmLoading: false })
|
||||||
|
|
||||||
|
const res = await previewCodegen(data.record.id)
|
||||||
|
let file = handleFiles(res)
|
||||||
|
previewCodes.value = res
|
||||||
|
activeKey.value = res[0].filePath
|
||||||
|
fileTree.value = handleTree2(file, 'id', 'parentId', 'children', '/')
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSelect(keys) {
|
||||||
|
activeKey.value = keys[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 生成 files 目录 **/
|
||||||
|
interface filesType {
|
||||||
|
id: string
|
||||||
|
label: string
|
||||||
|
parentId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFiles(datas) {
|
||||||
|
let exists = {} // key:file 的 id;value:true
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 复制 **/
|
||||||
|
async function copy(text: string) {
|
||||||
|
const { copy, copied, isSupported } = useClipboard({ source: text })
|
||||||
|
if (!isSupported) {
|
||||||
|
createMessage.error('复制失败')
|
||||||
|
} else {
|
||||||
|
await copy()
|
||||||
|
if (unref(copied)) {
|
||||||
|
createMessage.success('复制成功')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -2,16 +2,24 @@
|
||||||
<div>
|
<div>
|
||||||
<BasicTable @register="registerTable">
|
<BasicTable @register="registerTable">
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<a-button type="primary" :preIcon="IconEnum.IMPORT" @click="openImportTable"> {{ t('action.import') }} </a-button>
|
<a-button type="primary" :preIcon="IconEnum.IMPORT" @click="openImportTableModal(true)"> {{ t('action.import') }} </a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'action'">
|
<template v-if="column.key === 'action'">
|
||||||
<TableAction
|
<TableAction
|
||||||
:actions="[
|
:actions="[
|
||||||
{ icon: IconEnum.EDIT, label: t('action.view'), onClick: handleEdit.bind(null, record) },
|
{ icon: IconEnum.EDIT, label: '预览', onClick: handlePreview.bind(null, record) },
|
||||||
{ icon: IconEnum.EDIT, label: t('action.edit'), onClick: handleEdit.bind(null, record) },
|
{ icon: IconEnum.EDIT, label: t('action.edit'), onClick: handleEdit.bind(null, record) },
|
||||||
{ icon: IconEnum.EDIT, label: '同步', onClick: handleEdit.bind(null, record) },
|
{ icon: IconEnum.DOWNLOAD, label: '生成', onClick: handleGenTable.bind(null, record) },
|
||||||
{ icon: IconEnum.EDIT, label: '生成代码', onClick: handleEdit.bind(null, record) },
|
{
|
||||||
|
icon: IconEnum.RESET,
|
||||||
|
label: '同步',
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认要强制同步' + record.tableName + '表结构吗?',
|
||||||
|
placement: 'left',
|
||||||
|
confirm: handleSynchDb.bind(null, record)
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: IconEnum.DELETE,
|
icon: IconEnum.DELETE,
|
||||||
color: 'error',
|
color: 'error',
|
||||||
|
@ -27,22 +35,25 @@
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</BasicTable>
|
</BasicTable>
|
||||||
<ImportTableModal @register="registerModal" @success="reload()" />
|
<PreviewModal @register="registerPreviewModal" />
|
||||||
|
<ImportTableModal @register="registerImportTableModal" @success="reload()" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup name="Codegen">
|
<script lang="ts" setup name="Codegen">
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { useMessage } from '@/hooks/web/useMessage'
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
import { useModal } from '@/components/Modal'
|
import { useModal } from '@/components/Modal'
|
||||||
import ImportTableModal from './ImportTableModal.vue'
|
import PreviewModal from './components/PreviewModal.vue'
|
||||||
|
import ImportTableModal from './components/ImportTableModal.vue'
|
||||||
import { IconEnum } from '@/enums/appEnum'
|
import { IconEnum } from '@/enums/appEnum'
|
||||||
import { BasicTable, useTable, TableAction } from '@/components/Table'
|
import { BasicTable, useTable, TableAction } from '@/components/Table'
|
||||||
import { deleteCodegenTable, getCodegenTablePage } from '@/api/infra/codegen'
|
import { deleteCodegenTable, downloadCodegen, getCodegenTablePage, syncCodegenFromDB } from '@/api/infra/codegen'
|
||||||
import { columns, searchFormSchema } from './codegen.data'
|
import { columns, searchFormSchema } from './codegen.data'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { createMessage } = useMessage()
|
const { createMessage } = useMessage()
|
||||||
const [registerModal, { openModal }] = useModal()
|
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal()
|
||||||
|
const [registerImportTableModal, { openModal: openImportTableModal }] = useModal()
|
||||||
|
|
||||||
const [registerTable, { reload }] = useTable({
|
const [registerTable, { reload }] = useTable({
|
||||||
title: '代码生成列表',
|
title: '代码生成列表',
|
||||||
|
@ -63,17 +74,29 @@ const [registerTable, { reload }] = useTable({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function openImportTable() {
|
function handlePreview(record: Recordable) {
|
||||||
openModal(true)
|
openPreviewModal(true, {
|
||||||
|
record
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEdit(record: Recordable) {
|
function handleEdit(record: Recordable) {
|
||||||
openModal(true, {
|
openPreviewModal(true, {
|
||||||
record,
|
record
|
||||||
isUpdate: true
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleGenTable(record: Recordable) {
|
||||||
|
await downloadCodegen(record)
|
||||||
|
createMessage.success(t('common.successText'))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSynchDb(record: Recordable) {
|
||||||
|
await syncCodegenFromDB(record.id)
|
||||||
|
createMessage.success(t('common.successText'))
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
|
||||||
async function handleDelete(record: Recordable) {
|
async function handleDelete(record: Recordable) {
|
||||||
await deleteCodegenTable(record.id)
|
await deleteCodegenTable(record.id)
|
||||||
createMessage.success(t('common.delSuccessText'))
|
createMessage.success(t('common.delSuccessText'))
|
||||||
|
|
Loading…
Reference in New Issue