feat: 代码生成器
parent
cd0c7a86e5
commit
b376524980
|
|
@ -45,6 +45,7 @@
|
||||||
"dayjs": "catalog:",
|
"dayjs": "catalog:",
|
||||||
"pinia": "catalog:",
|
"pinia": "catalog:",
|
||||||
"vue": "catalog:",
|
"vue": "catalog:",
|
||||||
|
"vue-codemirror": "^6.1.1",
|
||||||
"vue-router": "catalog:"
|
"vue-router": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace InfraCodegenApi {
|
||||||
|
/** 代码生成表定义 */
|
||||||
|
export interface CodegenTable {
|
||||||
|
id: number;
|
||||||
|
tableId: number;
|
||||||
|
isParentMenuIdValid: boolean;
|
||||||
|
dataSourceConfigId: number;
|
||||||
|
scene: number;
|
||||||
|
tableName: string;
|
||||||
|
tableComment: string;
|
||||||
|
remark: string;
|
||||||
|
moduleName: string;
|
||||||
|
businessName: string;
|
||||||
|
className: string;
|
||||||
|
classComment: string;
|
||||||
|
author: string;
|
||||||
|
createTime: Date;
|
||||||
|
updateTime: Date;
|
||||||
|
templateType: number;
|
||||||
|
parentMenuId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 代码生成字段定义 */
|
||||||
|
export interface CodegenColumn {
|
||||||
|
id: number;
|
||||||
|
tableId: number;
|
||||||
|
columnName: string;
|
||||||
|
dataType: string;
|
||||||
|
columnComment: string;
|
||||||
|
nullable: number;
|
||||||
|
primaryKey: number;
|
||||||
|
ordinalPosition: number;
|
||||||
|
javaType: string;
|
||||||
|
javaField: string;
|
||||||
|
dictType: string;
|
||||||
|
example: string;
|
||||||
|
createOperation: number;
|
||||||
|
updateOperation: number;
|
||||||
|
listOperation: number;
|
||||||
|
listOperationCondition: string;
|
||||||
|
listOperationResult: number;
|
||||||
|
htmlType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 数据库表定义 */
|
||||||
|
export interface DatabaseTable {
|
||||||
|
name: string;
|
||||||
|
comment: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 代码生成详情 */
|
||||||
|
export interface CodegenDetail {
|
||||||
|
table: CodegenTable;
|
||||||
|
columns: CodegenColumn[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 代码预览 */
|
||||||
|
export interface CodegenPreview {
|
||||||
|
filePath: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新代码生成请求 */
|
||||||
|
export interface CodegenUpdateReq {
|
||||||
|
table: any | CodegenTable;
|
||||||
|
columns: CodegenColumn[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建代码生成请求 */
|
||||||
|
export interface CodegenCreateListReq {
|
||||||
|
dataSourceConfigId: number;
|
||||||
|
tableNames: string[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询列表代码生成表定义 */
|
||||||
|
export function getCodegenTableList(dataSourceConfigId: number) {
|
||||||
|
return requestClient.get<InfraCodegenApi.CodegenTable[]>('/infra/codegen/table/list', {
|
||||||
|
params: { dataSourceConfigId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询列表代码生成表定义 */
|
||||||
|
export function getCodegenTablePage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<InfraCodegenApi.CodegenTable>>('/infra/codegen/table/page', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询详情代码生成表定义 */
|
||||||
|
export function getCodegenTable(id: number) {
|
||||||
|
return requestClient.get<InfraCodegenApi.CodegenDetail>('/infra/codegen/detail', {
|
||||||
|
params: { tableId: id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增代码生成表定义 */
|
||||||
|
export function createCodegenTable(data: InfraCodegenApi.CodegenCreateListReq) {
|
||||||
|
return requestClient.post('/infra/codegen/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改代码生成表定义 */
|
||||||
|
export function updateCodegenTable(data: InfraCodegenApi.CodegenUpdateReq) {
|
||||||
|
return requestClient.put('/infra/codegen/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 基于数据库的表结构,同步数据库的表和字段定义 */
|
||||||
|
export function syncCodegenFromDB(id: number) {
|
||||||
|
return requestClient.put('/infra/codegen/sync-from-db', {
|
||||||
|
params: { tableId: id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 预览生成代码 */
|
||||||
|
export function previewCodegen(id: number) {
|
||||||
|
return requestClient.get<InfraCodegenApi.CodegenPreview[]>('/infra/codegen/preview', {
|
||||||
|
params: { tableId: id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 下载生成代码 */
|
||||||
|
export function downloadCodegen(id: number) {
|
||||||
|
return requestClient.download('/infra/codegen/download', {
|
||||||
|
params: { tableId: id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得表定义 */
|
||||||
|
export function getSchemaTableList(params: any) {
|
||||||
|
return requestClient.get<InfraCodegenApi.DatabaseTable[]>('/infra/codegen/db/table/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 基于数据库的表结构,创建代码生成器的表定义 */
|
||||||
|
export function createCodegenList(data: InfraCodegenApi.CodegenCreateListReq) {
|
||||||
|
return requestClient.post('/infra/codegen/create-list', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除代码生成表定义 */
|
||||||
|
export function deleteCodegenTable(id: number) {
|
||||||
|
return requestClient.delete('/infra/codegen/delete', {
|
||||||
|
params: { tableId: id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -23,11 +23,7 @@ const externalRoutes: RouteRecordRaw[] = [];
|
||||||
|
|
||||||
/** 路由列表,由基本路由、外部路由和404兜底路由组成
|
/** 路由列表,由基本路由、外部路由和404兜底路由组成
|
||||||
* 无需走权限验证(会一直显示在菜单中) */
|
* 无需走权限验证(会一直显示在菜单中) */
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [...coreRoutes, ...externalRoutes, fallbackNotFoundRoute];
|
||||||
...coreRoutes,
|
|
||||||
...externalRoutes,
|
|
||||||
fallbackNotFoundRoute,
|
|
||||||
];
|
|
||||||
|
|
||||||
/** 基本路由列表,这些路由不需要进入权限拦截 */
|
/** 基本路由列表,这些路由不需要进入权限拦截 */
|
||||||
const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
|
const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
|
||||||
|
|
@ -36,12 +32,10 @@ const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
|
||||||
const accessRoutes = [...dynamicRoutes, ...staticRoutes];
|
const accessRoutes = [...dynamicRoutes, ...staticRoutes];
|
||||||
|
|
||||||
// add by 芋艿:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/router/routes/index.ts#L38-L45
|
// add by 芋艿:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/router/routes/index.ts#L38-L45
|
||||||
const componentKeys: string[] = Object.keys(
|
const componentKeys: string[] = Object.keys(import.meta.glob('../../views/**/*.vue'))
|
||||||
import.meta.glob('../../views/**/*.vue'),
|
|
||||||
)
|
|
||||||
.filter((item) => !item.includes('/modules/'))
|
.filter((item) => !item.includes('/modules/'))
|
||||||
.map((v) => {
|
.map((v) => {
|
||||||
const path = v.replace('../../views/', '/');
|
const path = v.replace('../../views/', '/');
|
||||||
return path.endsWith('.vue') ? path.slice(0, -4) : path;
|
return path.endsWith('.vue') ? path.slice(0, -4) : path;
|
||||||
});
|
});
|
||||||
export { accessRoutes, coreRouteNames, routes, componentKeys };
|
export { accessRoutes, componentKeys, coreRouteNames, routes };
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
meta: {
|
||||||
|
icon: 'ic:baseline-view-in-ar',
|
||||||
|
keepAlive: true,
|
||||||
|
order: 1000,
|
||||||
|
title: $t('demos.title'),
|
||||||
|
},
|
||||||
|
name: 'Demos',
|
||||||
|
path: '/demos',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
meta: {
|
||||||
|
title: $t('demos.antd'),
|
||||||
|
},
|
||||||
|
name: 'AntDesignDemos',
|
||||||
|
path: '/demos/ant-design',
|
||||||
|
component: () => import('#/views/demos/antd/index.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/codegen',
|
||||||
|
name: 'CodegenEdit',
|
||||||
|
meta: {
|
||||||
|
icon: 'ic:baseline-view-in-ar',
|
||||||
|
keepAlive: true,
|
||||||
|
order: 1000,
|
||||||
|
title: $t('demos.title'),
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
// {
|
||||||
|
// path: 'codegen/edit',
|
||||||
|
// name: 'InfraCodegenEdit',
|
||||||
|
// component: () => import('#/views/infra/codegen/edit.vue'),
|
||||||
|
// meta: {
|
||||||
|
// title: '修改生成配置',
|
||||||
|
// activeMenu: '/infra/codegen',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routes;
|
||||||
|
|
@ -2,36 +2,24 @@ import dayjs from 'dayjs';
|
||||||
|
|
||||||
// TODO @芋艿:后续整理下
|
// TODO @芋艿:后续整理下
|
||||||
|
|
||||||
// TODO @puhui999:转成 function 方式哈
|
|
||||||
/** 时间段选择器拓展 */
|
/** 时间段选择器拓展 */
|
||||||
export const getRangePickerDefaultProps = () => {
|
export function getRangePickerDefaultProps() {
|
||||||
return {
|
return {
|
||||||
showTime: {
|
showTime: {
|
||||||
format: 'HH:mm:ss',
|
format: 'HH:mm:ss',
|
||||||
defaultValue: [
|
defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('23:59:59', 'HH:mm:ss')],
|
||||||
dayjs('00:00:00', 'HH:mm:ss'),
|
|
||||||
dayjs('23:59:59', 'HH:mm:ss'),
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
format: 'YYYY-MM-DD HH:mm:ss',
|
format: 'YYYY-MM-DD HH:mm:ss',
|
||||||
placeholder: ['开始时间', '结束时间'],
|
placeholder: ['开始时间', '结束时间'],
|
||||||
|
// prettier-ignore
|
||||||
ranges: {
|
ranges: {
|
||||||
'今天': [dayjs().startOf('day'), dayjs().endOf('day')],
|
'今天': [dayjs().startOf('day'), dayjs().endOf('day')],
|
||||||
'昨天': [
|
'昨天': [dayjs().subtract(1, 'day').startOf('day'), dayjs().subtract(1, 'day').endOf('day')],
|
||||||
dayjs().subtract(1, 'day').startOf('day'),
|
|
||||||
dayjs().subtract(1, 'day').endOf('day'),
|
|
||||||
],
|
|
||||||
'本周': [dayjs().startOf('week'), dayjs().endOf('day')],
|
'本周': [dayjs().startOf('week'), dayjs().endOf('day')],
|
||||||
'本月': [dayjs().startOf('month'), dayjs().endOf('day')],
|
'本月': [dayjs().startOf('month'), dayjs().endOf('day')],
|
||||||
'最近 7 天': [
|
'最近 7 天': [dayjs().subtract(7, 'day').startOf('day'), dayjs().endOf('day')],
|
||||||
dayjs().subtract(7, 'day').startOf('day'),
|
'最近 30 天': [dayjs().subtract(30, 'day').startOf('day'), dayjs().endOf('day')],
|
||||||
dayjs().endOf('day'),
|
|
||||||
],
|
|
||||||
'最近 30 天': [
|
|
||||||
dayjs().subtract(30, 'day').startOf('day'),
|
|
||||||
dayjs().endOf('day'),
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
transformDateFunc: (dates: any) => {
|
transformDateFunc: (dates: any) => {
|
||||||
if (dates && dates.length === 2) {
|
if (dates && dates.length === 2) {
|
||||||
|
|
@ -40,4 +28,4 @@ export const getRangePickerDefaultProps = () => {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const dictStore = useDictStore();
|
||||||
*/
|
*/
|
||||||
function getDictLabel(dictType: string, value: any) {
|
function getDictLabel(dictType: string, value: any) {
|
||||||
const dictObj = dictStore.getDictData(dictType, value);
|
const dictObj = dictStore.getDictData(dictType, value);
|
||||||
return isObject(dictObj)? dictObj.label : '';
|
return isObject(dictObj) ? dictObj.label : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,10 +38,7 @@ function getDictObj(dictType: string, value: any) {
|
||||||
* @param dictType 字典类型
|
* @param dictType 字典类型
|
||||||
* @returns 字典数组
|
* @returns 字典数组
|
||||||
*/
|
*/
|
||||||
function getDictOptions(
|
function getDictOptions(dictType: string, valueType: 'boolean' | 'number' | 'string' = 'string') {
|
||||||
dictType: string,
|
|
||||||
valueType: 'boolean' | 'number' | 'string' = 'string',
|
|
||||||
) {
|
|
||||||
const dictOpts = dictStore.getDictOptions(dictType);
|
const dictOpts = dictStore.getDictOptions(dictType);
|
||||||
const dictOptions: DefaultOptionType = [];
|
const dictOptions: DefaultOptionType = [];
|
||||||
if (dictOpts.length > 0) {
|
if (dictOpts.length > 0) {
|
||||||
|
|
@ -72,137 +69,138 @@ function getDictOptions(
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DICT_TYPE {
|
enum DICT_TYPE {
|
||||||
AI_GENERATE_MODE = 'ai_generate_mode', // AI 生成模式
|
USER_TYPE = 'user_type',
|
||||||
AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态
|
COMMON_STATUS = 'common_status',
|
||||||
AI_MUSIC_STATUS = 'ai_music_status', // AI 音乐状态
|
TERMINAL = 'terminal', // 终端
|
||||||
// ========== AI - 人工智能模块 ==========
|
DATE_INTERVAL = 'date_interval', // 数据间隔
|
||||||
AI_PLATFORM = 'ai_platform', // AI 平台
|
|
||||||
|
// ========== SYSTEM 模块 ==========
|
||||||
|
SYSTEM_USER_SEX = 'system_user_sex',
|
||||||
|
SYSTEM_MENU_TYPE = 'system_menu_type',
|
||||||
|
SYSTEM_ROLE_TYPE = 'system_role_type',
|
||||||
|
SYSTEM_DATA_SCOPE = 'system_data_scope',
|
||||||
|
SYSTEM_NOTICE_TYPE = 'system_notice_type',
|
||||||
|
SYSTEM_LOGIN_TYPE = 'system_login_type',
|
||||||
|
SYSTEM_LOGIN_RESULT = 'system_login_result',
|
||||||
|
SYSTEM_SMS_CHANNEL_CODE = 'system_sms_channel_code',
|
||||||
|
SYSTEM_SMS_TEMPLATE_TYPE = 'system_sms_template_type',
|
||||||
|
SYSTEM_SMS_SEND_STATUS = 'system_sms_send_status',
|
||||||
|
SYSTEM_SMS_RECEIVE_STATUS = 'system_sms_receive_status',
|
||||||
|
SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type',
|
||||||
|
SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status',
|
||||||
|
SYSTEM_NOTIFY_TEMPLATE_TYPE = 'system_notify_template_type',
|
||||||
|
SYSTEM_SOCIAL_TYPE = 'system_social_type',
|
||||||
|
|
||||||
|
// ========== INFRA 模块 ==========
|
||||||
|
INFRA_BOOLEAN_STRING = 'infra_boolean_string',
|
||||||
|
INFRA_JOB_STATUS = 'infra_job_status',
|
||||||
|
INFRA_JOB_LOG_STATUS = 'infra_job_log_status',
|
||||||
|
INFRA_API_ERROR_LOG_PROCESS_STATUS = 'infra_api_error_log_process_status',
|
||||||
|
INFRA_CONFIG_TYPE = 'infra_config_type',
|
||||||
|
INFRA_CODEGEN_TEMPLATE_TYPE = 'infra_codegen_template_type',
|
||||||
|
INFRA_CODEGEN_FRONT_TYPE = 'infra_codegen_front_type',
|
||||||
|
INFRA_CODEGEN_SCENE = 'infra_codegen_scene',
|
||||||
|
INFRA_FILE_STORAGE = 'infra_file_storage',
|
||||||
|
INFRA_OPERATE_TYPE = 'infra_operate_type',
|
||||||
|
|
||||||
AI_WRITE_FORMAT = 'ai_write_format', // AI 写作格式
|
|
||||||
AI_WRITE_LANGUAGE = 'ai_write_language', // AI 写作语言
|
|
||||||
AI_WRITE_LENGTH = 'ai_write_length', // AI 写作长度
|
|
||||||
AI_WRITE_TONE = 'ai_write_tone', // AI 写作语气
|
|
||||||
AI_WRITE_TYPE = 'ai_write_type', // AI 写作类型
|
|
||||||
BPM_MODEL_FORM_TYPE = 'bpm_model_form_type',
|
|
||||||
// ========== BPM 模块 ==========
|
// ========== BPM 模块 ==========
|
||||||
BPM_MODEL_TYPE = 'bpm_model_type',
|
BPM_MODEL_TYPE = 'bpm_model_type',
|
||||||
BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type',
|
BPM_MODEL_FORM_TYPE = 'bpm_model_form_type',
|
||||||
|
BPM_TASK_CANDIDATE_STRATEGY = 'bpm_task_candidate_strategy',
|
||||||
BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status',
|
BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status',
|
||||||
|
BPM_TASK_STATUS = 'bpm_task_status',
|
||||||
|
BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type',
|
||||||
BPM_PROCESS_LISTENER_TYPE = 'bpm_process_listener_type',
|
BPM_PROCESS_LISTENER_TYPE = 'bpm_process_listener_type',
|
||||||
BPM_PROCESS_LISTENER_VALUE_TYPE = 'bpm_process_listener_value_type',
|
BPM_PROCESS_LISTENER_VALUE_TYPE = 'bpm_process_listener_value_type',
|
||||||
BPM_TASK_CANDIDATE_STRATEGY = 'bpm_task_candidate_strategy',
|
|
||||||
BPM_TASK_STATUS = 'bpm_task_status',
|
|
||||||
BROKERAGE_BANK_NAME = 'brokerage_bank_name', // 佣金提现银行
|
|
||||||
BROKERAGE_BIND_MODE = 'brokerage_bind_mode', // 分销关系绑定模式
|
|
||||||
|
|
||||||
|
// ========== PAY 模块 ==========
|
||||||
|
PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型
|
||||||
|
PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态
|
||||||
|
PAY_REFUND_STATUS = 'pay_refund_status', // 退款订单状态
|
||||||
|
PAY_NOTIFY_STATUS = 'pay_notify_status', // 商户支付回调状态
|
||||||
|
PAY_NOTIFY_TYPE = 'pay_notify_type', // 商户支付回调状态
|
||||||
|
PAY_TRANSFER_STATUS = 'pay_transfer_status', // 转账订单状态
|
||||||
|
PAY_TRANSFER_TYPE = 'pay_transfer_type', // 转账订单状态
|
||||||
|
|
||||||
|
// ========== MP 模块 ==========
|
||||||
|
MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型
|
||||||
|
MP_MESSAGE_TYPE = 'mp_message_type', // 消息类型
|
||||||
|
|
||||||
|
// ========== Member 会员模块 ==========
|
||||||
|
MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型
|
||||||
|
MEMBER_EXPERIENCE_BIZ_TYPE = 'member_experience_biz_type', // 会员经验业务类型
|
||||||
|
|
||||||
|
// ========== MALL - 商品模块 ==========
|
||||||
|
PRODUCT_SPU_STATUS = 'product_spu_status', //商品状态
|
||||||
|
|
||||||
|
// ========== MALL - 交易模块 ==========
|
||||||
|
EXPRESS_CHARGE_MODE = 'trade_delivery_express_charge_mode', //快递的计费方式
|
||||||
|
TRADE_AFTER_SALE_STATUS = 'trade_after_sale_status', // 售后 - 状态
|
||||||
|
TRADE_AFTER_SALE_WAY = 'trade_after_sale_way', // 售后 - 方式
|
||||||
|
TRADE_AFTER_SALE_TYPE = 'trade_after_sale_type', // 售后 - 类型
|
||||||
|
TRADE_ORDER_TYPE = 'trade_order_type', // 订单 - 类型
|
||||||
|
TRADE_ORDER_STATUS = 'trade_order_status', // 订单 - 状态
|
||||||
|
TRADE_ORDER_ITEM_AFTER_SALE_STATUS = 'trade_order_item_after_sale_status', // 订单项 - 售后状态
|
||||||
|
TRADE_DELIVERY_TYPE = 'trade_delivery_type', // 配送方式
|
||||||
BROKERAGE_ENABLED_CONDITION = 'brokerage_enabled_condition', // 分佣模式
|
BROKERAGE_ENABLED_CONDITION = 'brokerage_enabled_condition', // 分佣模式
|
||||||
|
BROKERAGE_BIND_MODE = 'brokerage_bind_mode', // 分销关系绑定模式
|
||||||
|
BROKERAGE_BANK_NAME = 'brokerage_bank_name', // 佣金提现银行
|
||||||
|
BROKERAGE_WITHDRAW_TYPE = 'brokerage_withdraw_type', // 佣金提现类型
|
||||||
BROKERAGE_RECORD_BIZ_TYPE = 'brokerage_record_biz_type', // 佣金业务类型
|
BROKERAGE_RECORD_BIZ_TYPE = 'brokerage_record_biz_type', // 佣金业务类型
|
||||||
BROKERAGE_RECORD_STATUS = 'brokerage_record_status', // 佣金状态
|
BROKERAGE_RECORD_STATUS = 'brokerage_record_status', // 佣金状态
|
||||||
BROKERAGE_WITHDRAW_STATUS = 'brokerage_withdraw_status', // 佣金提现状态
|
BROKERAGE_WITHDRAW_STATUS = 'brokerage_withdraw_status', // 佣金提现状态
|
||||||
BROKERAGE_WITHDRAW_TYPE = 'brokerage_withdraw_type', // 佣金提现类型
|
|
||||||
COMMON_STATUS = 'common_status',
|
// ========== MALL - 营销模块 ==========
|
||||||
|
PROMOTION_DISCOUNT_TYPE = 'promotion_discount_type', // 优惠类型
|
||||||
|
PROMOTION_PRODUCT_SCOPE = 'promotion_product_scope', // 营销的商品范围
|
||||||
|
PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE = 'promotion_coupon_template_validity_type', // 优惠劵模板的有限期类型
|
||||||
|
PROMOTION_COUPON_STATUS = 'promotion_coupon_status', // 优惠劵的状态
|
||||||
|
PROMOTION_COUPON_TAKE_TYPE = 'promotion_coupon_take_type', // 优惠劵的领取方式
|
||||||
|
PROMOTION_CONDITION_TYPE = 'promotion_condition_type', // 营销的条件类型枚举
|
||||||
|
PROMOTION_BARGAIN_RECORD_STATUS = 'promotion_bargain_record_status', // 砍价记录的状态
|
||||||
|
PROMOTION_COMBINATION_RECORD_STATUS = 'promotion_combination_record_status', // 拼团记录的状态
|
||||||
|
PROMOTION_BANNER_POSITION = 'promotion_banner_position', // banner 定位
|
||||||
|
|
||||||
// ========== CRM - 客户管理模块 ==========
|
// ========== CRM - 客户管理模块 ==========
|
||||||
CRM_AUDIT_STATUS = 'crm_audit_status', // CRM 审批状态
|
CRM_AUDIT_STATUS = 'crm_audit_status', // CRM 审批状态
|
||||||
CRM_BIZ_TYPE = 'crm_biz_type', // CRM 业务类型
|
CRM_BIZ_TYPE = 'crm_biz_type', // CRM 业务类型
|
||||||
CRM_BUSINESS_END_STATUS_TYPE = 'crm_business_end_status_type', // CRM 商机结束状态类型
|
CRM_BUSINESS_END_STATUS_TYPE = 'crm_business_end_status_type', // CRM 商机结束状态类型
|
||||||
|
CRM_RECEIVABLE_RETURN_TYPE = 'crm_receivable_return_type', // CRM 回款的还款方式
|
||||||
CRM_CUSTOMER_INDUSTRY = 'crm_customer_industry', // CRM 客户所属行业
|
CRM_CUSTOMER_INDUSTRY = 'crm_customer_industry', // CRM 客户所属行业
|
||||||
|
|
||||||
CRM_CUSTOMER_LEVEL = 'crm_customer_level', // CRM 客户级别
|
CRM_CUSTOMER_LEVEL = 'crm_customer_level', // CRM 客户级别
|
||||||
CRM_CUSTOMER_SOURCE = 'crm_customer_source', // CRM 客户来源
|
CRM_CUSTOMER_SOURCE = 'crm_customer_source', // CRM 客户来源
|
||||||
CRM_FOLLOW_UP_TYPE = 'crm_follow_up_type', // CRM 跟进方式
|
|
||||||
CRM_PERMISSION_LEVEL = 'crm_permission_level', // CRM 数据权限的级别
|
|
||||||
CRM_PRODUCT_STATUS = 'crm_product_status', // CRM 商品状态
|
CRM_PRODUCT_STATUS = 'crm_product_status', // CRM 商品状态
|
||||||
|
CRM_PERMISSION_LEVEL = 'crm_permission_level', // CRM 数据权限的级别
|
||||||
CRM_PRODUCT_UNIT = 'crm_product_unit', // CRM 产品单位
|
CRM_PRODUCT_UNIT = 'crm_product_unit', // CRM 产品单位
|
||||||
CRM_RECEIVABLE_RETURN_TYPE = 'crm_receivable_return_type', // CRM 回款的还款方式
|
CRM_FOLLOW_UP_TYPE = 'crm_follow_up_type', // CRM 跟进方式
|
||||||
DATE_INTERVAL = 'date_interval', // 数据间隔
|
|
||||||
|
|
||||||
// ========== ERP - 企业资源计划模块 ==========
|
// ========== ERP - 企业资源计划模块 ==========
|
||||||
ERP_AUDIT_STATUS = 'erp_audit_status', // ERP 审批状态
|
ERP_AUDIT_STATUS = 'erp_audit_status', // ERP 审批状态
|
||||||
ERP_STOCK_RECORD_BIZ_TYPE = 'erp_stock_record_biz_type', // 库存明细的业务类型
|
ERP_STOCK_RECORD_BIZ_TYPE = 'erp_stock_record_biz_type', // 库存明细的业务类型
|
||||||
// ========== MALL - 交易模块 ==========
|
|
||||||
EXPRESS_CHARGE_MODE = 'trade_delivery_express_charge_mode', // 快递的计费方式
|
|
||||||
INFRA_API_ERROR_LOG_PROCESS_STATUS = 'infra_api_error_log_process_status',
|
|
||||||
// ========== INFRA 模块 ==========
|
|
||||||
INFRA_BOOLEAN_STRING = 'infra_boolean_string',
|
|
||||||
INFRA_CODEGEN_FRONT_TYPE = 'infra_codegen_front_type',
|
|
||||||
INFRA_CODEGEN_SCENE = 'infra_codegen_scene',
|
|
||||||
|
|
||||||
INFRA_CODEGEN_TEMPLATE_TYPE = 'infra_codegen_template_type',
|
// ========== AI - 人工智能模块 ==========
|
||||||
INFRA_CONFIG_TYPE = 'infra_config_type',
|
AI_PLATFORM = 'ai_platform', // AI 平台
|
||||||
|
AI_MODEL_TYPE = 'ai_model_type', // AI 模型类型
|
||||||
|
AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态
|
||||||
|
AI_MUSIC_STATUS = 'ai_music_status', // AI 音乐状态
|
||||||
|
AI_GENERATE_MODE = 'ai_generate_mode', // AI 生成模式
|
||||||
|
AI_WRITE_TYPE = 'ai_write_type', // AI 写作类型
|
||||||
|
AI_WRITE_LENGTH = 'ai_write_length', // AI 写作长度
|
||||||
|
AI_WRITE_FORMAT = 'ai_write_format', // AI 写作格式
|
||||||
|
AI_WRITE_TONE = 'ai_write_tone', // AI 写作语气
|
||||||
|
AI_WRITE_LANGUAGE = 'ai_write_language', // AI 写作语言
|
||||||
|
|
||||||
INFRA_FILE_STORAGE = 'infra_file_storage',
|
|
||||||
INFRA_JOB_LOG_STATUS = 'infra_job_log_status',
|
|
||||||
|
|
||||||
INFRA_JOB_STATUS = 'infra_job_status',
|
|
||||||
|
|
||||||
INFRA_OPERATE_TYPE = 'infra_operate_type',
|
|
||||||
IOT_DATA_FORMAT = 'iot_data_format', // IOT 数据格式
|
|
||||||
IOT_DATA_TYPE = 'iot_data_type', // IOT 数据类型
|
|
||||||
IOT_DEVICE_STATUS = 'iot_device_status', // IOT 设备状态
|
|
||||||
// ========== IOT - 物联网模块 ==========
|
// ========== IOT - 物联网模块 ==========
|
||||||
IOT_NET_TYPE = 'iot_net_type', // IOT 联网方式
|
IOT_NET_TYPE = 'iot_net_type', // IOT 联网方式
|
||||||
IOT_PRODUCT_DEVICE_TYPE = 'iot_product_device_type', // IOT 产品设备类型
|
|
||||||
IOT_PRODUCT_FUNCTION_TYPE = 'iot_product_function_type', // IOT 产品功能类型
|
|
||||||
IOT_PRODUCT_STATUS = 'iot_product_status', // IOT 产品状态
|
|
||||||
IOT_PROTOCOL_TYPE = 'iot_protocol_type', // IOT 接入网关协议
|
|
||||||
IOT_RW_TYPE = 'iot_rw_type', // IOT 读写类型
|
|
||||||
IOT_UNIT_TYPE = 'iot_unit_type', // IOT 单位类型
|
|
||||||
IOT_VALIDATE_TYPE = 'iot_validate_type', // IOT 数据校验级别
|
IOT_VALIDATE_TYPE = 'iot_validate_type', // IOT 数据校验级别
|
||||||
MEMBER_EXPERIENCE_BIZ_TYPE = 'member_experience_biz_type', // 会员经验业务类型
|
IOT_PRODUCT_STATUS = 'iot_product_status', // IOT 产品状态
|
||||||
// ========== Member 会员模块 ==========
|
IOT_PRODUCT_DEVICE_TYPE = 'iot_product_device_type', // IOT 产品设备类型
|
||||||
MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型
|
IOT_DATA_FORMAT = 'iot_data_format', // IOT 数据格式
|
||||||
// ========== MP 模块 ==========
|
IOT_PROTOCOL_TYPE = 'iot_protocol_type', // IOT 接入网关协议
|
||||||
MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型
|
IOT_DEVICE_STATUS = 'iot_device_status', // IOT 设备状态
|
||||||
|
IOT_PRODUCT_FUNCTION_TYPE = 'iot_product_function_type', // IOT 产品功能类型
|
||||||
MP_MESSAGE_TYPE = 'mp_message_type', // 消息类型
|
IOT_DATA_TYPE = 'iot_data_type', // IOT 数据类型
|
||||||
// ========== PAY 模块 ==========
|
IOT_UNIT_TYPE = 'iot_unit_type', // IOT 单位类型
|
||||||
PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型
|
IOT_RW_TYPE = 'iot_rw_type', // IOT 读写类型
|
||||||
PAY_NOTIFY_STATUS = 'pay_notify_status', // 商户支付回调状态
|
|
||||||
PAY_NOTIFY_TYPE = 'pay_notify_type', // 商户支付回调状态
|
|
||||||
PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态
|
|
||||||
PAY_REFUND_STATUS = 'pay_refund_status', // 退款订单状态
|
|
||||||
PAY_TRANSFER_STATUS = 'pay_transfer_status', // 转账订单状态
|
|
||||||
PAY_TRANSFER_TYPE = 'pay_transfer_type', // 转账订单状态
|
|
||||||
// ========== MALL - 商品模块 ==========
|
|
||||||
PRODUCT_SPU_STATUS = 'product_spu_status', // 商品状态
|
|
||||||
|
|
||||||
PROMOTION_BANNER_POSITION = 'promotion_banner_position', // banner 定位
|
|
||||||
PROMOTION_BARGAIN_RECORD_STATUS = 'promotion_bargain_record_status', // 砍价记录的状态
|
|
||||||
PROMOTION_COMBINATION_RECORD_STATUS = 'promotion_combination_record_status', // 拼团记录的状态
|
|
||||||
PROMOTION_CONDITION_TYPE = 'promotion_condition_type', // 营销的条件类型枚举
|
|
||||||
PROMOTION_COUPON_STATUS = 'promotion_coupon_status', // 优惠劵的状态
|
|
||||||
PROMOTION_COUPON_TAKE_TYPE = 'promotion_coupon_take_type', // 优惠劵的领取方式
|
|
||||||
PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE = 'promotion_coupon_template_validity_type', // 优惠劵模板的有限期类型
|
|
||||||
// ========== MALL - 营销模块 ==========
|
|
||||||
PROMOTION_DISCOUNT_TYPE = 'promotion_discount_type', // 优惠类型
|
|
||||||
PROMOTION_PRODUCT_SCOPE = 'promotion_product_scope', // 营销的商品范围
|
|
||||||
SYSTEM_DATA_SCOPE = 'system_data_scope',
|
|
||||||
SYSTEM_LOGIN_RESULT = 'system_login_result',
|
|
||||||
|
|
||||||
SYSTEM_LOGIN_TYPE = 'system_login_type',
|
|
||||||
SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status',
|
|
||||||
|
|
||||||
SYSTEM_MENU_TYPE = 'system_menu_type',
|
|
||||||
SYSTEM_NOTICE_TYPE = 'system_notice_type',
|
|
||||||
SYSTEM_NOTIFY_TEMPLATE_TYPE = 'system_notify_template_type',
|
|
||||||
SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type',
|
|
||||||
SYSTEM_ROLE_TYPE = 'system_role_type',
|
|
||||||
SYSTEM_SMS_CHANNEL_CODE = 'system_sms_channel_code',
|
|
||||||
SYSTEM_SMS_RECEIVE_STATUS = 'system_sms_receive_status',
|
|
||||||
SYSTEM_SMS_SEND_STATUS = 'system_sms_send_status',
|
|
||||||
SYSTEM_SMS_TEMPLATE_TYPE = 'system_sms_template_type',
|
|
||||||
|
|
||||||
SYSTEM_SOCIAL_TYPE = 'system_social_type',
|
|
||||||
// ========== SYSTEM 模块 ==========
|
|
||||||
SYSTEM_USER_SEX = 'system_user_sex',
|
|
||||||
TERMINAL = 'terminal', // 终端
|
|
||||||
TRADE_AFTER_SALE_STATUS = 'trade_after_sale_status', // 售后 - 状态
|
|
||||||
TRADE_AFTER_SALE_TYPE = 'trade_after_sale_type', // 售后 - 类型
|
|
||||||
TRADE_AFTER_SALE_WAY = 'trade_after_sale_way', // 售后 - 方式
|
|
||||||
TRADE_DELIVERY_TYPE = 'trade_delivery_type', // 配送方式
|
|
||||||
TRADE_ORDER_ITEM_AFTER_SALE_STATUS = 'trade_order_item_after_sale_status', // 订单项 - 售后状态
|
|
||||||
TRADE_ORDER_STATUS = 'trade_order_status', // 订单 - 状态
|
|
||||||
TRADE_ORDER_TYPE = 'trade_order_type', // 订单 - 类型
|
|
||||||
USER_TYPE = 'user_type',
|
|
||||||
}
|
}
|
||||||
export { DICT_TYPE, getDictObj, getDictLabel, getDictOptions };
|
export { DICT_TYPE, getDictLabel, getDictObj, getDictOptions };
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||||
|
|
||||||
|
import { QuestionCircleFilled } from '@vben/icons';
|
||||||
|
|
||||||
|
import { ref, unref, watch } from 'vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'InfraCodegenBasicInfoForm' });
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
table?: InfraCodegenApi.CodegenTable;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const formData = ref({
|
||||||
|
tableName: '',
|
||||||
|
tableComment: '',
|
||||||
|
className: '',
|
||||||
|
author: '',
|
||||||
|
remark: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
tableName: [{ required: true, message: '请输入表名称', trigger: 'blur' }],
|
||||||
|
tableComment: [{ required: true, message: '请输入表描述', trigger: 'blur' }],
|
||||||
|
className: [{ required: true, message: '请输入实体类名称', trigger: 'blur' }],
|
||||||
|
author: [{ required: true, message: '请输入作者', trigger: 'blur' }],
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 监听 table 属性,复制给 formData 属性 */
|
||||||
|
watch(
|
||||||
|
() => props.table,
|
||||||
|
(table) => {
|
||||||
|
if (!table) return;
|
||||||
|
formData.value = { ...table };
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
validate: async () => {
|
||||||
|
try {
|
||||||
|
await unref(formRef).validate();
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<a-form ref="formRef" :model="formData" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="表名称" name="tableName">
|
||||||
|
<a-input v-model:value="formData.tableName" placeholder="请输入仓库名称" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="表描述" name="tableComment">
|
||||||
|
<a-input v-model:value="formData.tableComment" placeholder="请输入" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item name="className">
|
||||||
|
<template #label>
|
||||||
|
<span>
|
||||||
|
实体类名称
|
||||||
|
<a-tooltip
|
||||||
|
title="默认去除表名的前缀。如果存在重复,则需要手动添加前缀,避免 MyBatis 报 Alias 重复的问题。"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<QuestionCircleFilled />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<a-input v-model:value="formData.className" placeholder="请输入" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="作者" name="author">
|
||||||
|
<a-input v-model:value="formData.author" placeholder="请输入" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="备注" name="remark">
|
||||||
|
<a-textarea v-model:value="formData.remark" :rows="3" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||||
|
import type { SystemDictTypeApi } from '#/api/system/dict/type';
|
||||||
|
|
||||||
|
import { getSimpleDictTypeList } from '#/api/system/dict/type';
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'InfraCodegenColumInfoForm' });
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
columns?: InfraCodegenApi.CodegenColumn[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const formData = ref<InfraCodegenApi.CodegenColumn[]>([]);
|
||||||
|
const tableHeight = computed(() => document.documentElement.scrollHeight - 350);
|
||||||
|
const dictOptions = ref<SystemDictTypeApi.SystemDictType[]>([]);
|
||||||
|
|
||||||
|
// 表格列定义
|
||||||
|
const columns = [
|
||||||
|
{ title: '字段列名', dataIndex: 'columnName', width: 100 },
|
||||||
|
{ title: '字段描述', dataIndex: 'columnComment', width: 100 },
|
||||||
|
{ title: '物理类型', dataIndex: 'dataType', width: 100 },
|
||||||
|
{ title: 'Java类型', dataIndex: 'javaType', width: 110 },
|
||||||
|
{ title: 'java属性', dataIndex: 'javaField', width: 100 },
|
||||||
|
{ title: '插入', dataIndex: 'createOperation', width: 40 },
|
||||||
|
{ title: '编辑', dataIndex: 'updateOperation', width: 40 },
|
||||||
|
{ title: '列表', dataIndex: 'listOperationResult', width: 40 },
|
||||||
|
{ title: '查询', dataIndex: 'listOperation', width: 40 },
|
||||||
|
{ title: '查询方式', dataIndex: 'listOperationCondition', width: 100 },
|
||||||
|
{ title: '允许空', dataIndex: 'nullable', width: 50 },
|
||||||
|
{ title: '显示类型', dataIndex: 'htmlType', width: 120 },
|
||||||
|
{ title: '字典类型', dataIndex: 'dictType', width: 120 },
|
||||||
|
{ title: '示例', dataIndex: 'example', width: 100 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 下拉框过滤方法
|
||||||
|
const filterOption = (input: string, option: any) => {
|
||||||
|
return option.children[0].toLowerCase().includes(input.toLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 查询字典下拉列表 */
|
||||||
|
const getDictOptions = async () => {
|
||||||
|
dictOptions.value = await getSimpleDictTypeList();
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.columns,
|
||||||
|
(columns) => {
|
||||||
|
if (!columns) return;
|
||||||
|
formData.value = [...columns];
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getDictOptions();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<a-table
|
||||||
|
ref="tableRef"
|
||||||
|
:data-source="formData"
|
||||||
|
:columns="columns"
|
||||||
|
:scroll="{ y: tableHeight }"
|
||||||
|
:pagination="false"
|
||||||
|
row-key="columnId"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, text, record }">
|
||||||
|
<template v-if="column.dataIndex === 'columnComment'">
|
||||||
|
<a-input v-model:value="record.columnComment" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'javaType'">
|
||||||
|
<a-select v-model:value="record.javaType" style="width: 100%">
|
||||||
|
<a-select-option value="Long">Long</a-select-option>
|
||||||
|
<a-select-option value="String">String</a-select-option>
|
||||||
|
<a-select-option value="Integer">Integer</a-select-option>
|
||||||
|
<a-select-option value="Double">Double</a-select-option>
|
||||||
|
<a-select-option value="BigDecimal">BigDecimal</a-select-option>
|
||||||
|
<a-select-option value="LocalDateTime">LocalDateTime</a-select-option>
|
||||||
|
<a-select-option value="Boolean">Boolean</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'javaField'">
|
||||||
|
<a-input v-model:value="record.javaField" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'createOperation'">
|
||||||
|
<a-checkbox v-model:checked="record.createOperation" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'updateOperation'">
|
||||||
|
<a-checkbox v-model:checked="record.updateOperation" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'listOperationResult'">
|
||||||
|
<a-checkbox v-model:checked="record.listOperationResult" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'listOperation'">
|
||||||
|
<a-checkbox v-model:checked="record.listOperation" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'listOperationCondition'">
|
||||||
|
<a-select v-model:value="record.listOperationCondition" style="width: 100%">
|
||||||
|
<a-select-option value="=">=</a-select-option>
|
||||||
|
<a-select-option value="!=">!=</a-select-option>
|
||||||
|
<a-select-option value=">">></a-select-option>
|
||||||
|
<a-select-option value=">=">>=</a-select-option>
|
||||||
|
<a-select-option value="<"><</a-select-option>
|
||||||
|
<a-select-option value="<="><=</a-select-option>
|
||||||
|
<a-select-option value="LIKE">LIKE</a-select-option>
|
||||||
|
<a-select-option value="BETWEEN">BETWEEN</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'nullable'">
|
||||||
|
<a-checkbox v-model:checked="record.nullable" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'htmlType'">
|
||||||
|
<a-select v-model:value="record.htmlType" style="width: 100%">
|
||||||
|
<a-select-option value="input">文本框</a-select-option>
|
||||||
|
<a-select-option value="textarea">文本域</a-select-option>
|
||||||
|
<a-select-option value="select">下拉框</a-select-option>
|
||||||
|
<a-select-option value="radio">单选框</a-select-option>
|
||||||
|
<a-select-option value="checkbox">复选框</a-select-option>
|
||||||
|
<a-select-option value="datetime">日期控件</a-select-option>
|
||||||
|
<a-select-option value="imageUpload">图片上传</a-select-option>
|
||||||
|
<a-select-option value="fileUpload">文件上传</a-select-option>
|
||||||
|
<a-select-option value="editor">富文本控件</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'dictType'">
|
||||||
|
<a-select
|
||||||
|
v-model:value="record.dictType"
|
||||||
|
allow-clear
|
||||||
|
show-search
|
||||||
|
placeholder="请选择"
|
||||||
|
style="width: 100%"
|
||||||
|
:filter-option="filterOption"
|
||||||
|
>
|
||||||
|
<a-select-option v-for="dict in dictOptions" :key="dict.id" :value="dict.type">
|
||||||
|
{{ dict.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'example'">
|
||||||
|
<a-input v-model:value="record.example" />
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,355 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||||
|
import type { SystemMenuApi } from '#/api/system/menu';
|
||||||
|
|
||||||
|
import { QuestionCircleFilled } from '@vben/icons';
|
||||||
|
import { getCodegenTableList } from '#/api/infra/codegen';
|
||||||
|
import { getSimpleMenusList } from '#/api/system/menu';
|
||||||
|
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||||
|
import { onMounted, ref, unref, watch } from 'vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'InfraCodegenGenerateInfoForm' });
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
columns?: InfraCodegenApi.CodegenColumn[];
|
||||||
|
table?: InfraCodegenApi.CodegenTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const formData = ref<InfraCodegenApi.CodegenTable>({} as InfraCodegenApi.CodegenTable);
|
||||||
|
const subColumns = ref<InfraCodegenApi.CodegenColumn[]>([]);
|
||||||
|
const tables = ref<InfraCodegenApi.CodegenTable[]>([]);
|
||||||
|
const menus = ref<SystemMenuApi.SystemMenu[]>([]);
|
||||||
|
|
||||||
|
/** 菜单树选项 */
|
||||||
|
const menuTreeProps = {
|
||||||
|
value: 'id',
|
||||||
|
title: 'name',
|
||||||
|
children: 'children',
|
||||||
|
};
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
templateType: [{ required: true, message: '生成模板不能为空', trigger: 'blur' }],
|
||||||
|
frontType: [{ required: true, message: '前端类型不能为空', trigger: 'blur' }],
|
||||||
|
scene: [{ required: true, message: '生成场景不能为空', trigger: 'blur' }],
|
||||||
|
moduleName: [{ required: true, message: '模块名不能为空', trigger: 'blur' }],
|
||||||
|
businessName: [{ required: true, message: '业务名不能为空', trigger: 'blur' }],
|
||||||
|
className: [{ required: true, message: '类名称不能为空', trigger: 'blur' }],
|
||||||
|
classComment: [{ required: true, message: '类描述不能为空', trigger: 'blur' }],
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 监听 table 属性,复制给 formData 属性 */
|
||||||
|
watch(
|
||||||
|
() => props.table,
|
||||||
|
(table) => {
|
||||||
|
if (!table) return;
|
||||||
|
formData.value = { ...table };
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 子表名称选中 */
|
||||||
|
async function subTableNameSelected(value) {
|
||||||
|
// 使用表ID获取子表的字段
|
||||||
|
const subTableColumns = await getSubCodegenColumns(formData.value.dataSourceConfigId, value);
|
||||||
|
subColumns.value = subTableColumns;
|
||||||
|
// 重置已经选择的子表字段
|
||||||
|
formData.value.subTableFkColumnId = undefined;
|
||||||
|
formData.value.mainTableFKColumnId = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取子表的列 */
|
||||||
|
async function getSubCodegenColumns(dataSourceConfigId: number, tableName: string) {
|
||||||
|
const tableList = await getCodegenTableList(dataSourceConfigId);
|
||||||
|
const subTable = tableList.find((item) => item.tableName === tableName);
|
||||||
|
if (subTable) {
|
||||||
|
// 这里需要实际API来获取子表的列信息
|
||||||
|
// 由于没有完整接口信息,这里仅模拟返回
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
// 获取菜单列表
|
||||||
|
menus.value = await getSimpleMenusList();
|
||||||
|
// 获取表数据,用于主子表选择
|
||||||
|
tables.value = await getCodegenTableList(formData.value.dataSourceConfigId);
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 暴露方法 */
|
||||||
|
defineExpose({
|
||||||
|
validate: async () => {
|
||||||
|
try {
|
||||||
|
await unref(formRef).validate();
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-form ref="formRef" :model="formData" :rules="rules" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="生成模板" name="templateType">
|
||||||
|
<a-select v-model:value="formData.templateType">
|
||||||
|
<a-select-option
|
||||||
|
v-for="dict in getDictOptions(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE, 'number')"
|
||||||
|
:key="dict.value"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="前端类型" name="frontType">
|
||||||
|
<a-select v-model:value="formData.frontType">
|
||||||
|
<a-select-option
|
||||||
|
v-for="dict in getDictOptions(DICT_TYPE.INFRA_CODEGEN_FRONT_TYPE, 'number')"
|
||||||
|
:key="dict.value"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="生成场景" name="scene">
|
||||||
|
<a-select v-model:value="formData.scene">
|
||||||
|
<a-select-option
|
||||||
|
v-for="dict in getDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE, 'number')"
|
||||||
|
:key="dict.value"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="上级菜单">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>分配到指定菜单下,例如 系统管理</template>
|
||||||
|
<template #default>
|
||||||
|
<a-tree-select
|
||||||
|
v-model:value="formData.parentMenuId"
|
||||||
|
:tree-data="menus"
|
||||||
|
:field-names="menuTreeProps"
|
||||||
|
show-search
|
||||||
|
tree-node-filter-prop="title"
|
||||||
|
:tree-checkable="false"
|
||||||
|
:tree-default-expand-all="false"
|
||||||
|
placeholder="请选择系统菜单"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="模块名" name="moduleName">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>模块名,即一级目录,例如 system、infra、tool 等等</template>
|
||||||
|
<template #default>
|
||||||
|
<a-input v-model:value="formData.moduleName" />
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="业务名" name="businessName">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>业务名,即二级目录,例如 user、permission、dict 等等</template>
|
||||||
|
<template #default>
|
||||||
|
<a-input v-model:value="formData.businessName" />
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="类名称" name="className">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>类名称(首字母大写),例如SysUser、SysMenu、SysDictData 等等</template>
|
||||||
|
<template #default>
|
||||||
|
<a-input v-model:value="formData.className" />
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="类描述" name="classComment">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>用作类描述,例如 用户</template>
|
||||||
|
<template #default>
|
||||||
|
<a-input v-model:value="formData.classComment" />
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<!-- 树表信息 -->
|
||||||
|
<template v-if="formData.templateType == 2">
|
||||||
|
<a-divider>树表信息</a-divider>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="父编号字段" name="treeParentColumnId">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>树显示的父编码字段名, 如:parent_Id</template>
|
||||||
|
<template #icon><QuestionCircleFilled /></template>
|
||||||
|
<template #default>
|
||||||
|
<a-select v-model:value="formData.treeParentColumnId" placeholder="请选择" allow-clear>
|
||||||
|
<a-select-option v-for="column in columns" :key="column.id" :value="column.id">
|
||||||
|
{{ column.columnName }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="名称字段" name="treeNameColumnId">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>树节点显示的名称字段,一般是name</template>
|
||||||
|
<template #icon><QuestionCircleFilled /></template>
|
||||||
|
<template #default>
|
||||||
|
<a-select v-model:value="formData.treeNameColumnId" placeholder="请选择" allow-clear>
|
||||||
|
<a-select-option v-for="column in columns" :key="column.id" :value="column.id">
|
||||||
|
{{ column.columnName }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 主子表信息 -->
|
||||||
|
<template v-if="formData.templateType == 3">
|
||||||
|
<a-divider>主子表信息</a-divider>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="关联子表的成员变量名" name="subJoinColumnId">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>子表的成员变量名,默认为子表的名称</template>
|
||||||
|
<template #icon><QuestionCircleFilled /></template>
|
||||||
|
<template #default>
|
||||||
|
<a-input v-model:value="formData.subJoinColumnId" placeholder="请输入" />
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="子表的表名" name="subTableName">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>关联的子表的表名, 如:sys_user_post</template>
|
||||||
|
<template #icon><QuestionCircleFilled /></template>
|
||||||
|
<template #default>
|
||||||
|
<a-select
|
||||||
|
v-model:value="formData.subTableName"
|
||||||
|
placeholder="请选择"
|
||||||
|
allow-clear
|
||||||
|
@change="subTableNameSelected"
|
||||||
|
>
|
||||||
|
<a-select-option v-for="table in tables" :key="table.tableName" :value="table.tableName">
|
||||||
|
{{ `${table.tableName}:${table.tableComment}` }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="子表的类名" name="subTableClassName">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>子表的类名, 如:SysUserPost</template>
|
||||||
|
<template #icon><QuestionCircleFilled /></template>
|
||||||
|
<template #default>
|
||||||
|
<a-input v-model:value="formData.subTableClassName" placeholder="请输入" />
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="子表的外键字段" name="subTableFkColumnId">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>子表的外键字段的编号,对应主表的主键</template>
|
||||||
|
<template #icon><QuestionCircleFilled /></template>
|
||||||
|
<template #default>
|
||||||
|
<a-select v-model:value="formData.subTableFkColumnId" placeholder="请选择" allow-clear>
|
||||||
|
<a-select-option v-for="column in subColumns" :key="column.id" :value="column.id">
|
||||||
|
{{ column.columnName }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="主表的类名" name="mainTableClassName">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>主表的类名, 如:SysUser</template>
|
||||||
|
<template #icon><QuestionCircleFilled /></template>
|
||||||
|
<template #default>
|
||||||
|
<a-input v-model:value="formData.mainTableClassName" placeholder="请输入" />
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="主表的外键字段" name="mainTableFKColumnId">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>子表的外键关联到主表的主键字段编号</template>
|
||||||
|
<template #icon><QuestionCircleFilled /></template>
|
||||||
|
<template #default>
|
||||||
|
<a-select v-model:value="formData.mainTableFKColumnId" placeholder="请选择" allow-clear>
|
||||||
|
<a-select-option v-for="column in columns" :key="column.id" :value="column.id">
|
||||||
|
{{ column.columnName }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="子表的类描述" name="subTableClassComment">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template #title>子表的描述,例如 用户岗位</template>
|
||||||
|
<template #icon><QuestionCircleFilled /></template>
|
||||||
|
<template #default>
|
||||||
|
<a-input v-model:value="formData.subTableClassComment" placeholder="请输入" />
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.form-header {
|
||||||
|
padding-left: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
border-left: 3px solid #1890ff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
|
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||||
|
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
||||||
|
|
||||||
|
import { getDataSourceConfigList } from '#/api/infra/data-source-config';
|
||||||
|
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||||
|
|
||||||
|
import { useAccess } from '@vben/access';
|
||||||
|
|
||||||
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
|
/** 导入数据库表的表单 */
|
||||||
|
export function useImportTableFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'dataSourceConfigId',
|
||||||
|
label: '数据源',
|
||||||
|
component: 'ApiSelect',
|
||||||
|
componentProps: {
|
||||||
|
api: async () => await getDataSourceConfigList(),
|
||||||
|
labelField: 'name',
|
||||||
|
valueField: 'id',
|
||||||
|
placeholder: '请选择数据源',
|
||||||
|
},
|
||||||
|
rules: 'required',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '表名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入表名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'comment',
|
||||||
|
label: '表描述',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入表描述',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 预览代码模态框 */
|
||||||
|
export function usePreviewFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'content',
|
||||||
|
component: 'Textarea',
|
||||||
|
componentProps: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的搜索表单 */
|
||||||
|
export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'tableName',
|
||||||
|
label: '表名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入表名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'tableComment',
|
||||||
|
label: '表描述',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入表描述',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'createTime',
|
||||||
|
label: '创建时间',
|
||||||
|
component: 'RangePicker',
|
||||||
|
componentProps: {
|
||||||
|
...getRangePickerDefaultProps(),
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的字段 */
|
||||||
|
export function useGridColumns<T = InfraCodegenApi.CodegenTable>(
|
||||||
|
onActionClick: OnActionClickFn<T>,
|
||||||
|
dataSourceConfigList: InfraDataSourceConfigApi.InfraDataSourceConfig[],
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'dataSourceConfigId',
|
||||||
|
title: '数据源',
|
||||||
|
minWidth: 120,
|
||||||
|
formatter: ({ cellValue }) => {
|
||||||
|
const config = dataSourceConfigList.find((item) => item.id === cellValue);
|
||||||
|
return config ? config.name : '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'tableName',
|
||||||
|
title: '表名称',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'tableComment',
|
||||||
|
title: '表描述',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'className',
|
||||||
|
title: '实体',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'updateTime',
|
||||||
|
title: '更新时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
width: 300,
|
||||||
|
fixed: 'right',
|
||||||
|
align: 'center',
|
||||||
|
cellRender: {
|
||||||
|
attrs: {
|
||||||
|
nameField: 'tableName',
|
||||||
|
nameTitle: '代码生成',
|
||||||
|
onClick: onActionClick,
|
||||||
|
},
|
||||||
|
name: 'CellOperation',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
code: 'preview',
|
||||||
|
text: '预览',
|
||||||
|
show: hasAccessByCodes(['infra:codegen:preview']),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'edit',
|
||||||
|
show: hasAccessByCodes(['infra:codegen:update']),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'delete',
|
||||||
|
show: hasAccessByCodes(['infra:codegen:delete']),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'sync',
|
||||||
|
text: '同步',
|
||||||
|
show: hasAccessByCodes(['infra:codegen:update']),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'generate',
|
||||||
|
text: '生成代码',
|
||||||
|
show: hasAccessByCodes(['infra:codegen:download']),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||||
|
|
||||||
|
import { Page } from '@vben/common-ui';
|
||||||
|
import { Button, message, Tabs } from 'ant-design-vue';
|
||||||
|
import { ArrowLeftOutlined } from '@vben/icons';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
import { getCodegenTable, updateCodegenTable } from '#/api/infra/codegen';
|
||||||
|
|
||||||
|
import BasicInfo from './components/BasicInfoForm.vue';
|
||||||
|
import ColumnInfo from './components/ColumInfoForm.vue';
|
||||||
|
import GenerateInfo from './components/GenerateInfoForm.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const loading = ref(false);
|
||||||
|
const activeKey = ref('colum');
|
||||||
|
const formData = ref<InfraCodegenApi.CodegenDetail>({
|
||||||
|
table: {},
|
||||||
|
columns: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 表单引用
|
||||||
|
const basicInfoRef = ref();
|
||||||
|
const columnInfoRef = ref();
|
||||||
|
const generateInfoRef = ref();
|
||||||
|
|
||||||
|
// 获取详情数据
|
||||||
|
const getDetail = async () => {
|
||||||
|
const id = Number(route.query.id);
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
formData.value = await getCodegenTable(id);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const submitForm = async () => {
|
||||||
|
if (!formData.value) return;
|
||||||
|
|
||||||
|
// 表单验证
|
||||||
|
await basicInfoRef.value?.validate();
|
||||||
|
await generateInfoRef.value?.validate();
|
||||||
|
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.saving'),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateCodegenTable(formData.value);
|
||||||
|
message.success({
|
||||||
|
content: $t('ui.actionMessage.saveSuccess'),
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存失败', error);
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 返回列表
|
||||||
|
const close = () => {
|
||||||
|
router.push('/infra/codegen');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
getDetail();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<Tabs v-model:activeKey="activeKey">
|
||||||
|
<Tabs.TabPane key="basicInfo" tab="基本信息">
|
||||||
|
<BasicInfo ref="basicInfoRef" :table="formData.table" />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane key="colum" tab="字段信息">
|
||||||
|
<ColumnInfo ref="columnInfoRef" :columns="formData.columns" />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane key="generateInfo" tab="生成信息">
|
||||||
|
<GenerateInfo ref="generateInfoRef" :table="formData.table" :columns="formData.columns" />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
|
<Button type="primary" :loading="loading" @click="submitForm">保存</Button>
|
||||||
|
<Button class="ml-2" @click="close">
|
||||||
|
<ArrowLeftOutlined class="mr-1" />
|
||||||
|
返回
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||||
|
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
||||||
|
|
||||||
|
import { DocAlert } from '#/components/doc-alert';
|
||||||
|
// import ImportTable from './modules/import-table.vue';
|
||||||
|
// import PreviewCode from './modules/preview-code.vue';
|
||||||
|
import { Page } from '@vben/common-ui';
|
||||||
|
import { Plus } from '@vben/icons';
|
||||||
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { deleteCodegenTable, downloadCodegen, getCodegenTablePage, syncCodegenFromDB } from '#/api/infra/codegen';
|
||||||
|
import { getDataSourceConfigList } from '#/api/infra/data-source-config';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const dataSourceConfigList = ref<InfraDataSourceConfigApi.InfraDataSourceConfig[]>([]);
|
||||||
|
|
||||||
|
// const [ImportModal, importModalApi] = useVbenModal({
|
||||||
|
// connectedComponent: ImportTable,
|
||||||
|
// destroyOnClose: true,
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// const [PreviewModal, previewModalApi] = useVbenModal({
|
||||||
|
// connectedComponent: PreviewCode,
|
||||||
|
// destroyOnClose: true,
|
||||||
|
// });
|
||||||
|
|
||||||
|
/** 刷新表格 */
|
||||||
|
function onRefresh() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导入表格 */
|
||||||
|
function onImport() {
|
||||||
|
// importModalApi.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 预览代码 */
|
||||||
|
function onPreview(row: InfraCodegenApi.CodegenTable) {
|
||||||
|
// previewModalApi.setData(row.id).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑表格 */
|
||||||
|
function onEdit(row: InfraCodegenApi.CodegenTable) {
|
||||||
|
router.push(`/infra/codegen/edit?id=${row.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除代码生成配置 */
|
||||||
|
async function onDelete(row: InfraCodegenApi.CodegenTable) {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.deleting', [row.tableName]),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await deleteCodegenTable(row.id);
|
||||||
|
message.success({
|
||||||
|
content: $t('ui.actionMessage.deleteSuccess', [row.tableName]),
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
onRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 同步数据库 */
|
||||||
|
async function onSync(row: InfraCodegenApi.CodegenTable) {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.updating', [row.tableName]),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await syncCodegenFromDB(row.id);
|
||||||
|
message.success({
|
||||||
|
content: $t('ui.actionMessage.updateSuccess', [row.tableName]),
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
onRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 生成代码 */
|
||||||
|
async function onGenerate(row: InfraCodegenApi.CodegenTable) {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: '正在生成代码...',
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const res = await downloadCodegen(row.id);
|
||||||
|
const blob = new Blob([res], { type: 'application/zip' });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = `codegen-${row.className}.zip`;
|
||||||
|
link.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
message.success({
|
||||||
|
content: '代码生成成功',
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表格操作按钮的回调函数 */
|
||||||
|
function onActionClick({ code, row }: OnActionClickParams<InfraCodegenApi.CodegenTable>) {
|
||||||
|
switch (code) {
|
||||||
|
case 'delete': {
|
||||||
|
onDelete(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'edit': {
|
||||||
|
onEdit(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'generate': {
|
||||||
|
onGenerate(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'preview': {
|
||||||
|
onPreview(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'sync': {
|
||||||
|
onSync(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useGridFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useGridColumns(onActionClick, dataSourceConfigList.value),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getCodegenTablePage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<InfraCodegenApi.CodegenTable>,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取数据源配置列表
|
||||||
|
async function initDataSourceConfig() {
|
||||||
|
try {
|
||||||
|
dataSourceConfigList.value = await getDataSourceConfigList();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取数据源配置失败', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
initDataSourceConfig();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height>
|
||||||
|
<DocAlert title="代码生成(单表)" url="https://doc.iocoder.cn/new-feature/" />
|
||||||
|
<DocAlert title="代码生成(树表)" url="https://doc.iocoder.cn/new-feature/tree/" />
|
||||||
|
<DocAlert title="代码生成(主子表)" url="https://doc.iocoder.cn/new-feature/master-sub/" />
|
||||||
|
<DocAlert title="单元测试" url="https://doc.iocoder.cn/unit-test/" />
|
||||||
|
|
||||||
|
<!-- <ImportModal @success="onRefresh" />-->
|
||||||
|
<!-- <PreviewModal />-->
|
||||||
|
<Grid table-title="代码生成列表">
|
||||||
|
<template #toolbar-tools>
|
||||||
|
<Button type="primary" @click="onImport" v-access:code="['infra:codegen:create']">
|
||||||
|
<Plus class="size-5" />
|
||||||
|
{{ $t('ui.actionTitle.create', ['代码生成']) }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { createCodegenList, getSchemaTableList } from '#/api/infra/codegen';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { useImportTableFormSchema } from '../data';
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'success'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const tableList = ref<string[]>([]);
|
||||||
|
|
||||||
|
// 导入表单
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
layout: 'horizontal',
|
||||||
|
schema: useImportTableFormSchema(),
|
||||||
|
showDefaultActions: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 表格实例
|
||||||
|
const [Table, tableApi] = useVbenVxeGrid({
|
||||||
|
columns: [
|
||||||
|
{ field: 'name', title: '表名称', minWidth: 200 },
|
||||||
|
{ field: 'comment', title: '表描述', minWidth: 200 },
|
||||||
|
],
|
||||||
|
toolbarConfig: false,
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'name',
|
||||||
|
},
|
||||||
|
rowSelection: {
|
||||||
|
multiple: true,
|
||||||
|
onChange: (records) => {
|
||||||
|
tableList.value = records.map((item) => item.name);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 模态框实例
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
async onConfirm() {
|
||||||
|
if (tableList.value.length === 0) {
|
||||||
|
message.warning('请选择至少一个表');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const values = await formApi.getValues();
|
||||||
|
modalApi.lock();
|
||||||
|
try {
|
||||||
|
await createCodegenList({
|
||||||
|
dataSourceConfigId: values.dataSourceConfigId,
|
||||||
|
tableNames: tableList.value,
|
||||||
|
});
|
||||||
|
message.success('导入成功');
|
||||||
|
await modalApi.close();
|
||||||
|
emit('success');
|
||||||
|
} finally {
|
||||||
|
modalApi.lock(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
tableList.value = [];
|
||||||
|
tableApi.clearSelection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formApi.reset();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 查询表格数据
|
||||||
|
async function getList() {
|
||||||
|
const values = await formApi.getValues();
|
||||||
|
if (!values.dataSourceConfigId) {
|
||||||
|
message.warning('请选择数据源');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tableApi.loading(true);
|
||||||
|
try {
|
||||||
|
const data = await getSchemaTableList(values);
|
||||||
|
tableApi.loadData(data);
|
||||||
|
} finally {
|
||||||
|
tableApi.loading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置查询
|
||||||
|
async function resetQuery() {
|
||||||
|
formApi.reset();
|
||||||
|
tableApi.clearData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单值变化时重新查询
|
||||||
|
formApi.on('fieldValueChange', (field) => {
|
||||||
|
if (field.name === 'dataSourceConfigId') {
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal title="导入表">
|
||||||
|
<div class="px-4">
|
||||||
|
<Form class="mb-3" @submit="getList" @reset="resetQuery">
|
||||||
|
<template #actions>
|
||||||
|
<a-space>
|
||||||
|
<a-button html-type="submit">搜索</a-button>
|
||||||
|
<a-button html-type="reset">重置</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</Form>
|
||||||
|
<Table height="300px" />
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { CopyOutlined } from '@vben/icons';
|
||||||
|
import { message, Tree } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import { previewCodegen } from '#/api/infra/codegen';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
import hljs from 'highlight.js/lib/core';
|
||||||
|
import java 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';
|
||||||
|
import xml from 'highlight.js/lib/languages/xml';
|
||||||
|
|
||||||
|
import { usePreviewFormSchema } from '../data';
|
||||||
|
|
||||||
|
import 'highlight.js/styles/github.css';
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 文件树类型
|
||||||
|
interface FileNode {
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
parentKey: string;
|
||||||
|
isLeaf?: boolean;
|
||||||
|
children?: FileNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const fileTree = ref<FileNode[]>([]);
|
||||||
|
const previewFiles = ref<InfraCodegenApi.CodegenPreview[]>([]);
|
||||||
|
const activeKey = ref<string>('');
|
||||||
|
const highlightedCode = ref<string>('');
|
||||||
|
|
||||||
|
// 表单实例
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
schema: usePreviewFormSchema(),
|
||||||
|
showActionButtonGroup: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 复制代码
|
||||||
|
const copyCode = async () => {
|
||||||
|
const { copy } = useClipboard();
|
||||||
|
await copy(highlightedCode.value);
|
||||||
|
message.success('复制成功');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 文件节点点击事件
|
||||||
|
const handleNodeClick = (selectedKeys: string[], e: any) => {
|
||||||
|
if (e.node.isLeaf) {
|
||||||
|
activeKey.value = e.node.key;
|
||||||
|
const file = previewFiles.value.find((item) => item.filePath === activeKey.value);
|
||||||
|
if (file) {
|
||||||
|
const lang = file.filePath.split('.').pop() || '';
|
||||||
|
try {
|
||||||
|
highlightedCode.value = hljs.highlight(file.code, { language: lang }).value;
|
||||||
|
} catch {
|
||||||
|
highlightedCode.value = file.code;
|
||||||
|
}
|
||||||
|
formApi.setFieldValue('content', file.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理文件树
|
||||||
|
const handleFiles = (data: InfraCodegenApi.CodegenPreview[]): FileNode[] => {
|
||||||
|
const exists: Record<string, boolean> = {};
|
||||||
|
const files: FileNode[] = [];
|
||||||
|
|
||||||
|
// 处理文件路径
|
||||||
|
for (const item of data) {
|
||||||
|
const paths = item.filePath.split('/');
|
||||||
|
let fullPath = '';
|
||||||
|
|
||||||
|
// 处理Java文件路径
|
||||||
|
const newPaths = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < paths.length) {
|
||||||
|
const path = paths[i];
|
||||||
|
|
||||||
|
if (path === 'java' && i + 1 < paths.length) {
|
||||||
|
newPaths.push(path);
|
||||||
|
|
||||||
|
// 合并包路径
|
||||||
|
let packagePath = '';
|
||||||
|
i++;
|
||||||
|
while (i < paths.length) {
|
||||||
|
const nextPath = paths[i];
|
||||||
|
if (['controller', 'convert', 'dal', 'dataobject', 'enums', 'mysql', 'service', 'vo'].includes(nextPath)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
packagePath = packagePath ? `${packagePath}.${nextPath}` : nextPath;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packagePath) {
|
||||||
|
newPaths.push(packagePath);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newPaths.push(path);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建文件树
|
||||||
|
for (let i = 0; i < newPaths.length; i++) {
|
||||||
|
const oldFullPath = fullPath;
|
||||||
|
fullPath = fullPath.length === 0 ? newPaths[i] : `${fullPath.replaceAll('.', '/')}/${newPaths[i]}`;
|
||||||
|
|
||||||
|
if (exists[fullPath]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
exists[fullPath] = true;
|
||||||
|
files.push({
|
||||||
|
key: fullPath,
|
||||||
|
title: newPaths[i],
|
||||||
|
parentKey: oldFullPath || '/',
|
||||||
|
isLeaf: i === newPaths.length - 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建树形结构
|
||||||
|
const buildTree = (parentKey: string): FileNode[] => {
|
||||||
|
return files
|
||||||
|
.filter((file) => file.parentKey === parentKey)
|
||||||
|
.map((file) => ({
|
||||||
|
...file,
|
||||||
|
children: buildTree(file.key),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return buildTree('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 模态框实例
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
footer: false,
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
previewFiles.value = [];
|
||||||
|
fileTree.value = [];
|
||||||
|
activeKey.value = '';
|
||||||
|
highlightedCode.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取表ID
|
||||||
|
const id = modalApi.getData<number>();
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
// 加载预览数据
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const data = await previewCodegen(id);
|
||||||
|
previewFiles.value = data;
|
||||||
|
fileTree.value = handleFiles(data);
|
||||||
|
|
||||||
|
// 默认选中第一个文件
|
||||||
|
if (data.length > 0) {
|
||||||
|
activeKey.value = data[0].filePath;
|
||||||
|
const lang = data[0].filePath.split('.').pop() || '';
|
||||||
|
try {
|
||||||
|
highlightedCode.value = hljs.highlight(data[0].code, { language: lang }).value;
|
||||||
|
} catch {
|
||||||
|
highlightedCode.value = data[0].code;
|
||||||
|
}
|
||||||
|
formApi.setFieldValue('content', data[0].code);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal title="代码预览">
|
||||||
|
<div class="flex h-[70vh]" v-loading="loading">
|
||||||
|
<!-- 文件树 -->
|
||||||
|
<div class="w-1/3 border-r pr-4">
|
||||||
|
<Tree :selected-keys="[activeKey]" :tree-data="fileTree" @select="handleNodeClick" />
|
||||||
|
</div>
|
||||||
|
<!-- 代码预览 -->
|
||||||
|
<div class="w-2/3 pl-4">
|
||||||
|
<div class="mb-2 flex justify-between">
|
||||||
|
<div class="text-lg font-medium">{{ activeKey.split('/').pop() }}</div>
|
||||||
|
<a-button type="primary" @click="copyCode">
|
||||||
|
<CopyOutlined class="mr-1" />
|
||||||
|
复制代码
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<div class="h-[calc(100%-40px)] overflow-auto">
|
||||||
|
<pre><code v-html="highlightedCode" class="hljs"></code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.hljs {
|
||||||
|
padding: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue