Browse Source

feat: codegen

main
xingyu 2 years ago
parent
commit
f8d8407fb4
  1. 4
      src/api/infra/codegen/index.ts
  2. 35
      src/utils/tree.ts
  3. 1
      src/views/infra/codegen/EditTable.vue
  4. 54
      src/views/infra/codegen/PreviewModal.vue
  5. 2
      src/views/infra/codegen/components/ImportTableModal.vue
  6. 141
      src/views/infra/codegen/components/PreviewModal.vue
  7. 49
      src/views/infra/codegen/index.vue

4
src/api/infra/codegen/index.ts

@ -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')
}
// 获得表定义

35
src/utils/tree.ts

@ -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
}

1
src/views/infra/codegen/EditTable.vue

@ -0,0 +1 @@
<template><sapn>123</sapn></template>

54
src/views/infra/codegen/PreviewModal.vue

@ -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>

2
src/views/infra/codegen/ImportTableModal.vue → src/views/infra/codegen/components/ImportTableModal.vue

@ -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'])

141
src/views/infra/codegen/components/PreviewModal.vue

@ -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 = {} // 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
}
/** 复制 **/
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>

49
src/views/infra/codegen/index.vue

@ -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'))