feat: 代码生成器
parent
cd0c7a86e5
commit
b376524980
|
|
@ -45,6 +45,7 @@
|
|||
"dayjs": "catalog:",
|
||||
"pinia": "catalog:",
|
||||
"vue": "catalog:",
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"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兜底路由组成
|
||||
* 无需走权限验证(会一直显示在菜单中) */
|
||||
const routes: RouteRecordRaw[] = [
|
||||
...coreRoutes,
|
||||
...externalRoutes,
|
||||
fallbackNotFoundRoute,
|
||||
];
|
||||
const routes: RouteRecordRaw[] = [...coreRoutes, ...externalRoutes, fallbackNotFoundRoute];
|
||||
|
||||
/** 基本路由列表,这些路由不需要进入权限拦截 */
|
||||
const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
|
||||
|
|
@ -36,12 +32,10 @@ const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
|
|||
const accessRoutes = [...dynamicRoutes, ...staticRoutes];
|
||||
|
||||
// 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(
|
||||
import.meta.glob('../../views/**/*.vue'),
|
||||
)
|
||||
const componentKeys: string[] = Object.keys(import.meta.glob('../../views/**/*.vue'))
|
||||
.filter((item) => !item.includes('/modules/'))
|
||||
.map((v) => {
|
||||
const path = v.replace('../../views/', '/');
|
||||
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 @puhui999:转成 function 方式哈
|
||||
/** 时间段选择器拓展 */
|
||||
export const getRangePickerDefaultProps = () => {
|
||||
export function getRangePickerDefaultProps() {
|
||||
return {
|
||||
showTime: {
|
||||
format: 'HH:mm:ss',
|
||||
defaultValue: [
|
||||
dayjs('00:00:00', 'HH:mm:ss'),
|
||||
dayjs('23:59:59', 'HH:mm:ss'),
|
||||
],
|
||||
defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('23:59:59', 'HH:mm:ss')],
|
||||
},
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
// prettier-ignore
|
||||
ranges: {
|
||||
'今天': [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('month'), dayjs().endOf('day')],
|
||||
'最近 7 天': [
|
||||
dayjs().subtract(7, 'day').startOf('day'),
|
||||
dayjs().endOf('day'),
|
||||
],
|
||||
'最近 30 天': [
|
||||
dayjs().subtract(30, 'day').startOf('day'),
|
||||
dayjs().endOf('day'),
|
||||
],
|
||||
'最近 7 天': [dayjs().subtract(7, 'day').startOf('day'), dayjs().endOf('day')],
|
||||
'最近 30 天': [dayjs().subtract(30, 'day').startOf('day'), dayjs().endOf('day')],
|
||||
},
|
||||
transformDateFunc: (dates: any) => {
|
||||
if (dates && dates.length === 2) {
|
||||
|
|
@ -40,4 +28,4 @@ export const getRangePickerDefaultProps = () => {
|
|||
return {};
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const dictStore = useDictStore();
|
|||
*/
|
||||
function getDictLabel(dictType: string, value: any) {
|
||||
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 字典类型
|
||||
* @returns 字典数组
|
||||
*/
|
||||
function getDictOptions(
|
||||
dictType: string,
|
||||
valueType: 'boolean' | 'number' | 'string' = 'string',
|
||||
) {
|
||||
function getDictOptions(dictType: string, valueType: 'boolean' | 'number' | 'string' = 'string') {
|
||||
const dictOpts = dictStore.getDictOptions(dictType);
|
||||
const dictOptions: DefaultOptionType = [];
|
||||
if (dictOpts.length > 0) {
|
||||
|
|
@ -72,137 +69,138 @@ function getDictOptions(
|
|||
}
|
||||
|
||||
enum DICT_TYPE {
|
||||
AI_GENERATE_MODE = 'ai_generate_mode', // AI 生成模式
|
||||
AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态
|
||||
AI_MUSIC_STATUS = 'ai_music_status', // AI 音乐状态
|
||||
// ========== AI - 人工智能模块 ==========
|
||||
AI_PLATFORM = 'ai_platform', // AI 平台
|
||||
USER_TYPE = 'user_type',
|
||||
COMMON_STATUS = 'common_status',
|
||||
TERMINAL = 'terminal', // 终端
|
||||
DATE_INTERVAL = 'date_interval', // 数据间隔
|
||||
|
||||
// ========== 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_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_TASK_STATUS = 'bpm_task_status',
|
||||
BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type',
|
||||
BPM_PROCESS_LISTENER_TYPE = 'bpm_process_listener_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_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_STATUS = 'brokerage_record_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_AUDIT_STATUS = 'crm_audit_status', // CRM 审批状态
|
||||
CRM_BIZ_TYPE = 'crm_biz_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_LEVEL = 'crm_customer_level', // 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_PERMISSION_LEVEL = 'crm_permission_level', // CRM 数据权限的级别
|
||||
CRM_PRODUCT_UNIT = 'crm_product_unit', // CRM 产品单位
|
||||
CRM_RECEIVABLE_RETURN_TYPE = 'crm_receivable_return_type', // CRM 回款的还款方式
|
||||
DATE_INTERVAL = 'date_interval', // 数据间隔
|
||||
CRM_FOLLOW_UP_TYPE = 'crm_follow_up_type', // CRM 跟进方式
|
||||
|
||||
// ========== ERP - 企业资源计划模块 ==========
|
||||
ERP_AUDIT_STATUS = 'erp_audit_status', // ERP 审批状态
|
||||
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',
|
||||
INFRA_CONFIG_TYPE = 'infra_config_type',
|
||||
// ========== AI - 人工智能模块 ==========
|
||||
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_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 数据校验级别
|
||||
MEMBER_EXPERIENCE_BIZ_TYPE = 'member_experience_biz_type', // 会员经验业务类型
|
||||
// ========== Member 会员模块 ==========
|
||||
MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型
|
||||
// ========== MP 模块 ==========
|
||||
MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型
|
||||
|
||||
MP_MESSAGE_TYPE = 'mp_message_type', // 消息类型
|
||||
// ========== PAY 模块 ==========
|
||||
PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型
|
||||
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',
|
||||
IOT_PRODUCT_STATUS = 'iot_product_status', // IOT 产品状态
|
||||
IOT_PRODUCT_DEVICE_TYPE = 'iot_product_device_type', // IOT 产品设备类型
|
||||
IOT_DATA_FORMAT = 'iot_data_format', // IOT 数据格式
|
||||
IOT_PROTOCOL_TYPE = 'iot_protocol_type', // IOT 接入网关协议
|
||||
IOT_DEVICE_STATUS = 'iot_device_status', // IOT 设备状态
|
||||
IOT_PRODUCT_FUNCTION_TYPE = 'iot_product_function_type', // IOT 产品功能类型
|
||||
IOT_DATA_TYPE = 'iot_data_type', // IOT 数据类型
|
||||
IOT_UNIT_TYPE = 'iot_unit_type', // IOT 单位类型
|
||||
IOT_RW_TYPE = 'iot_rw_type', // IOT 读写类型
|
||||
}
|
||||
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