feat: codegen
parent
a7d04683b2
commit
f8d8407fb4
|
@ -37,8 +37,8 @@ export function previewCodegen(id: number) {
|
|||
}
|
||||
|
||||
// 下载生成代码
|
||||
export function downloadCodegen(id: number) {
|
||||
return defHttp.download({ url: '/infra/codegen/download?tableId=' + id }, '生成代码.zip')
|
||||
export function downloadCodegen(data) {
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造树型结构数据
|
||||
* @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">
|
||||
import { BasicModal, useModalInner } from '@/components/Modal'
|
||||
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'
|
||||
|
||||
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>
|
||||
<BasicTable @register="registerTable">
|
||||
<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 #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction
|
||||
: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: '同步', 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.RESET,
|
||||
label: '同步',
|
||||
popConfirm: {
|
||||
title: '确认要强制同步' + record.tableName + '表结构吗?',
|
||||
placement: 'left',
|
||||
confirm: handleSynchDb.bind(null, record)
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: IconEnum.DELETE,
|
||||
color: 'error',
|
||||
|
@ -27,22 +35,25 @@
|
|||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<ImportTableModal @register="registerModal" @success="reload()" />
|
||||
<PreviewModal @register="registerPreviewModal" />
|
||||
<ImportTableModal @register="registerImportTableModal" @success="reload()" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="Codegen">
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
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 { 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'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { createMessage } = useMessage()
|
||||
const [registerModal, { openModal }] = useModal()
|
||||
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal()
|
||||
const [registerImportTableModal, { openModal: openImportTableModal }] = useModal()
|
||||
|
||||
const [registerTable, { reload }] = useTable({
|
||||
title: '代码生成列表',
|
||||
|
@ -63,17 +74,29 @@ const [registerTable, { reload }] = useTable({
|
|||
}
|
||||
})
|
||||
|
||||
function openImportTable() {
|
||||
openModal(true)
|
||||
function handlePreview(record: Recordable) {
|
||||
openPreviewModal(true, {
|
||||
record
|
||||
})
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true
|
||||
openPreviewModal(true, {
|
||||
record
|
||||
})
|
||||
}
|
||||
|
||||
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) {
|
||||
await deleteCodegenTable(record.id)
|
||||
createMessage.success(t('common.delSuccessText'))
|
||||
|
|
Loading…
Reference in New Issue