diff --git a/src/components/SmartTable/index.ts b/src/components/SmartTable/index.ts index 5b4adc37c..a9cbb6ab0 100644 --- a/src/components/SmartTable/index.ts +++ b/src/components/SmartTable/index.ts @@ -16,3 +16,4 @@ export * from './src/types/SmartTableColumnType'; export * from './src/types/SmartTableAuthType'; export * from './src/types/SmartTableToolbarConfigType'; export * from './src/utils/TableCommon'; +export * from './src/hooks/useVxeTableSortable'; diff --git a/src/components/SmartTable/src/hooks/useVxeTableSortable.ts b/src/components/SmartTable/src/hooks/useVxeTableSortable.ts new file mode 100644 index 000000000..4ffaa4ec7 --- /dev/null +++ b/src/components/SmartTable/src/hooks/useVxeTableSortable.ts @@ -0,0 +1,40 @@ +import { onMounted, onUnmounted } from 'vue'; +import type { Ref } from 'vue'; + +import Sortable from 'sortablejs'; + +/** + * vxe支持行拖动 + * @param tableRef + * @param handle css + * @param tableData 表格数据 + */ +export const useVxeTableSortable = (tableRef: Ref, handle: string, tableData: Ref) => { + let sortable: any = null; + const handleRowDrop = () => { + if (sortable !== null) { + sortable.destroy(); + } + sortable = Sortable.create( + tableRef.value.$el.querySelector('.body--wrapper>.vxe-table--body tbody'), + { + handle: handle, + animation: 150, + ghostClass: 'blue-background-class', + onEnd: ({ newIndex, oldIndex }: any) => { + const currentRow = tableData.value.splice(oldIndex, 1)[0]; + tableData.value.splice(newIndex, 0, currentRow); + }, + }, + ); + }; + onUnmounted(() => { + if (sortable !== null) { + sortable.destroy(); + } + }); + onMounted(handleRowDrop); + return { + handleRowDrop, + }; +}; diff --git a/src/modules/codeGenerator/components/template/TemplateGroup.vue b/src/modules/codeGenerator/components/template/TemplateGroup.vue new file mode 100644 index 000000000..4b132b906 --- /dev/null +++ b/src/modules/codeGenerator/components/template/TemplateGroup.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/src/modules/codeGenerator/constants/Constants.ts b/src/modules/codeGenerator/constants/Constants.ts new file mode 100644 index 000000000..c2e452762 --- /dev/null +++ b/src/modules/codeGenerator/constants/Constants.ts @@ -0,0 +1,8 @@ +export const extensionLanguageMap: { [index: string]: string } = { + 'text/x-java': 'java', + xml: 'xml', + javascript: 'js', + html: 'html', + 'text/x-vue': 'vue', + typescript: 'ts', +}; diff --git a/src/modules/codeGenerator/constants/DatabaseConstants.ts b/src/modules/codeGenerator/constants/DatabaseConstants.ts new file mode 100644 index 000000000..ffdee6651 --- /dev/null +++ b/src/modules/codeGenerator/constants/DatabaseConstants.ts @@ -0,0 +1,13 @@ +/** + * 模板类型 + */ +export const TemplateType: any = { + TEMPLATE_CODE: { + value: 'TEMPLATE_CODE', + label: 'generator.views.template.label.templateType.templateCode', + }, + TEMPLATE_DB_DICT: { + value: 'TEMPLATE_DB_DICT', + label: 'generator.views.template.label.templateType.templateDbDict', + }, +}; diff --git a/src/modules/codeGenerator/router/CodeBasicRouter.ts b/src/modules/codeGenerator/router/CodeBasicRouter.ts new file mode 100644 index 000000000..43dedb7f7 --- /dev/null +++ b/src/modules/codeGenerator/router/CodeBasicRouter.ts @@ -0,0 +1,11 @@ +import { AppRouteRecordRaw } from '@/router/types'; + +export const CREATE_CODE_ROUTER: AppRouteRecordRaw = { + path: '/codeCreateView', + name: 'CodeCreateView', + meta: { + title: '生成代码', + }, + component: () => import('/@/modules/codeGenerator/views/codeCreate/CodeCreateView.vue'), + props: (route) => route.query, +}; diff --git a/src/modules/codeGenerator/views/codeCreate/CodeCreateSupport.ts b/src/modules/codeGenerator/views/codeCreate/CodeCreateSupport.ts new file mode 100644 index 000000000..b8105984d --- /dev/null +++ b/src/modules/codeGenerator/views/codeCreate/CodeCreateSupport.ts @@ -0,0 +1,11 @@ +/** + * 扩展名类型映射 + */ +export const extensionLanguageMap: any = { + 'text/x-java': 'java', + xml: 'xml', + javascript: 'js', + html: 'html', + 'text/x-vue': 'vue', + typescript: 'ts', +}; diff --git a/src/modules/codeGenerator/views/codeCreate/CodeCreateView.vue b/src/modules/codeGenerator/views/codeCreate/CodeCreateView.vue new file mode 100644 index 000000000..232733d4e --- /dev/null +++ b/src/modules/codeGenerator/views/codeCreate/CodeCreateView.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeCreate/CodeCreateViewOld.vue b/src/modules/codeGenerator/views/codeCreate/CodeCreateViewOld.vue new file mode 100644 index 000000000..bf065686a --- /dev/null +++ b/src/modules/codeGenerator/views/codeCreate/CodeCreateViewOld.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeDesign/CodeDesignPage.api.ts b/src/modules/codeGenerator/views/codeDesign/CodeDesignPage.api.ts new file mode 100644 index 000000000..225197590 --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/CodeDesignPage.api.ts @@ -0,0 +1,30 @@ +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +enum Api { + queryDbTable = 'db/connection/queryDbTable', + getConfigById = 'db/code/main/getConfigById', + saveConfig = 'db/code/main/save', +} + +/** + * 查询数据库信息 + * @param connectionId + * @param tableName + */ +export const queryDbTableApi = (connectionId: number, tableName: number) => + defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: Api.queryDbTable, + data: { dbConnectionId: connectionId, tableName }, + }); + +export const getConfigByIdApi = (configId: number) => + defHttp.post({ service: ApiServiceEnum.SMART_CODE, url: Api.getConfigById, data: configId }); + +export const saveConfigApi = (model: Recordable) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: Api.saveConfig, + data: model, + }); +}; diff --git a/src/modules/codeGenerator/views/codeDesign/CodeDesignPage.config.ts b/src/modules/codeGenerator/views/codeDesign/CodeDesignPage.config.ts new file mode 100644 index 000000000..2636b603f --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/CodeDesignPage.config.ts @@ -0,0 +1,330 @@ +import type { FormSchema } from '@/components/Form'; + +type ButtonType = + | 'SEARCH' + | 'RESET' + | 'ADD' + | 'EDIT' + | 'DELETE' + | 'EXCEL_IMPORT' + | 'EXCEL_EXPORT' + | 'COLUMN_SETTING' + | 'ZOOM' + | 'REFRESH' + | 'SHOW_SEARCH' + | 'PRINT'; + +interface Button { + key: ButtonType; + value: string; +} + +const tableTypeList = [ + { + label: 'generator.views.code.title.tableType.single', + value: '10', + }, + { + label: 'generator.views.code.title.tableType.main', + value: '20', + }, + { + label: 'generator.views.code.title.tableType.addendum', + value: '30', + }, +]; + +const yesNoList = [ + { + label: 'Yes', + value: true, + }, + { + label: 'No', + value: false, + }, +]; + +export const formSchemas = (t: Function): FormSchema[] => { + return [ + { + label: '', + field: 'systemId', + component: 'Input', + show: false, + }, + { + label: t('generator.views.code.table.connectionName'), + field: 'connectionId', + slot: 'addEditForm-connectionId', + required: true, + }, + { + label: t('generator.views.code.table.tableName'), + field: 'tableName', + component: 'Input', + required: true, + }, + { + label: t('generator.views.code.table.configName'), + field: 'configName', + component: 'Input', + required: true, + }, + { + label: t('generator.views.code.table.type'), + field: 'type', + component: 'Select', + defaultValue: '10', + componentProps: { + options: tableTypeList.map((item) => ({ label: t(item.label), value: item.value })), + }, + }, + // ------------ 第二行 --------------------- + { + label: t('generator.views.code.title.showCheckBox'), + field: 'showCheckbox', + component: 'RadioGroup', + defaultValue: true, + componentProps: { + options: yesNoList, + }, + }, + { + label: t('generator.views.code.title.isPage'), + field: 'page', + component: 'RadioGroup', + defaultValue: true, + componentProps: { + options: yesNoList, + }, + }, + { + label: t('generator.views.code.title.invented'), + field: 'invented', + component: 'RadioGroup', + defaultValue: false, + componentProps: { + options: yesNoList, + }, + }, + { + label: t('generator.views.code.title.columnSort'), + field: 'columnSort', + component: 'RadioGroup', + defaultValue: false, + componentProps: { + options: yesNoList, + }, + }, + // ------------ 第三行 --------------------- + { + label: t('generator.views.code.title.leftButton'), + field: 'leftButtonList', + component: 'Select', + defaultValue: ['ADD', 'DELETE'], + componentProps: { + mode: 'multiple', + options: letButtonList.map((item) => ({ + label: item.value, + value: item.key, + })), + }, + }, + { + label: t('generator.views.code.title.rightButton'), + field: 'rightButtonList', + component: 'Select', + defaultValue: ['ZOOM', 'REFRESH', 'SHOW_SEARCH', 'COLUMN_SETTING'], + componentProps: { + mode: 'multiple', + options: rightButtonList.map((item) => ({ + label: item.value, + value: item.key, + })), + }, + }, + { + label: t('generator.views.code.title.rowButtonType.title'), + field: 'rowButtonType', + component: 'Select', + defaultValue: 'NONE', + componentProps: { + options: rowButtonTypeList(t), + }, + }, + { + label: t('generator.views.code.title.rowButtonList'), + field: 'rowButtonList', + component: 'Select', + componentProps: { + mode: 'multiple', + options: rowButtonList.map((item) => ({ + label: item.value, + value: item.key, + })), + }, + }, + // ------------ 第四行 --------------------- + { + label: t('generator.views.code.title.formColNum'), + field: 'formColNum', + component: 'Select', + defaultValue: 1, + componentProps: { + options: columnNumList(t, false), + }, + }, + { + label: t('generator.views.code.title.searchColNum'), + field: 'searchColNum', + component: 'Select', + defaultValue: 0, + componentProps: { + options: columnNumList(t), + }, + }, + { + label: t('common.table.remark'), + field: 'remark', + component: 'Input', + }, + { + label: t('generator.views.code.title.i18nPrefix'), + field: 'i18nPrefix', + component: 'Input', + }, + // ------------ 第五行 --------------------- + { + label: t('generator.views.code.title.relateTable'), + field: 'addendumTableList', + defaultValue: [], + slot: 'addEditForm-RelateTable', + }, + { + label: '', + field: 'id', + slot: 'addEditForm-syncTable', + }, + ]; +}; + +/** + * 左侧按钮列表 + */ +const letButtonList: Button[] = [ + { + key: 'SEARCH', + value: '搜索', + }, + { + key: 'RESET', + value: '重置', + }, + { + key: 'ADD', + value: '添加', + }, + { + key: 'EDIT', + value: '修改', + }, + { + key: 'DELETE', + value: '删除', + }, +]; + +const rightButtonList: Button[] = [ + { + key: 'EXCEL_IMPORT', + value: 'Excel导入', + }, + { + key: 'EXCEL_EXPORT', + value: 'Excel导出', + }, + { + key: 'COLUMN_SETTING', + value: '列配置', + }, + { + key: 'ZOOM', + value: '放大缩小', + }, + { + key: 'REFRESH', + value: '刷新', + }, + { + key: 'SHOW_SEARCH', + value: '显示搜索', + }, + { + key: 'PRINT', + value: '打印', + }, +]; + +/** + * 行按钮 + */ +const rowButtonList = [ + { + key: 'EDIT', + value: '修改', + }, + { + key: 'DELETE', + value: '删除', + }, +]; + +const columnNumList = (t: Function, hasZeroColumn = true) => { + const column = [ + { + value: 1, + label: t('generator.views.code.title.colNum.one'), + }, + { + value: 2, + label: t('generator.views.code.title.colNum.two'), + }, + { + value: 3, + label: t('generator.views.code.title.colNum.three'), + }, + { + value: 4, + label: t('generator.views.code.title.colNum.four'), + }, + ]; + if (hasZeroColumn) { + return [ + { + value: 0, + label: t('generator.views.design.title.colNum.zero'), + }, + ].concat(column); + } + return column; +}; + +const rowButtonTypeList = (t: Function) => [ + { + label: t('generator.views.code.title.rowButtonType.none'), + value: 'NONE', + }, + { + label: t('generator.views.code.title.rowButtonType.single'), + value: 'SINGLE', + }, + { + label: t('generator.views.code.title.rowButtonType.more'), + value: 'MORE', + }, + { + label: t('generator.views.code.title.rowButtonType.text'), + value: 'TEXT', + }, +]; diff --git a/src/modules/codeGenerator/views/codeDesign/CodeDesignPage.vue b/src/modules/codeGenerator/views/codeDesign/CodeDesignPage.vue new file mode 100644 index 000000000..536214e02 --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/CodeDesignPage.vue @@ -0,0 +1,250 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeDesign/CodeDesignPageHook.ts b/src/modules/codeGenerator/views/codeDesign/CodeDesignPageHook.ts new file mode 100644 index 000000000..475dbef60 --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/CodeDesignPageHook.ts @@ -0,0 +1,142 @@ +import { computed, createVNode, Ref, ref, unref } from 'vue'; + +import { queryDbTableApi, saveConfigApi } from './CodeDesignPage.api'; +import { message, Modal } from 'ant-design-vue'; +import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; + +/** + * 加载数据库数据 + */ +export const useLoadDbData = (validate: Function) => { + // 数据库数据加载状态 + const dbDataLoading = ref(false); + const dbDataRef = ref({}); + const isSyncRef = ref(false); + + /** + * 表格table计算属性 + */ + const computedTableData = computed(() => { + const dbData = unref(dbDataRef); + if (!dbData.tableName) { + return []; + } + const primaryKeyList = dbData.primaryKeyList || []; + const baseColumnList = dbData.baseColumnList || []; + return [...primaryKeyList, ...baseColumnList]; + }); + + const handleSyncTableData = async () => { + const { connectionId, tableName } = await validate(); + try { + dbDataLoading.value = true; + dbDataRef.value = await queryDbTableApi(connectionId, tableName); + isSyncRef.value = true; + } finally { + dbDataLoading.value = false; + } + }; + + return { + dbDataLoading, + dbDataRef, + computedTableData, + isSyncRef, + handleSyncTableData, + }; +}; + +/** + * 保存操作hook + */ +export const useSaveConfig = ( + t: Function, + isSync: Ref, + validate: Function, + dbDataRef: Ref, + afterSave?: Function, +) => { + const pageTableSettingRef = ref(); + const pageSearchSettingRef = ref(); + const pageFormSettingRef = ref(); + + const saveLoading = ref(false); + + const handleSave = () => { + if (!unref(isSync)) { + message.warn(t('generator.views.code.validate.syncTable')); + return false; + } + if (!unref(pageTableSettingRef)) { + message.warn(t('generator.views.code.validate.tableSetting')); + return false; + } + if (!unref(pageFormSettingRef)) { + message.warn(t('generator.views.code.validate.formSetting')); + return false; + } + // 搜索配置实体 + if (!unref(pageSearchSettingRef)) { + message.warn(t('generator.views.code.validate.searchSetting')); + return false; + } + // 验证必填字段是否设置表单 + const pageFormSettingData = unref(pageFormSettingRef).getData() as Array; + const nonNullField: Array = []; + pageFormSettingData.forEach((item) => { + if (item.nullable === 0 && (item.visible === false || item.used === false)) { + nonNullField.push(item.columnName); + } + }); + if (nonNullField.length > 0) { + Modal.confirm({ + title: t('common.notice.confirmSave'), + icon: createVNode(ExclamationCircleOutlined), + content: t('generator.views.code.message.saveConfirmContent', nonNullField.join(',')), + onCancel() { + return false; + }, + onOk() { + doSave(); + }, + }); + } else { + doSave(); + } + }; + + const doSave = async () => { + const formModel = await validate(); + const dbData = unref(dbDataRef); + const saveData = { + ...formModel, + codePageConfigList: unref(pageTableSettingRef).getData(), + codeFormConfigList: unref(pageFormSettingRef).getData(), + codeSearchConfigList: unref(pageSearchSettingRef).getData(), + className: dbData.className, + remarks: dbData.remarks, + }; + try { + saveLoading.value = true; + const configId = await saveConfigApi(saveData); + afterSave && afterSave(configId); + } catch (e: any) { + if (e.code === 400) { + e.data.forEach((item: string) => { + message.error(item); + }); + } + return false; + } finally { + saveLoading.value = false; + } + }; + + return { + handleSave, + pageTableSettingRef, + pageSearchSettingRef, + pageFormSettingRef, + saveLoading, + }; +}; diff --git a/src/modules/codeGenerator/views/codeDesign/componenets/DatabaseSelect/DatabaseSelect.vue b/src/modules/codeGenerator/views/codeDesign/componenets/DatabaseSelect/DatabaseSelect.vue new file mode 100644 index 000000000..0f946459c --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/componenets/DatabaseSelect/DatabaseSelect.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeDesign/componenets/PageAddendumTableChoseModal.vue b/src/modules/codeGenerator/views/codeDesign/componenets/PageAddendumTableChoseModal.vue new file mode 100644 index 000000000..753209a95 --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/componenets/PageAddendumTableChoseModal.vue @@ -0,0 +1,210 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeDesign/componenets/PageFromSetting/FormRuleSetModal.vue b/src/modules/codeGenerator/views/codeDesign/componenets/PageFromSetting/FormRuleSetModal.vue new file mode 100644 index 000000000..5de902a4d --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/componenets/PageFromSetting/FormRuleSetModal.vue @@ -0,0 +1,222 @@ + + + + + + diff --git a/src/modules/codeGenerator/views/codeDesign/componenets/PageFromSetting/PageFormSetting.vue b/src/modules/codeGenerator/views/codeDesign/componenets/PageFromSetting/PageFormSetting.vue new file mode 100644 index 000000000..bb0006ec8 --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/componenets/PageFromSetting/PageFormSetting.vue @@ -0,0 +1,362 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeDesign/componenets/PageSearchSetting/PageSearchSetting.vue b/src/modules/codeGenerator/views/codeDesign/componenets/PageSearchSetting/PageSearchSetting.vue new file mode 100644 index 000000000..5f4c8326a --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/componenets/PageSearchSetting/PageSearchSetting.vue @@ -0,0 +1,380 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeDesign/componenets/PageSettingSupport.ts b/src/modules/codeGenerator/views/codeDesign/componenets/PageSettingSupport.ts new file mode 100644 index 000000000..503ac00bc --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/componenets/PageSettingSupport.ts @@ -0,0 +1,174 @@ +import { ref, watch } from 'vue'; +import type { Ref } from 'vue'; +import { useModal } from '@/components/Modal'; + +/** + * 空间列表 + */ +export const controlList = [ + { + key: 'INPUT', + value: 'generator.views.code.title.controlList.input', + }, + { + key: 'TEXTAREA', + value: 'generator.views.code.title.controlList.textarea', + }, + { + key: 'NUMBER', + value: 'generator.views.code.title.controlList.number', + }, + { + key: 'PASSWORD', + value: 'generator.views.code.title.controlList.password', + }, + { + key: 'SELECT', + value: 'generator.views.code.title.controlList.select', + }, + { + key: 'TRANSFER', + value: 'generator.views.code.title.controlList.transfer', + }, + { + key: 'SELECT_TABLE', + value: 'generator.views.code.title.controlList.selectTable', + }, + { + key: 'RADIO', + value: 'generator.views.code.title.controlList.radio', + }, + { + key: 'CHECKBOX', + value: 'generator.views.code.title.controlList.checkbox', + }, + { + key: 'SWITCH_TYPE', + value: 'generator.views.code.title.controlList.switch_type', + }, + { + key: 'DATE', + value: 'generator.views.code.title.controlList.date', + }, + { + key: 'TIME', + value: 'generator.views.code.title.controlList.time', + }, + { + key: 'DATETIME', + value: 'generator.views.code.title.controlList.datetime', + }, + { + key: 'FILE', + value: 'generator.views.code.title.controlList.file', + }, + { + key: 'DATA_DICT', + value: 'generator.views.design.title.controlList.dataDict', + }, + { + key: 'CATEGORY_DICT', + value: 'generator.views.design.title.controlList.categoryDict', + }, +]; + +/** + * rule列表 + */ +const ruleList = [ + { + value: 'NOT_EMPTY', + label: 'generator.views.code.title.ruleList.notEmpty', + }, + { + value: 'PHONE', + label: 'generator.views.code.title.ruleList.PHONE', + }, + { + value: 'EMAIL', + label: 'generator.views.code.title.ruleList.EMAIL', + }, + { + value: 'NUMBER', + label: 'generator.views.code.title.ruleList.NUMBER', + }, + { + value: 'REGEXP', + label: 'generator.views.code.title.ruleList.REGEXP', + }, +]; + +export const getRuleList = (t: Function): Recordable[] => { + return ruleList.map((item) => { + return { + ...item, + label: t(item.label), + }; + }); +}; + +/** + * 查询标识列表 + */ +export const searchSymbolList = [ + '=', + 'like', + '>', + '>=', + '<', + '<=', + 'in', + 'notIn', + 'notLike', + 'likeLeft', + 'likeRight', +]; + +/** + * table header checkbox + * @param tableData + * @param field + * @param defaultValue + */ +export const vueTableHeaderCheckboxSupport = ( + tableData: Ref, + field: string, + defaultValue = true, +) => { + const checked = ref(defaultValue); + watch(checked, () => { + tableData.value.forEach((item: any) => { + item[field] = checked.value; + }); + }); + return { + checked, + }; +}; + +/** + * 下拉表格支持 + */ +export const vueChoseSelectTableSupport = (currentRow: Ref) => { + const [registerSelectTableModal, { openModal: openSelectTableModal }] = useModal(); + /** + * 显示列选择 + * @param row + */ + const handleShowChoseSelectTable = (row: any) => { + currentRow.value = row; + openSelectTableModal(true, {}); + }; + /** + * 选择表格后 + * @param tableList + */ + const handleChoseTable = (tableList: Array) => { + currentRow.value.selectTableList = tableList; + }; + return { + registerSelectTableModal, + handleShowChoseSelectTable, + handleChoseTable, + }; +}; diff --git a/src/modules/codeGenerator/views/codeDesign/componenets/PageTableSetting/PageTableSetting.vue b/src/modules/codeGenerator/views/codeDesign/componenets/PageTableSetting/PageTableSetting.vue new file mode 100644 index 000000000..655a15dc0 --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/componenets/PageTableSetting/PageTableSetting.vue @@ -0,0 +1,331 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeDesign/componenets/TableFieldTable/TableFieldTable.vue b/src/modules/codeGenerator/views/codeDesign/componenets/TableFieldTable/TableFieldTable.vue new file mode 100644 index 000000000..25b603d56 --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/componenets/TableFieldTable/TableFieldTable.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeDesign/lang/en_US.ts b/src/modules/codeGenerator/views/codeDesign/lang/en_US.ts new file mode 100644 index 000000000..350891bf6 --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/lang/en_US.ts @@ -0,0 +1,20 @@ +export default { + trans: true, + key: 'generator.views.design', + data: { + title: { + controlList: { + dataDict: 'Data dict', + categoryDict: 'Category dict', + }, + colNum: { + zero: 'inline', + }, + }, + formSetting: { + title: { + tableName: 'Model class', + }, + }, + }, +}; diff --git a/src/modules/codeGenerator/views/codeDesign/lang/zh_CN.ts b/src/modules/codeGenerator/views/codeDesign/lang/zh_CN.ts new file mode 100644 index 000000000..0c44b2dac --- /dev/null +++ b/src/modules/codeGenerator/views/codeDesign/lang/zh_CN.ts @@ -0,0 +1,20 @@ +export default { + trans: true, + key: 'generator.views.design', + data: { + title: { + controlList: { + dataDict: '数据字典', + categoryDict: '分类字典', + }, + colNum: { + zero: '单行', + }, + }, + formSetting: { + title: { + tableName: '实体类名', + }, + }, + }, +}; diff --git a/src/modules/codeGenerator/views/codeList/CodeListView.api.ts b/src/modules/codeGenerator/views/codeList/CodeListView.api.ts new file mode 100644 index 000000000..b6515167f --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/CodeListView.api.ts @@ -0,0 +1,16 @@ +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +enum Api { + listBySystem = 'db/code/main/listBySystem', + delete = 'db/code/main/batchDeleteById', +} + +export const listBySystemApi = (parameter) => + defHttp.post({ service: ApiServiceEnum.SMART_CODE, url: Api.listBySystem, data: parameter }); + +export const deleteApi = (data) => + defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: Api.delete, + data: data.map((item: any) => item.id), + }); diff --git a/src/modules/codeGenerator/views/codeList/CodeListView.config.tsx b/src/modules/codeGenerator/views/codeList/CodeListView.config.tsx new file mode 100644 index 000000000..a19278e11 --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/CodeListView.config.tsx @@ -0,0 +1,136 @@ +import type { SmartColumn, SmartSearchFormSchema } from '@/components/SmartTable'; + +const tableTypeList = [ + { + label: 'generator.views.code.title.tableType.single', + value: '10', + color: 'green', + }, + { + label: 'generator.views.code.title.tableType.main', + value: '20', + color: 'blue', + }, + { + label: 'generator.views.code.title.tableType.addendum', + value: '30', + color: 'purple', + }, +]; + +export const tableColumns = (t: Function): SmartColumn[] => { + return [ + { + type: 'checkbox', + width: 60, + fixed: 'left', + }, + { + title: '{generator.views.code.table.connectionName}', + field: 'connectionName', + width: 160, + fixed: 'left', + }, + { + title: '{generator.views.code.table.configName}', + field: 'configName', + width: 160, + fixed: 'left', + }, + { + title: '{generator.views.code.table.tableName}', + field: 'tableName', + width: 160, + fixed: 'left', + }, + { + title: '{generator.views.code.table.type}', + field: 'type', + width: 120, + slots: { + default: ({ row }) => { + const value = row.type; + if (value) { + const filterList = tableTypeList.filter((item) => item.value === value); + if (filterList.length > 0) { + const data = filterList[0]; + return {t(data.label)}; + } + } + return ''; + }, + }, + }, + { + title: '{generator.views.code.table.remarks}', + field: 'remarks', + minWidth: 200, + }, + { + title: '{common.table.remark}', + field: 'remark', + minWidth: 200, + }, + { + title: '{common.table.createTime}', + field: 'createTime', + width: 165, + sortable: true, + }, + { + title: '{common.table.createUser}', + field: 'createBy', + width: 120, + }, + { + title: '{common.table.updateTime}', + field: 'updateTime', + width: 165, + sortable: true, + }, + { + title: '{common.table.updateUser}', + field: 'updateBy', + width: 120, + }, + { + title: '{common.table.operation}', + field: 'operation', + width: 140, + fixed: 'right', + slots: { + default: 'table-operation', + }, + }, + ]; +}; + +export const searchFormColumns = (t: Function): SmartSearchFormSchema[] => { + return [ + { + field: 'tableName', + label: '', + component: 'Input', + componentProps: { + placeholder: t('generator.views.code.table.tableName'), + }, + }, + { + field: 'type', + label: '', + component: 'Select', + componentProps: { + style: { + width: '100px', + }, + placeholder: t('generator.views.code.table.type'), + options: tableTypeList.map((item) => { + return { + ...item, + label: t(item.label), + }; + }), + }, + }, + ]; +}; diff --git a/src/modules/codeGenerator/views/codeList/CodeListView.vue b/src/modules/codeGenerator/views/codeList/CodeListView.vue new file mode 100644 index 000000000..c1f0d96de --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/CodeListView.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeList/components/CodeCreateForm.vue b/src/modules/codeGenerator/views/codeList/components/CodeCreateForm.vue new file mode 100644 index 000000000..17ac96ce3 --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/components/CodeCreateForm.vue @@ -0,0 +1,187 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeList/components/CodeCreateModal.vue b/src/modules/codeGenerator/views/codeList/components/CodeCreateModal.vue new file mode 100644 index 000000000..092a6f619 --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/components/CodeCreateModal.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeList/components/CodeListAddEditModal.vue b/src/modules/codeGenerator/views/codeList/components/CodeListAddEditModal.vue new file mode 100644 index 000000000..6edd88bc6 --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/components/CodeListAddEditModal.vue @@ -0,0 +1,9 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeList/components/TemplateSelectTable.vue b/src/modules/codeGenerator/views/codeList/components/TemplateSelectTable.vue new file mode 100644 index 000000000..9ec62684c --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/components/TemplateSelectTable.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/src/modules/codeGenerator/views/codeList/hooks/useLoadDbData.ts b/src/modules/codeGenerator/views/codeList/hooks/useLoadDbData.ts new file mode 100644 index 000000000..82672dbd7 --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/hooks/useLoadDbData.ts @@ -0,0 +1,55 @@ +import { computed, ref } from 'vue'; +import { isEmpty } from '@/utils/is'; +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +export const useLoadDbData = ({ validateAddEdit }) => { + // 数据库数据加载状态 + const dbDataLoading = ref(false); + const dbData = ref({}); + + /** + * 加载数据库数据 + */ + const loadDbData = async () => { + const { connectionId, tableName } = await validateAddEdit(); + if (!isEmpty(connectionId) && !isEmpty(tableName)) { + dbDataLoading.value = true; + try { + dbData.value = await defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: 'db/connection/queryDbTable', + data: { + dbConnectionId: connectionId, + tableName: tableName, + }, + }); + } finally { + dbDataLoading.value = false; + } + } + }; + /** + * 表格table计算属性 + */ + const computedTableData = computed(() => { + if (!dbData.value.tableName) { + return []; + } + const primaryKeyList = dbData.value.primaryKeyList || []; + const baseColumnList = dbData.value.baseColumnList || []; + return [...primaryKeyList, ...baseColumnList]; + }); + /** + * 同步表 + */ + const handleSyncTableData = () => { + loadDbData(); + }; + return { + dbData, + dbDataLoading, + computedTableData, + handleSyncTableData, + loadDbData, + }; +}; diff --git a/src/modules/codeGenerator/views/codeList/lang/en_US.ts b/src/modules/codeGenerator/views/codeList/lang/en_US.ts new file mode 100644 index 000000000..0a0e557e7 --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/lang/en_US.ts @@ -0,0 +1,166 @@ +export default { + generator: { + views: { + code: { + table: { + connectionName: 'Connection name', + configName: 'Config name', + tableName: 'Table name', + type: 'Type', + remarks: 'Table remark', + }, + title: { + dbMessage: 'DB information', + tableSetting: 'Table setting', + formSetting: 'Form setting', + searchSetting: 'Search setting', + design: 'Design', + showCheckBox: 'Show check box', + isPage: 'Pagination', + invented: 'Virtual scrolling', + columnSort: 'Order adjustable', + leftButton: 'Left button', + rightButton: 'Right button', + rowButtonType: { + title: 'Row button type', + none: 'none', + single: 'unified', + more: 'more', + text: 'text', + }, + rowButtonList: 'Row button', + formColNum: 'Form col num', + searchColNum: 'Search col num', + relateTable: 'Relate addendum', + tableType: { + single: 'single', + main: 'main', + addendum: 'addendum', + }, + colNum: { + one: 'One column', + two: 'Two column', + three: 'Three column', + four: 'Four column', + }, + controlList: { + input: 'INPUT', + textarea: 'TEXTAREA', + number: 'NUMBER', + password: 'PASSWORD', + select: 'SELECT', + transfer: 'TRANSFER', + selectTable: 'SELECT_TABLE', + radio: 'RADIO', + checkbox: 'CHECKBOX', + switch_type: 'SWITCH', + date: 'DATE', + time: 'TIME', + datetime: 'DATETIME', + file: 'FILE', + }, + ruleList: { + notEmpty: 'NOT_EMPTY', + PHONE: 'PHONE', + EMAIL: 'EMAIL', + NUMBER: 'NUMBER', + REGEXP: 'REGEXP', + }, + i18nPrefix: 'I18n prefix', + }, + button: { + createCode: 'Generate code', + syncTableData: 'Sync table information', + }, + validate: { + connectionName: 'Please select a database connection', + tableName: 'Please enter a table name', + configName: 'Please enter the configuration name', + syncTable: 'Please synchronize table information first', + tableSetting: 'Please configure the table', + formSetting: 'Please configure the form', + searchSetting: 'Please configure the search', + i18nPrefix: 'Please enter the i18n prefix', + }, + message: { + saveConfirmContent: 'Non empty field: [{0}] has no form set', + noSelectSystem: 'Please select system', + }, + }, + codeCreateForm: { + title: { + description: 'Function description', + tableName: 'Table name', + className: 'Model class name', + packages: 'packages', + extPackages: 'ext packages', + controllerBasePath: 'controller path', + choseAddendum: 'Select addendum', + customConfig: 'Custom config', + }, + message: { + choseTemplate: 'Please select a template', + customConfig: 'Please enter json', + }, + validate: { + packages: 'Please enter package', + className: 'Please enter model class name', + controllerBasePath: 'Path cannot be empty', + }, + }, + tableField: { + title: { + columnName: 'Column name', + typeName: 'Column type', + columnSize: 'Column size', + decimalDigits: 'Decimal digits', + columnDef: 'Default value', + nullable: 'Nullable', + remarks: 'Table remark', + primaryKey: 'Primary key', + indexed: 'Indexed', + }, + }, + tableSetting: { + title: { + title: 'Title', + sortable: 'Sortable', + fixed: 'Fixed', + width: 'Width', + align: 'Align', + resizable: 'Resizable', + visible: 'Render', + hidden: 'Hidden', + editable: 'Editable', + format: 'Format', + }, + }, + formSetting: { + title: { + controlType: 'Control type', + readonly: 'Readonly', + used: 'Transfer background', + useTableSearch: 'Query DB', + keyColumnName: 'Key column', + valueColumnName: 'Value column', + tableWhere: 'Query criteria/Dict code', + rules: 'Rules', + }, + }, + searchSetting: { + title: { + searchSymbol: 'Search identity', + }, + }, + addendumTable: { + title: { + relatedColumn: 'Associated field', + }, + validate: { + relatedColumn: 'Please select an associated field', + relatedColumnWithConfig: 'Please set the associated field, configuration: {0}', + }, + }, + }, + }, +}; diff --git a/src/modules/codeGenerator/views/codeList/lang/zh_CN.ts b/src/modules/codeGenerator/views/codeList/lang/zh_CN.ts new file mode 100644 index 000000000..63333bde5 --- /dev/null +++ b/src/modules/codeGenerator/views/codeList/lang/zh_CN.ts @@ -0,0 +1,167 @@ +export default { + generator: { + views: { + code: { + table: { + connectionName: '连接名称', + configName: '配置名称', + tableName: '表名', + type: '类型', + remarks: '表备注', + }, + title: { + dbMessage: '数据库信息', + tableSetting: '表格配置', + formSetting: '表单配置', + searchSetting: '查询配置', + design: '设计', + showCheckBox: '显示复选框', + isPage: '是否分页', + invented: '虚拟滚动', + columnSort: '列顺序可调', + leftButton: '左侧按钮', + rightButton: '右侧按钮', + rowButtonType: { + title: '行按钮类型', + none: '无', + single: '统一', + more: '多个', + text: '文本', + }, + rowButtonList: '行操作按钮', + formColNum: '表单列数', + searchColNum: '搜索列数', + relateTable: '关联附表', + tableType: { + single: '单表', + main: '主表', + addendum: '附表', + }, + colNum: { + one: '一列', + two: '两列', + three: '三列', + four: '四列', + }, + controlList: { + input: '文本框', + textarea: '文本域', + number: '数字', + password: '密码', + select: '下拉框', + transfer: '穿梭框', + selectTable: '下拉表格', + radio: '单选框', + checkbox: '多选框', + switch_type: '开关', + date: '日期', + time: '时间', + datetime: '日期时间', + file: '文件', + }, + ruleList: { + notEmpty: '非空', + PHONE: '手机号码', + EMAIL: '邮箱', + NUMBER: '数字', + REGEXP: '正则', + }, + i18nPrefix: '国际化前缀', + }, + button: { + createCode: '生成代码', + syncTableData: '同步表信息', + }, + validate: { + connectionName: '请选择数据库连接', + tableName: '请输入表名', + configName: '请输入配置名', + syncTable: '请先同步表信息', + tableSetting: '请进行表格配置', + formSetting: '请进行表单配置', + searchSetting: '请进行搜索配置', + i18nPrefix: '请输入国际化前缀', + }, + message: { + saveConfirmContent: '非空字段:【{0}】未设置form', + noSelectSystem: '请选择系统', + }, + }, + codeCreateForm: { + title: { + description: '功能描述', + tableName: '表名', + className: '实体类名', + packages: '包名', + extPackages: 'ext包名', + controllerBasePath: 'controller路径', + choseAddendum: '选择附表', + customConfig: '自定义配置', + templateList: '模板', + }, + message: { + choseTemplate: '请选择模板', + customConfig: '请输入json', + }, + validate: { + packages: '请输入包名', + className: '请输入实体类名', + controllerBasePath: '路径不能为空', + }, + }, + tableField: { + title: { + columnName: '字段名称', + typeName: '字段类型', + columnSize: '字段长度', + decimalDigits: '小数位数', + columnDef: '默认值', + nullable: '允许空值', + remarks: '表备注', + primaryKey: '主键', + indexed: '索引', + }, + }, + tableSetting: { + title: { + title: '标题', + sortable: '是否排序', + fixed: '列冻结', + width: '宽度', + align: '对齐方式', + resizable: '支持拖动', + visible: '是否渲染', + hidden: '是否隐藏', + editable: '是否可编辑', + format: '格式化', + }, + }, + formSetting: { + title: { + controlType: '控件类型', + readonly: '是否只读', + used: '是否传送后台', + useTableSearch: '查询数据库', + keyColumnName: 'key字段', + valueColumnName: 'value字段', + tableWhere: '查询条件/字典code', + rules: '验证规则', + }, + }, + searchSetting: { + title: { + searchSymbol: '搜索标识', + }, + }, + addendumTable: { + title: { + relatedColumn: '关联字段', + }, + validate: { + relatedColumn: '请选择关联字段', + relatedColumnWithConfig: '请设置关联字段,配置:{0}', + }, + }, + }, + }, +}; diff --git a/src/modules/codeGenerator/views/database/DatabaseListHooks.ts b/src/modules/codeGenerator/views/database/DatabaseListHooks.ts new file mode 100644 index 000000000..2632e0031 --- /dev/null +++ b/src/modules/codeGenerator/views/database/DatabaseListHooks.ts @@ -0,0 +1,19 @@ +import { testConnectedApi } from './DatabaseListView.api'; +import { message, Modal } from 'ant-design-vue'; + +export const handleTestConnected = async (row, t: Function, setLoading) => { + try { + setLoading(true); + const result = await testConnectedApi(row.id); + if (result.result === true) { + message.success(t('generator.views.database.message.connectSuccess')); + } else { + Modal.error({ + title: t('generator.views.database.message.connectFail'), + content: result.message, + }); + } + } finally { + setLoading(false); + } +}; diff --git a/src/modules/codeGenerator/views/database/DatabaseListView.api.ts b/src/modules/codeGenerator/views/database/DatabaseListView.api.ts new file mode 100644 index 000000000..ada3295b4 --- /dev/null +++ b/src/modules/codeGenerator/views/database/DatabaseListView.api.ts @@ -0,0 +1,70 @@ +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +enum api { + saveUpdate = 'db/connection/saveUpdate', + getById = 'db/connection/getById', + listBySystem = 'db/connection/listBySystem', + batchDeleteById = 'db/connection/batchDeleteById', + testConnected = 'db/connection/testConnection', + listTemplate = 'db/code/template/list', + createDict = '/public/db/createDic', +} + +export const saveUpdateApi = (data: any) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: api.saveUpdate, + data, + }); +}; + +export const getByIdApi = (id: number) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: api.getById, + data: id, + }); +}; + +export const listApi = (data?: any) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: api.listBySystem, + data, + }); +}; + +export const deleteApi = async (rows: any[]) => { + if (rows.length === 0) { + return; + } + await defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: api.batchDeleteById, + data: rows.map((item: any) => item.id), + }); +}; + +export const testConnectedApi = (id: number) => + defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: api.testConnected, + data: id, + }); + +export const listTemplate = (templateType?: string) => + defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: api.listTemplate, + data: { + parameter: { + 'templateType@=': templateType, + }, + }, + }); + +export const getCreateDicUrl = ({ row, templateId, tempToken }) => { + return `${defHttp.getApiUrlByService( + ApiServiceEnum.SMART_CODE, + )}/public/db/createDic?connectionId=${row.id}&templateId=${templateId}&access-token=${tempToken}`; +}; diff --git a/src/modules/codeGenerator/views/database/DatabaseListView.data.ts b/src/modules/codeGenerator/views/database/DatabaseListView.data.ts new file mode 100644 index 000000000..957599c7d --- /dev/null +++ b/src/modules/codeGenerator/views/database/DatabaseListView.data.ts @@ -0,0 +1,203 @@ +import type { FormSchema } from '@/components/Form'; +import type { SmartColumn, SmartSearchFormSchema } from '@/components/SmartTable'; + +const dbTypeList = ['MYSQL', 'SQL_SERVER', 'ORACLE']; + +export const tableColumns: SmartColumn[] = [ + { + type: 'checkbox', + width: 60, + align: 'center', + fixed: 'left', + }, + { + title: '{generator.views.database.table.connectionName}', + field: 'connectionName', + width: 160, + fixed: 'left', + }, + { + title: '{generator.views.database.table.databaseName}', + field: 'databaseName', + width: 160, + fixed: 'left', + }, + { + title: '{generator.views.database.table.type}', + field: 'type', + width: 120, + }, + { + title: '{generator.views.database.table.url}', + field: 'url', + minWidth: 200, + showOverflow: 'tooltip', + }, + { + title: '{generator.views.database.table.username}', + field: 'username', + width: 120, + }, + { + title: '{generator.views.database.table.tableSchema}', + field: 'tableSchema', + width: 120, + }, + { + title: '{common.table.createTime}', + field: 'createTime', + width: 165, + sortable: true, + }, + { + title: '{common.table.createUser}', + field: 'createBy', + width: 120, + }, + { + title: '{common.table.updateTime}', + field: 'updateTime', + width: 165, + sortable: true, + }, + { + title: '{common.table.updateUser}', + field: 'updateBy', + width: 120, + }, + { + title: '{common.table.operation}', + field: 'operation', + width: 120, + fixed: 'right', + slots: { + default: 'table-operation', + }, + }, +]; + +export const addEditForm: (t: Function) => Array = (t: Function) => { + return [ + { + field: 'id', + component: 'Input', + show: false, + }, + { + field: 'systemId', + component: 'Input', + show: false, + }, + { + label: t('generator.views.database.table.connectionName'), + field: 'connectionName', + component: 'Input', + componentProps: { + placeholder: t('generator.views.database.validate.connectionName'), + }, + required: true, + }, + { + label: t('generator.views.database.table.databaseName'), + field: 'databaseName', + component: 'Input', + componentProps: { + placeholder: t('generator.views.database.validate.databaseName'), + }, + required: true, + }, + { + label: t('generator.views.database.table.type'), + field: 'type', + component: 'Select', + componentProps: { + placeholder: t('generator.views.database.validate.type'), + options: dbTypeList.map((item) => { + return { + label: item, + value: item, + }; + }), + }, + rules: [ + { + message: t('generator.views.database.validate.type'), + required: true, + trigger: 'change', + }, + ], + }, + { + label: t('generator.views.database.table.url'), + field: 'url', + component: 'Input', + componentProps: { + placeholder: t('generator.views.database.validate.url'), + }, + required: true, + }, + { + label: t('generator.views.database.table.username'), + field: 'username', + component: 'Input', + componentProps: { + placeholder: t('generator.views.database.validate.username'), + }, + required: true, + }, + { + label: t('generator.views.database.table.password'), + field: 'password', + component: 'InputPassword', + componentProps: { + placeholder: t('generator.views.database.validate.password'), + }, + required: true, + }, + { + label: t('generator.views.database.table.tableSchema'), + field: 'tableSchema', + component: 'Input', + componentProps: {}, + }, + ] as FormSchema[]; +}; + +/** + * 搜索表单配置 + * @param t + */ +export const searchForm: (t: Function) => SmartSearchFormSchema[] = (t: Function) => { + return [ + { + field: 'connectionName', + component: 'Input', + componentProps: { + placeholder: t('generator.views.database.table.connectionName'), + }, + colProps: { span: 6 }, + searchSymbol: 'likeRight', + label: '', + }, + { + field: 'databaseName', + component: 'Input', + componentProps: { + placeholder: t('generator.views.database.table.databaseName'), + }, + colProps: { span: 6 }, + searchSymbol: '=', + label: '', + }, + { + field: 'project', + component: 'Input', + componentProps: { + placeholder: t('generator.views.database.table.project'), + }, + colProps: { span: 6 }, + searchSymbol: 'likeLeft', + label: '', + }, + ]; +}; diff --git a/src/modules/codeGenerator/views/database/DatabaseListView.vue b/src/modules/codeGenerator/views/database/DatabaseListView.vue new file mode 100644 index 000000000..2485f914e --- /dev/null +++ b/src/modules/codeGenerator/views/database/DatabaseListView.vue @@ -0,0 +1,172 @@ + + + + + + diff --git a/src/modules/codeGenerator/views/database/components/DatabaseListViewAddEditModal.vue b/src/modules/codeGenerator/views/database/components/DatabaseListViewAddEditModal.vue new file mode 100644 index 000000000..3a5d44da5 --- /dev/null +++ b/src/modules/codeGenerator/views/database/components/DatabaseListViewAddEditModal.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/modules/codeGenerator/views/database/components/TemplateSelected.vue b/src/modules/codeGenerator/views/database/components/TemplateSelected.vue new file mode 100644 index 000000000..aa18c5e8d --- /dev/null +++ b/src/modules/codeGenerator/views/database/components/TemplateSelected.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/src/modules/codeGenerator/views/database/components/TemplateSelectedModal.vue b/src/modules/codeGenerator/views/database/components/TemplateSelectedModal.vue new file mode 100644 index 000000000..79acff999 --- /dev/null +++ b/src/modules/codeGenerator/views/database/components/TemplateSelectedModal.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/modules/codeGenerator/views/database/lang/en_US.ts b/src/modules/codeGenerator/views/database/lang/en_US.ts new file mode 100644 index 000000000..241240be1 --- /dev/null +++ b/src/modules/codeGenerator/views/database/lang/en_US.ts @@ -0,0 +1,40 @@ +export default { + generator: { + views: { + database: { + common: { + chooseTemplate: 'Select template', + }, + table: { + connectionName: 'Connection name', + databaseName: 'DB name', + type: 'Type', + project: 'Project', + url: 'URL', + username: 'Username', + tableSchema: 'Table schema', + password: 'Password', + }, + button: { + testConnected: 'Test connection', + createDic: 'Generate database dic', + }, + validate: { + type: 'Please select database type', + connectionName: 'Please enter connection name', + databaseName: 'Please enter database name', + project: 'Please enter database', + url: 'Please enter URL', + username: 'Please enter username', + password: 'Please enter password', + template: 'Please select template', + }, + message: { + deleteOwn: 'You can only delete database connections that you have created', + connectSuccess: 'Database connection succeeded', + connectFail: 'Database connection failed', + }, + }, + }, + }, +}; diff --git a/src/modules/codeGenerator/views/database/lang/zh_CN.ts b/src/modules/codeGenerator/views/database/lang/zh_CN.ts new file mode 100644 index 000000000..f374fc48d --- /dev/null +++ b/src/modules/codeGenerator/views/database/lang/zh_CN.ts @@ -0,0 +1,40 @@ +export default { + generator: { + views: { + database: { + common: { + chooseTemplate: '选择模板', + }, + table: { + connectionName: '连接名称', + databaseName: '数据库名称', + type: '类型', + project: '项目', + url: 'URL', + username: '用户名', + tableSchema: 'TableSchema', + password: '密码', + }, + button: { + testConnected: '测试连接', + createDic: '生成数据库字典', + }, + validate: { + type: '请选择数据库类型', + connectionName: '请输入连接名称', + databaseName: '请输入数据库名称', + project: '请输入项目', + url: '请输入URL', + username: '请输入用户名', + password: '请输入密码', + template: '请选择模板', + }, + message: { + deleteOwn: '只能删除自己创建的数据库连接', + connectSuccess: '数据库连接成功', + connectFail: '数据库连接失败', + }, + }, + }, + }, +}; diff --git a/src/modules/codeGenerator/views/document/TemplateDataDocumentView.vue b/src/modules/codeGenerator/views/document/TemplateDataDocumentView.vue new file mode 100644 index 000000000..0fa676d02 --- /dev/null +++ b/src/modules/codeGenerator/views/document/TemplateDataDocumentView.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/src/modules/codeGenerator/views/template/CodeTemplateList.api.ts b/src/modules/codeGenerator/views/template/CodeTemplateList.api.ts new file mode 100644 index 000000000..b6ab23b84 --- /dev/null +++ b/src/modules/codeGenerator/views/template/CodeTemplateList.api.ts @@ -0,0 +1,39 @@ +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +enum Api { + list = 'db/code/template/list', + saveUpdate = 'db/code/template/saveUpdate', + delete = 'db/code/template/batchDeleteById', + getById = 'db/code/template/getById', +} + +export const listApi = (params) => + defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: Api.list, + data: params, + }); + +export const saveUpdateApi = (model) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: Api.saveUpdate, + data: model, + }); +}; + +export const deleteApi = (ids: number[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: Api.delete, + data: ids, + }); +}; + +export const getByIdApi = (id: number) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_CODE, + url: Api.getById, + data: id, + }); +}; diff --git a/src/modules/codeGenerator/views/template/CodeTemplateList.config.ts b/src/modules/codeGenerator/views/template/CodeTemplateList.config.ts new file mode 100644 index 000000000..2b0531639 --- /dev/null +++ b/src/modules/codeGenerator/views/template/CodeTemplateList.config.ts @@ -0,0 +1,160 @@ +import type { SmartColumn, SmartSearchFormSchema } from '@/components/SmartTable'; +import type { FormSchema } from '@/components/Form'; + +import { TemplateType as templateTypeConstants } from '../../constants/DatabaseConstants'; +import { extensionLanguageMap } from '../../constants/Constants'; + +export const getTableColumns = (t: Function): SmartColumn[] => { + return [ + { + type: 'checkbox', + width: 60, + fixed: 'left', + }, + { + field: 'name', + title: '{generator.views.template.table.name}', + width: 200, + fixed: 'left', + align: 'left', + headerAlign: 'center', + }, + { + field: 'templateType', + title: '{generator.views.template.table.templateType}', + width: 140, + formatter: ({ row }: any) => { + const templateType = templateTypeConstants[row.templateType]; + if (templateType) { + return t(templateType.label); + } + return ''; + }, + }, + { + field: 'language', + title: '{generator.views.template.table.language}', + width: 200, + }, + { + field: 'remark', + title: '{generator.views.template.table.remark}', + minWidth: 200, + align: 'left', + headerAlign: 'center', + }, + { + title: '{common.table.createTime}', + field: 'createTime', + width: 165, + sortable: true, + }, + { + title: '{common.table.createUser}', + field: 'createBy', + width: 120, + }, + { + title: '{common.table.updateTime}', + field: 'updateTime', + width: 165, + sortable: true, + }, + { + title: '{common.table.updateUser}', + field: 'updateBy', + width: 120, + }, + { + title: '{common.table.operation}', + field: 'operation', + width: 120, + fixed: 'right', + slots: { + default: 'table-operation', + }, + }, + ]; +}; + +export const getSearchSchemas = (t: Function): SmartSearchFormSchema[] => { + return [ + { + label: t('generator.views.template.table.name'), + field: 'name', + component: 'Input', + searchSymbol: 'like', + }, + ]; +}; + +export const getAddEditFormSchemas = (t: Function): FormSchema[] => { + return [ + { + label: '', + field: 'templateId', + component: 'Input', + show: false, + }, + { + label: '', + field: 'groupId', + component: 'Input', + show: false, + }, + { + label: t('generator.views.template.table.templateType'), + field: 'templateType', + component: 'Select', + required: true, + componentProps: { + options: Object.keys(templateTypeConstants).map((item) => { + const value = templateTypeConstants[item]; + return { + value: value.value, + label: t(value.label), + }; + }), + }, + }, + { + label: t('generator.views.template.table.name'), + field: 'name', + component: 'Input', + required: true, + }, + { + label: t('generator.views.template.table.remark'), + field: 'remark', + component: 'Input', + }, + { + label: t('generator.views.template.table.filenameSuffix'), + field: 'filenameSuffix', + component: 'Input', + }, + { + label: t('generator.views.template.table.language'), + field: 'language', + component: 'Select', + componentProps: { + options: Object.keys(extensionLanguageMap).map((item) => { + return { + label: extensionLanguageMap[item], + value: item, + }; + }), + }, + }, + { + label: '', + field: 'template', + slot: 'addEditForm-language', + colProps: { + span: 24, + }, + labelWidth: 0, + disabledLabelWidth: true, + }, + ]; +}; diff --git a/src/modules/codeGenerator/views/template/CodeTemplateList.vue b/src/modules/codeGenerator/views/template/CodeTemplateList.vue new file mode 100644 index 000000000..1de9786aa --- /dev/null +++ b/src/modules/codeGenerator/views/template/CodeTemplateList.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/src/modules/codeGenerator/views/template/components/TemplateGroup.vue b/src/modules/codeGenerator/views/template/components/TemplateGroup.vue new file mode 100644 index 000000000..b6ac70206 --- /dev/null +++ b/src/modules/codeGenerator/views/template/components/TemplateGroup.vue @@ -0,0 +1,248 @@ + + + + + diff --git a/src/modules/codeGenerator/views/template/components/TemplateSetUserGroup.vue b/src/modules/codeGenerator/views/template/components/TemplateSetUserGroup.vue new file mode 100644 index 000000000..3e5edb478 --- /dev/null +++ b/src/modules/codeGenerator/views/template/components/TemplateSetUserGroup.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/src/modules/codeGenerator/views/template/lang/en_US.ts b/src/modules/codeGenerator/views/template/lang/en_US.ts new file mode 100644 index 000000000..0dad99a32 --- /dev/null +++ b/src/modules/codeGenerator/views/template/lang/en_US.ts @@ -0,0 +1,37 @@ +export default { + generator: { + views: { + template: { + label: { + templateType: { + templateCode: 'Code template', + templateDbDict: 'DB dict template', + }, + }, + title: { + userGroup: 'User group', + templateGroup: 'Template group', + seq: 'Seq', + }, + table: { + name: 'Name', + templateType: 'Template Type', + language: 'Language', + remark: 'Remark', + filenameSuffix: 'Filename Suffix', + }, + notice: { + onlyDeleteMy: 'Only self created templates can be deleted', + choseGroup: 'Please select a template group first', + }, + validate: { + templateType: 'Please enter template type', + name: 'Please enter name', + remark: 'Please enter remark', + seq: 'Please enter seq', + templateGroup: 'Please enter template group', + }, + }, + }, + }, +}; diff --git a/src/modules/codeGenerator/views/template/lang/zh_CN.ts b/src/modules/codeGenerator/views/template/lang/zh_CN.ts new file mode 100644 index 000000000..b7f1b632f --- /dev/null +++ b/src/modules/codeGenerator/views/template/lang/zh_CN.ts @@ -0,0 +1,37 @@ +export default { + generator: { + views: { + template: { + label: { + templateType: { + templateCode: '代码模板', + templateDbDict: '数据库字典模板', + }, + }, + title: { + userGroup: '用户组', + templateGroup: '模板分组', + seq: '序号', + }, + table: { + name: '名称', + templateType: '模板类型', + language: '语言', + remark: '备注', + filenameSuffix: '文件名后缀', + }, + notice: { + onlyDeleteMy: '只能删除自己创建的模板', + choseGroup: '请先选择模板分组', + }, + validate: { + templateType: '请输入模板类型', + name: '请输入模板名称', + remark: '请输入备注', + seq: '请输入序号', + templateGroup: '请输入模板分组', + }, + }, + }, + }, +}; diff --git a/src/modules/system/views/accessSecret/SysAuthAccessSecretListView.api.ts b/src/modules/system/views/accessSecret/SysAuthAccessSecretListView.api.ts new file mode 100644 index 000000000..94268ef3e --- /dev/null +++ b/src/modules/system/views/accessSecret/SysAuthAccessSecretListView.api.ts @@ -0,0 +1,42 @@ +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +enum Api { + list = '/sys/auth/accessSecret/list', + getById = '/sys/auth/accessSecret/getById', + saveUpdate = '/sys/auth/accessSecret/saveUpdate', + delete = '/sys/auth/accessSecret/batchDeleteById', +} + +export const listApi = (params) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.list, + data: { + ...params, + }, + }); +}; + +export const saveUpdateApi = (modelList: any[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.saveUpdate, + data: modelList[0], + }); +}; + +export const deleteApi = (removeRecords: Recordable[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.delete, + data: removeRecords.map((item) => item.id), + }); +}; + +export const getByIdApi = (id: number) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.getById, + data: id, + }); +}; diff --git a/src/modules/system/views/accessSecret/SysAuthAccessSecretListView.config.ts b/src/modules/system/views/accessSecret/SysAuthAccessSecretListView.config.ts new file mode 100644 index 000000000..6b1e508ac --- /dev/null +++ b/src/modules/system/views/accessSecret/SysAuthAccessSecretListView.config.ts @@ -0,0 +1,186 @@ +import type { SmartColumn, SmartSearchFormSchema } from '@/components/SmartTable'; +import type { FormSchema } from '@/components/Form'; + +import { tableUseYnClass } from '@/components/SmartTable'; + +/** + * 表格列表 + */ +export const getTableColumns = (): SmartColumn[] => { + return [ + { + type: 'checkbox', + width: 60, + align: 'center', + fixed: 'left', + }, + { + field: 'seq', + sortable: true, + title: '{common.table.seq}', + width: 100, + }, + { + field: 'accessKey', + title: '{system.views.auth.acccessSecret.title.accessKey}', + width: 120, + }, + { + field: 'secretKey', + title: '{system.views.auth.acccessSecret.title.secretKey}', + width: 120, + }, + { + field: 'expireDate', + title: '{system.views.auth.acccessSecret.title.expireDate}', + width: 165, + }, + { + field: 'accessIp', + title: '{system.views.auth.acccessSecret.title.accessIp}', + width: 120, + }, + { + field: 'remark', + title: '{common.table.remark}', + width: 200, + }, + { + ...tableUseYnClass(), + }, + { + field: 'createBy', + title: '{common.table.createUser}', + width: 120, + }, + { + field: 'createTime', + title: '{common.table.createTime}', + width: 165, + }, + { + field: 'updateBy', + title: '{common.table.updateUser}', + width: 120, + }, + { + field: 'updateTime', + title: '{common.table.updateTime}', + width: 165, + }, + { + title: '{common.table.operation}', + field: 'operation', + width: 120, + fixed: 'right', + slots: { + default: 'table-operation', + }, + }, + ]; +}; + +/** + * 添加修改表单 + */ +export const getFormSchemas = (t: Function): FormSchema[] => { + return [ + { + field: 'id', + show: false, + label: '', + component: 'Input', + componentProps: {}, + }, + { + field: 'accessKey', + label: t('system.views.auth.acccessSecret.title.accessKey'), + component: 'Input', + componentProps: {}, + dynamicDisabled: true, + }, + { + field: 'secretKey', + label: t('system.views.auth.acccessSecret.title.secretKey'), + component: 'Input', + componentProps: {}, + dynamicDisabled: true, + }, + { + field: 'expireDate', + label: t('system.views.auth.acccessSecret.title.expireDate'), + component: 'DatePicker', + componentProps: { + showTime: true, + style: { width: '100%' }, + }, + }, + { + field: 'accessIp', + label: t('system.views.auth.acccessSecret.title.accessIp'), + component: 'InputTextArea', + componentProps: { + placeholder: t('system.views.auth.acccessSecret.validate.accessIp'), + }, + }, + { + field: 'remark', + label: t('common.table.remark'), + component: 'InputTextArea', + componentProps: {}, + }, + { + field: 'useYn', + label: t('common.table.useYn'), + component: 'Switch', + componentProps: {}, + defaultValue: true, + }, + { + field: 'seq', + label: t('common.table.seq'), + component: 'InputNumber', + componentProps: {}, + required: true, + }, + ]; +}; + +export const getSearchFormSchemas = (t: Function): SmartSearchFormSchema[] => { + return [ + { + field: 'accessKey', + label: t('system.views.auth.acccessSecret.title.accessKey'), + component: 'Input', + searchSymbol: 'like', + }, + { + field: 'secretKey', + label: t('system.views.auth.acccessSecret.title.secretKey'), + component: 'Input', + searchSymbol: 'like', + }, + { + field: 'useYn', + label: t('common.table.useYn'), + component: 'Select', + searchSymbol: '=', + defaultValue: 1, + componentProps: { + style: { + width: '120px', + }, + options: [ + { + label: t('common.form.use'), + value: 1, + }, + { + label: t('common.form.noUse'), + value: 0, + }, + ], + }, + }, + ]; +}; diff --git a/src/modules/system/views/accessSecret/SysAuthAccessSecretListView.vue b/src/modules/system/views/accessSecret/SysAuthAccessSecretListView.vue new file mode 100644 index 000000000..96b317ff0 --- /dev/null +++ b/src/modules/system/views/accessSecret/SysAuthAccessSecretListView.vue @@ -0,0 +1,98 @@ + + + diff --git a/src/modules/system/views/accessSecret/lang/zh_CN.ts b/src/modules/system/views/accessSecret/lang/zh_CN.ts new file mode 100644 index 000000000..887991458 --- /dev/null +++ b/src/modules/system/views/accessSecret/lang/zh_CN.ts @@ -0,0 +1,28 @@ +/** + * 国际化信息 + */ +export default { + trans: true, + key: 'system.views.auth.acccessSecret', + data: { + title: { + accessKey: 'Access key', + secretKey: 'Secret key', + expireDate: '过期时间', + accessIp: '授权IP或域名', + createBy: 'createBy', + updateBy: 'updateBy', + }, + validate: { + accessKey: '请输入Access key', + secretKey: '请输入Secret key', + expireDate: '请输入过期时间', + accessIp: '请输入授权IP或域名,以逗号分隔', + }, + rules: {}, + search: { + accessKey: '请输入Access key', + secretKey: '请输入Secret key', + }, + }, +}; diff --git a/src/utils/http/axios/Axios.ts b/src/utils/http/axios/Axios.ts index 4e6ad1d9d..a6b14ace9 100644 --- a/src/utils/http/axios/Axios.ts +++ b/src/utils/http/axios/Axios.ts @@ -1,5 +1,11 @@ import type { AxiosInstance, AxiosResponse, AxiosError, InternalAxiosRequestConfig } from 'axios'; -import type { SmartAxiosRequestConfig, RequestOptions, Result, UploadFileParams } from '#/axios'; +import type { + SmartAxiosRequestConfig, + RequestOptions, + Result, + UploadFileParams, + UploadFileItemParams, +} from '#/axios'; import type { CreateAxiosOptions } from './axiosTransform'; import axios from 'axios'; import qs from 'qs'; @@ -9,6 +15,7 @@ import { cloneDeep } from 'lodash-es'; import { ContentTypeEnum, RequestEnum } from '@/enums/httpEnum'; import { useGlobSetting } from '@/hooks/setting'; import { downloadByData } from '@/utils/file/download'; +import { isArray } from 'xe-utils'; export * from './axiosTransform'; @@ -124,13 +131,23 @@ export class VAxios { */ uploadFile(config: SmartAxiosRequestConfig, params: UploadFileParams) { const formData = new window.FormData(); - const customFilename = params.name || 'file'; + // const customFilename = params.name || 'file'; - if (params.filename) { - formData.append(customFilename, params.file, params.filename); + const file = params.file; + let uploadFileList: UploadFileItemParams[]; + if (isArray(file)) { + uploadFileList = file; } else { - formData.append(customFilename, params.file); + uploadFileList = [file]; } + uploadFileList.forEach((item) => { + const customFilename = item.name || 'file'; + if (item.filename) { + formData.append(customFilename, item.file, item.filename); + } else { + formData.append(customFilename, item.file); + } + }); if (params.data) { Object.keys(params.data).forEach((key) => { diff --git a/src/utils/http/axios/index.ts b/src/utils/http/axios/index.ts index e8c63356f..372bfe250 100644 --- a/src/utils/http/axios/index.ts +++ b/src/utils/http/axios/index.ts @@ -104,8 +104,22 @@ const transform: AxiosTransform = { config.url = `${urlPrefix}${config.url}`; } + // 处理URL + const { isStandalone } = useGlobSetting(); + if (!isStandalone) { + let spi = ''; + if (!config.url?.startsWith('/')) { + spi = '/'; + } + config.url = `${config.service}${spi}${config.url}`; + } + if (apiUrl && isString(apiUrl)) { - config.url = `${apiUrl}${config.url}`; + let spi = ''; + if (!config.url?.startsWith('/')) { + spi = '/'; + } + config.url = `${apiUrl}${spi}${config.url}`; } const params = config.params || {}; const data = config.data || false;