Compare commits

..

No commits in common. "32e84f5433a7ab84ba0f6d17d63c6d12ce99cd66" and "b14435e35b9bec1ccb8ee213f4de913d7efc4c22" have entirely different histories.

30 changed files with 139 additions and 326 deletions

View File

@ -1,13 +0,0 @@
# 代码生成器
## 功能展示
点击 表名 或 注释内容 跳转到另一个页面
![屏幕截图_6-4-2025_1486_localhost](./images/屏幕截图_6-4-2025_1486_localhost.jpeg)
![image-20250406140934864](./images/image-20250406140934864.png)
![屏幕截图_6-4-2025_1486_localhost](./images/屏幕截图_6-4-2025_1486_localhost-1743920303637-1.jpeg)
<video src="./images/QQ202546-141117.mp4"></video>

View File

@ -1,5 +1,7 @@
import type { CSSOptions } from 'vite';
import type {AcceptedPlugin} from 'postcss';
import type {CSSOptions} from 'vite';
import {wrapperEnv} from './utils';
export const css = (mode: string): CSSOptions => {
return {
@ -10,3 +12,5 @@ export const css = (mode: string): CSSOptions => {
},
};
};

View File

@ -11,4 +11,4 @@ const include = ['vue', 'vue-router', 'dayjs', 'axios', 'pinia', 'vue-types', 'j
*/
const exclude: string[] = [];
export { exclude, include };
export { include, exclude };

View File

@ -9,7 +9,7 @@ import Inspector from 'vite-plugin-vue-inspector';
import { useCDN } from './cdn';
import { viteConsoleLog } from './info';
import { compressPack, report } from './utils';
import { compressPack, report, wrapperEnv } from './utils';
export const plugins = (mode: string): PluginOption[] => {
return [
@ -43,3 +43,4 @@ export const plugins = (mode: string): PluginOption[] => {
compressPack(mode),
];
};

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8"/>
<link href="/favicon.png" rel="icon" type="image/svg+xml"/>
<link href="/favicon.ico" rel="icon" type="image/svg+xml"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>%VITE_APP_TITLE%</title>
</head>

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,8 +0,0 @@
git init
git add .
git commit -m "🎉 ci: 推送脚本更新"
git checkout master
git merge dev
git push --all
git push --tags
git checkout dev

View File

@ -43,18 +43,22 @@ service.interceptors.response.use(
return response.data;
}
// 系统出错
// ElMessage.error(msg || '系统出错');
return Promise.reject(response.data.message || 'Error');
},
(error: any) => {
// 异常处理
if (error.response.data) {
const { code, message } = error.response.data;
if (code === 500) {
(window as any).$message.error(message);
} else {
(window as any).$message.error(message || '系统出错');
}
// const { code, msg } = error.response.data;
// if (code === ResultEnum.TOKEN_INVALID) {
// ElNotification({
// title: '提示',
// message: '您的会话已过期,请重新登录',
// type: 'info',
// });
// } else {
// ElMessage.error(msg || '系统出错');
// }
}
return Promise.reject(error.message);
}

View File

@ -1,14 +1,9 @@
import request from '@/api/server/request';
import type { BaseResult } from '@/types/request';
/* 所有的数据库 */
export const getDbList = () => {
return request<any, any>({ url: '/table/getDbList', method: 'GET' });
};
/* 数据库所有的表 */
export const getDbTables = (params: any) => {
return request<any, BaseResult<any>>({ url: '/table/getDbTables', method: 'get', params });
/* 获取所有数据表 */
export const getAllTableMetaData = () => {
return request<any, BaseResult<any>>({ url: '/table/getAllTableMetaData', method: 'get' });
};
/* 获取表属性 */

View File

@ -10,13 +10,3 @@ export const generator = (data: any) => {
export const getVmsPathList = () => {
return request<any, BaseResult<any>>({ url: '/vms/getVmsPathList', method: 'get' });
};
/* 打包成zip下载 */
export const downloadByZip = (data: any) => {
return request<any, any>({
url: '/vms/downloadByZip',
method: 'POST',
data,
responseType: 'blob',
});
};

View File

@ -1,11 +1,9 @@
import { createPinia } from 'pinia';
import piniaPluginPersistedState from 'pinia-plugin-persistedstate';
import type { App } from 'vue';
const pinia = createPinia();
const store = createPinia();
// 全局注册 store
export function setupStore(app: App<Element>) {
pinia.use(piniaPluginPersistedState);
app.use(pinia);
app.use(store);
}

View File

@ -1,45 +1,17 @@
import { defineStore } from 'pinia';
import { getColumnInfo, getDbList, getDbTables, getTableMetaData } from '@/api/table';
import { getAllTableMetaData, getColumnInfo, getTableMetaData } from '@/api/table';
export const useTableStore = defineStore('tableStore', {
state: () => ({
// 数据库所有的表
tableList: [],
tableListLoading: false,
// 数据列表
dbList: [],
}),
getters: {},
actions: {
/* 所有的数据库 */
async getDbList() {
this.tableListLoading = true;
const result = await getDbList();
if (result.code !== 200) {
(window as any).$message.error(result.message);
this.tableListLoading = false;
return;
}
// 整理返回数据格式
const list = result.data.map((db: any) => ({
label: db.tableCat,
value: db.tableCat,
comment: db.comment,
}));
// 在开头添加
list.unshift({ label: '无', value: undefined, comment: '查询全部' });
this.dbList = list;
this.tableListLoading = false;
},
/* 数据库所有的表 */
async getDbTables(data: any) {
const result = await getDbTables(data);
/* 获取所有数据表 */
async getAllTableMetaData() {
const result = await getAllTableMetaData();
if (result.code !== 200) {
(window as any).$message.error(result.message);
}

View File

@ -3,38 +3,12 @@ import { defineStore } from 'pinia';
import { generator, getVmsPathList } from '@/api/vms';
export const useVmsStore = defineStore('vmsStore', {
// 开启持久化
// persist: true,
// persist: {
// paths: ['formValue', 'formOption'],
// },
state: () => ({
generators: [],
// 生成服务端内容
serverOptions: [],
// 生成前端内容
webOptions: [],
// 查询的表单
formValue: {
author: 'Bunny',
packageName: 'cn.bunny.services',
requestMapping: '/api',
className: '',
tableName: '',
simpleDateFormat: 'yyyy-MM-dd HH:mm:ss',
tablePrefixes: 't_,sys_,qrtz_,log_',
comment: '',
path: [],
},
// 表单选择内容
formOption: {
generatorServer: [],
generatorWeb: [],
},
}),
getters: {},
actions: {
@ -46,7 +20,7 @@ export const useVmsStore = defineStore('vmsStore', {
(window as any).$message.error(result.message);
}
this.generators = result.data.map((i: any) => ({ ...i, path: i.path.replace('.vm', '') }));
this.generators = result.data.map((i) => ({ ...i, path: i.path.replace('.vm', '') }));
(window as any).$message.success(`生成成功,共 ${this.generators.length} 数据`);
},
@ -62,10 +36,5 @@ export const useVmsStore = defineStore('vmsStore', {
this.webOptions = result.data.web;
this.serverOptions = result.data.server;
},
/* 晴空已生成 */
clearGenerators() {
this.generators = [];
},
},
});

View File

@ -18,28 +18,3 @@ export function downloadTextAsFile(text: string, filename: string) {
URL.revokeObjectURL(a.href);
});
}
export const downloadBlob = (response: any) => {
try {
// 从响应头获取文件名
const contentDisposition = response.headers['content-disposition'];
let fileName = 'download.zip';
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename="?(.+)"?/);
if (fileNameMatch && fileNameMatch[1]) {
fileName = fileNameMatch[1];
}
}
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error(error);
}
};

View File

@ -4,7 +4,6 @@
import { useMessage } from 'naive-ui';
import { defineComponent } from 'vue';
// naive ui https://www.naiveui.com/zh-CN/light/components/message
export default defineComponent({
setup() {
window.$message = useMessage();

View File

@ -24,6 +24,7 @@ onMounted(() => {
</script>
<template>
<!-- 当前表的列字段 -->
<n-data-table :bordered="true" :columns="columns" :data="datalist" />
</template>
<style scoped></style>

View File

@ -1,52 +1,43 @@
<script lang="ts" setup>
import { NCheckbox, NCheckboxGroup, NFormItemGi, NGrid, NInput, NSpace } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { useVmsStore } from '@/store/modules/vms';
import SelectButtonGroup from '@/views/generator-code/components/generator/components/select-button-group.vue';
import { formOption, formValue } from '@/views/generator-code/components/generator/option';
const vmsStore = useVmsStore();
const { formValue, formOption } = storeToRefs(vmsStore);
</script>
<template>
<!-- 需要提交的生成表单 -->
<n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="8" label="作者名称" path="author">
<n-input v-model:value="formValue.author" placeholder="作者名称" />
<n-input v-model:value="formValue.author" placeholder="输入姓名" />
</n-form-item-gi>
<n-form-item-gi :span="8" label="requestMapping名称" path="requestMapping">
<n-input v-model:value="formValue.requestMapping" placeholder="requestMapping名称" />
<n-input v-model:value="formValue.requestMapping" placeholder="输入年龄" />
</n-form-item-gi>
<n-form-item-gi :span="8" label="表名称" path="tableName">
<n-input v-model:value="formValue.tableName" placeholder="表名称" />
<n-input v-model:value="formValue.tableName" placeholder="电话号码" />
</n-form-item-gi>
</n-grid>
<n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="8" label="类名称" path="className">
<n-input v-model:value="formValue.className" placeholder="类名称" />
<n-input v-model:value="formValue.className" placeholder="电话号码" />
</n-form-item-gi>
<n-form-item-gi :span="8" label="包名称" path="packageName">
<n-input v-model:value="formValue.packageName" placeholder="包名称" />
<n-input v-model:value="formValue.packageName" placeholder="电话号码" />
</n-form-item-gi>
<n-form-item-gi :span="8" label="时间格式" path="simpleDateFormat">
<n-input v-model:value="formValue.simpleDateFormat" placeholder="时间格式" />
<n-input v-model:value="formValue.simpleDateFormat" placeholder="电话号码" />
</n-form-item-gi>
</n-grid>
<n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="8" label="去除开头前缀" path="tablePrefixes">
<n-input v-model:value="formValue.tablePrefixes" placeholder="去除开头前缀" />
<n-input v-model:value="formValue.tablePrefixes" placeholder="电话号码" />
</n-form-item-gi>
<n-form-item-gi :span="8" label="修改注释名称" path="comment">
<n-input v-model:value="formValue.comment" placeholder="修改注释名称" />
</n-form-item-gi>
</n-grid>
<n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="12" label="生成后端" path="generatorServer">
<n-form-item-gi :span="8" label="生成后端" path="generatorServer">
<n-checkbox-group v-model:value="formOption.generatorServer">
<n-space>
<n-checkbox
@ -66,8 +57,7 @@ const { formValue, formOption } = storeToRefs(vmsStore);
</n-space>
</n-checkbox-group>
</n-form-item-gi>
<n-form-item-gi :span="12" label="生成前端" path="generatorWeb">
<n-form-item-gi :span="8" label="生成前端" path="generatorWeb">
<n-checkbox-group v-model:value="formOption.generatorWeb">
<n-space>
<n-checkbox

View File

@ -26,7 +26,6 @@ const download = (code: string, filename: string) => {
/>
),
onPositiveClick: () => {
//
downloadTextAsFile(code, inputValue.value);
},
onNegativeClick: () => {
@ -37,7 +36,6 @@ const download = (code: string, filename: string) => {
</script>
<template>
<!-- 生成好之后下面的预览文件 -->
<n-collapse>
<n-collapse-item
v-for="(item, index) in vmsStore.generators"

View File

@ -1,59 +1,61 @@
import { storeToRefs } from 'pinia';
import { reactive } from 'vue';
import { useVmsStore } from '@/store/modules/vms';
const vmsStore = useVmsStore();
const { formValue, formOption } = storeToRefs(vmsStore);
export const formOption = reactive({
generatorServer: [],
generatorWeb: [],
});
export const formValue = reactive({
author: 'Bunny',
packageName: 'cn.bunny.services',
requestMapping: '/api',
className: '',
tableName: '',
simpleDateFormat: 'yyyy-MM-dd HH:mm:ss',
tablePrefixes: 't_,sys_,qrtz_,log_',
path: [],
});
/* 初始化表单信息 */
export const formValueInit = (tableName: any) => {
formValue.value.tableName = tableName.toString();
formValue.tableName = tableName.toString();
// 替换类名称
let className: any = tableName as any;
formValue.value.tablePrefixes.split(/[,]/).forEach((item) => {
formValue.tablePrefixes.split(/[,]/).forEach((item) => {
className = className.replace(item, '');
});
formValue.value.className = className;
formValue.className = className;
};
/* 全部选择 */
export const selectAll = () => {
formOption.value.generatorServer = vmsStore.serverOptions.map((option: any) => option.name);
formOption.value.generatorWeb = vmsStore.webOptions.map((option: any) => option.name);
formOption.generatorServer = vmsStore.serverOptions.map((option: any) => option.name);
formOption.generatorWeb = vmsStore.webOptions.map((option: any) => option.name);
};
/* 全部反选 */
export const selectAllInvert = () => {
// 反选server
const serverNames: string[] = vmsStore.serverOptions.map((item) => item['name']);
const generatorServer = formOption.value.generatorServer;
formOption.value.generatorServer = serverNames.filter(
const generatorServer = formOption.generatorServer;
formOption.generatorServer = serverNames.filter(
(name: string) => !generatorServer.includes(name)
);
// 反选web
const webNames: string[] = vmsStore.webOptions.map((item) => item['name']);
const generatorWeb = formOption.value.generatorWeb;
formOption.value.generatorWeb = webNames.filter((name) => !generatorWeb.includes(name));
const generatorWeb = formOption.generatorWeb;
formOption.generatorWeb = webNames.filter((name) => !generatorWeb.includes(name));
};
/* 取消全部选择 */
export const selectCancelAll = () => {
formOption.value.generatorServer = [];
formOption.value.generatorWeb = [];
formValue.value.path = [];
};
/* 验证 formValue.path 是否为空 */
export const validateFormValue = () => {
// 选择要生成的模板
const web = formOption.value.generatorWeb;
const server = formOption.value.generatorServer;
// 整理好数据
formValue.value.path = [...server, ...web];
if (formValue.value.path.length <= 0) {
(window as any).$message.error(`选择要生成的模板`);
return;
}
formOption.generatorServer = [];
formOption.generatorWeb = [];
formValue.path = [];
};

View File

@ -1,13 +1,45 @@
<template>
<n-form ref="formRef" :label-width="80" :model="formValue" :rules="rules">
<generator-form />
<n-form-item>
<n-grid class="justify-items-center" cols="3" x-gap="24">
<n-gi>
<n-button attr-type="button" type="success" @click="selectAll">全部选择</n-button>
<n-button attr-type="button" type="warning" @click="selectAllInvert">全部反选</n-button>
<n-button attr-type="button" type="error" @click="selectCancelAll">全选取消</n-button>
</n-gi>
<n-gi>
<n-button attr-type="button" type="success" @click="onSubmit">开始生成</n-button>
<n-button attr-type="button" type="error" @click="() => (vmsStore.generators = [])">
清空已生成
</n-button>
<n-button
:disabled="!(vmsStore.generators.length > 0)"
attr-type="button"
type="primary"
@click="downloadAll"
>
下载全部 {{ vmsStore.generators.length }}
</n-button>
</n-gi>
</n-grid>
</n-form-item>
</n-form>
<!-- 生成好的数据 -->
<generator-preview />
</template>
<script lang="tsx" setup>
import { NButton, NForm, NFormItem, NGi, NGrid, useMessage } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { onMounted, ref } from 'vue';
import { computed } from 'vue-demi';
import { toRaw } from 'vue-demi';
import { useRoute } from 'vue-router';
import { downloadByZip } from '@/api/vms';
import { useVmsStore } from '@/store/modules/vms';
import { downloadBlob, downloadTextAsFile } from '@/utils/file';
import { downloadTextAsFile } from '@/utils/file';
import GeneratorForm from '@/views/generator-code/components/generator/components/generator-form.vue';
import GeneratorPreview from '@/views/generator-code/components/generator/components/generator-preview.vue';
import {
@ -15,20 +47,13 @@ import {
selectAll,
selectAllInvert,
selectCancelAll,
validateFormValue,
} from '@/views/generator-code/components/generator/hook';
import { rules } from '@/views/generator-code/components/generator/option';
import { formOption, formValue, rules } from '@/views/generator-code/components/generator/option';
const route = useRoute();
const vmsStore = useVmsStore();
const { formValue, formOption } = storeToRefs(vmsStore);
const message = useMessage();
const formRef = ref();
const hasDownloadZip = computed(
() => !(formOption.value.generatorWeb.length > 0 || formOption.value.generatorServer.length > 0)
);
/* 提交表单 */
const onSubmit = (e: MouseEvent) => {
@ -36,10 +61,19 @@ const onSubmit = (e: MouseEvent) => {
formRef.value?.validate(async (errors: any) => {
if (!errors) {
validateFormValue();
//
const web = formOption.generatorWeb;
const server = formOption.generatorServer;
//
formValue.path = [...server, ...web];
if (formValue.path.length <= 0) {
message.error(`选择要生成的模板`);
return;
}
//
await vmsStore.generator(formValue.value);
await vmsStore.generator(toRaw(formValue));
} else {
errors.forEach((error: any) => {
error.forEach((err: any) => {
@ -60,14 +94,6 @@ const downloadAll = () => {
});
};
/* 下载zip文件 */
const downloadZipFile = async () => {
validateFormValue();
const result = await downloadByZip(formValue.value);
downloadBlob(result);
};
onMounted(() => {
//
const tableName: any = route.query.tableName;
@ -76,49 +102,3 @@ onMounted(() => {
vmsStore.getVmsPathList();
});
</script>
<template>
<n-form ref="formRef" :label-width="80" :model="formValue" :rules="rules">
<generator-form />
<n-form-item>
<n-grid class="justify-items-center" cols="3" x-gap="24">
<n-gi>
<n-button attr-type="button" type="success" @click="selectAll">全部选择</n-button>
<n-button attr-type="button" type="warning" @click="selectAllInvert">全部反选</n-button>
<n-button attr-type="button" type="error" @click="selectCancelAll">全选取消</n-button>
</n-gi>
<n-gi>
<n-button attr-type="button" type="success" @click="onSubmit">开始生成</n-button>
<n-button attr-type="button" type="error" @click="vmsStore.clearGenerators()">
清空已生成
</n-button>
<n-button
:disabled="!(vmsStore.generators.length > 0)"
attr-type="button"
type="primary"
@click="downloadAll"
>
下载全部 {{ vmsStore.generators.length }}
</n-button>
</n-gi>
<n-gi class="w-full">
<n-button
:disabled="hasDownloadZip"
attr-type="button"
class="w-full"
type="success"
@click="downloadZipFile"
>
下载zip
</n-button>
</n-gi>
</n-grid>
</n-form-item>
</n-form>
<!-- 生成好的数据 -->
<generator-preview />
</template>

View File

@ -1,14 +1,10 @@
import type { FormRules } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { useVmsStore } from '@/store/modules/vms';
const vmsStore = useVmsStore();
const { formOption } = storeToRefs(vmsStore);
import { formOption, formValue } from '@/views/generator-code/components/generator/hook';
/* 验证生成前后端内容是否合法 */
const validatorFormOption = () => {
return formOption.value.generatorServer.length > 0 || formOption.value.generatorWeb.length > 0;
return formOption.generatorServer.length > 0 || formOption.generatorWeb.length > 0;
};
// 表单验证
@ -44,3 +40,5 @@ export const rules: FormRules = {
},
],
};
export { formOption, formValue };

View File

@ -1,29 +1,21 @@
<script lang="ts" setup>
import { NCard, NTabPane, NTabs } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { onMounted, reactive } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useTableStore } from '@/store/modules/table';
import { useVmsStore } from '@/store/modules/vms';
import Index from '@/views/generator-code/components/column-field/index.vue';
import GeneratorForm from '@/views/generator-code/components/generator/index.vue';
const router = useRouter();
const route = useRoute();
const tableStore = useTableStore();
const vmsStore = useVmsStore();
const { formValue, formOption } = storeToRefs(vmsStore);
//
const tableInfo = reactive({
/* 表名称 */
tableName: '',
/* 注释内容 */
comment: '',
/* 数据库内容 */
tableCat: '',
/* 通常是 "TABLE" */
tableType: '',
});
@ -32,9 +24,6 @@ const getTableData = async () => {
const tableName: any = route.query.tableName;
const tableMetaData = await tableStore.getTableMetaData(tableName);
Object.assign(tableInfo, tableMetaData);
//
formValue.value.comment = tableInfo.comment;
};
onMounted(() => {
@ -45,7 +34,7 @@ onMounted(() => {
<n-card>
<template #header>
<n-card title="数据库信息">
<span class="color-blue cursor-pointer" @click="router.push('/')">回到首页</span>
<span class="color-blue" @click="router.push('/')">回到首页</span>
<ul>
<li>表名{{ route.query.tableName }}</li>
<li>表注释{{ tableInfo.comment }}</li>

View File

@ -1,30 +1,14 @@
<script lang="tsx" setup>
import type { SelectOption } from 'naive-ui';
import { NCard, NDataTable, NSelect, NTag, NTooltip } from 'naive-ui';
import type { VNode } from 'vue';
import { h, onMounted } from 'vue';
import { NCard, NDataTable, NTag } from 'naive-ui';
import { onMounted } from 'vue';
import { useTableStore } from '@/store/modules/table';
import { columns } from '@/views/home/columns';
const tableStore = useTableStore();
/* 数据库所有的表 */
const getDbTables = (dbName: string) => {
tableStore.getDbTables({ dbName: dbName ?? undefined });
};
/* 为select添加 提示 */
const renderOptions = ({ node, option }: { node: VNode; option: SelectOption }) => {
return h(NTooltip, null, {
trigger: () => node,
default: () => option.comment,
});
};
onMounted(() => {
getDbTables(undefined);
tableStore.getDbList();
tableStore.getAllTableMetaData();
});
</script>
@ -42,23 +26,9 @@ onMounted(() => {
<n-tag type="info">{{ tableStore.tableList.length }}</n-tag>
张表
</p>
<!-- 选择数据库 -->
<n-select
:on-update-value="getDbTables"
:options="tableStore.dbList"
:render-option="renderOptions"
class="mt-2 w-[200px]"
clear-filter-after-select
clearable
placeholder="选择数据库"
/>
</n-card>
<n-data-table
:bordered="true"
:columns="columns()"
:data="tableStore.tableList"
:loading="tableStore.tableListLoading"
/>
<n-data-table :bordered="true" :columns="columns()" :data="tableStore.tableList" />
</template>
<style scoped></style>

View File

@ -35,7 +35,6 @@
]
},
"include": [
"build/**",
"mock/*.ts",
"src/**/*.ts",
"src/**/*.tsx",