feat: 新增代码生成预览抽屉

This commit is contained in:
xlsea
2024-09-07 18:27:39 +08:00
parent 9a0df2e4f3
commit c0ad0e4e4f
22 changed files with 508 additions and 52 deletions

View File

@ -7,6 +7,7 @@ import { setupElegantRouter } from './router';
import { setupUnocss } from './unocss';
import { setupUnplugin } from './unplugin';
import { setupHtmlPlugin } from './html';
import { setupMonacoEditorPlugin } from './monaco-editor';
export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) {
const plugins: PluginOption = [
@ -21,7 +22,8 @@ export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) {
setupUnocss(viteEnv),
...setupUnplugin(viteEnv),
progress(),
setupHtmlPlugin(buildTime)
setupHtmlPlugin(buildTime),
setupMonacoEditorPlugin()
];
return plugins;

View File

@ -0,0 +1,7 @@
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
export function setupMonacoEditorPlugin() {
return (monacoEditorPlugin as any).default({
languageWorkers: ['editorWorkerService', 'css', 'html', 'json', 'typescript']
});
}

View File

@ -46,6 +46,7 @@
"dayjs": "1.11.12",
"echarts": "5.5.1",
"jsencrypt": "^3.3.2",
"monaco-editor": "^0.48.0",
"naive-ui": "2.39.0",
"nprogress": "0.2.0",
"pinia": "2.2.2",
@ -82,6 +83,7 @@
"unplugin-icons": "0.19.2",
"unplugin-vue-components": "0.27.4",
"vite": "5.4.1",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "7.3.8",

20
pnpm-lock.yaml generated
View File

@ -44,6 +44,9 @@ importers:
jsencrypt:
specifier: ^3.3.2
version: 3.3.2
monaco-editor:
specifier: ^0.48.0
version: 0.48.0
naive-ui:
specifier: 2.39.0
version: 2.39.0(vue@3.4.38(typescript@5.5.4))
@ -147,6 +150,9 @@ importers:
vite:
specifier: 5.4.1
version: 5.4.1(@types/node@22.4.1)(sass@1.77.8)
vite-plugin-monaco-editor:
specifier: ^1.1.0
version: 1.1.0(monaco-editor@0.48.0)
vite-plugin-progress:
specifier: 0.0.7
version: 0.0.7(vite@5.4.1(@types/node@22.4.1)(sass@1.77.8))
@ -3042,6 +3048,9 @@ packages:
mlly@1.7.1:
resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==}
monaco-editor@0.48.0:
resolution: {integrity: sha512-goSDElNqFfw7iDHMg8WDATkfcyeLTNpBHQpO8incK6p5qZt5G/1j41X0xdGzpIkGojGXM+QiRQyLjnfDVvrpwA==}
mrmime@2.0.0:
resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==}
engines: {node: '>=10'}
@ -4025,6 +4034,11 @@ packages:
'@nuxt/kit':
optional: true
vite-plugin-monaco-editor@1.1.0:
resolution: {integrity: sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==}
peerDependencies:
monaco-editor: '>=0.33.0'
vite-plugin-progress@0.0.7:
resolution: {integrity: sha512-zyvKdcc/X+6hnw3J1HVV1TKrlFKC4Rh8GnDnWG/2qhRXjqytTcM++xZ+SAPnoDsSyWl8O93ymK0wZRgHAoglEQ==}
engines: {node: '>=14', pnpm: '>=7.0.0'}
@ -7270,6 +7284,8 @@ snapshots:
pkg-types: 1.1.3
ufo: 1.5.4
monaco-editor@0.48.0: {}
mrmime@2.0.0: {}
ms@2.0.0: {}
@ -8335,6 +8351,10 @@ snapshots:
- rollup
- supports-color
vite-plugin-monaco-editor@1.1.0(monaco-editor@0.48.0):
dependencies:
monaco-editor: 0.48.0
vite-plugin-progress@0.0.7(vite@5.4.1(@types/node@22.4.1)(sass@1.77.8)):
dependencies:
picocolors: 1.0.1

View File

@ -0,0 +1,227 @@
<script setup lang="ts">
import { nextTick, onMounted, ref, watch } from 'vue';
import * as monaco from 'monaco-editor';
import type { editor } from 'monaco-editor';
import { useThemeStore } from '@/store/modules/theme';
defineOptions({ name: 'MonacoEditor' });
interface Props {
language?: string;
readOnly?: boolean;
valueFormat?: string;
height?: string;
config?: editor.IStandaloneEditorConstructionOptions;
lineNumbers?: 'on' | 'off';
autoToggleTheme?: boolean;
theme?: 'vs-light' | 'vs-dark' | 'hc-black';
}
const props = withDefaults(defineProps<Props>(), {
language: 'json',
readOnly: false,
valueFormat: 'string',
lineNumbers: 'on',
theme: 'vs-light',
height: '150px',
autoToggleTheme: true,
config: () => ({
selectOnLineNumbers: true,
minimap: {
enabled: false
}
})
});
const value = defineModel<any>('value', { required: true, default: '' });
const isActive = ref(false);
const editContainer = ref<HTMLElement | null>(null);
let monacoEditor: monaco.editor.IStandaloneCodeEditor | null = null;
const { darkMode: isDark } = useThemeStore();
function handleToggleTheme() {
if (isDark) {
monaco.editor.setTheme('vs-dark');
} else {
monaco.editor.setTheme('vs-light');
}
}
/**
* 设置文本
*
* @param text
*/
function setValue(text: string) {
monacoEditor?.setValue(text || '');
}
/**
* 光标处插入文本
*
* @param text
*/
function insertText(text: string) {
// 获取光标位置
const position = monacoEditor?.getPosition();
// 未获取到光标位置信息
if (!position) {
return;
}
// 插入
monacoEditor?.executeEdits('', [
{
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
text
}
]);
// 设置新的光标位置
monacoEditor?.setPosition({ ...position, column: position.column + text.length });
// 重新聚焦
monacoEditor?.focus();
}
onMounted(() => {
monacoEditor = monaco.editor.create(editContainer.value as HTMLElement, {
value: getValue(),
...props.config,
language: props.language,
readOnly: props.readOnly,
lineNumbers: props.lineNumbers,
theme: props.theme,
automaticLayout: false
});
// 自动切换主题
if (props.autoToggleTheme) {
watch(
() => isDark,
() => {
nextTick(() => handleToggleTheme());
},
{
immediate: true
}
);
}
// 获取值
function getValue() {
// valueFormat 为json 格式,需要转换处理
if (props.valueFormat === 'json') {
if (value.value) {
return JSON.stringify(value.value, null, 2);
}
}
return value.value ?? '';
}
// 监听值变化
monacoEditor.onDidChangeModelContent(() => {
const currenValue = monacoEditor?.getValue();
// valueFormat 为json 格式,需要转换处理
if (props.valueFormat === 'json' && currenValue) {
value.value = JSON.parse(currenValue);
return;
}
value.value = currenValue ?? '';
});
const decorationsOverviewRuler = document.querySelector('.decorationsOverviewRuler');
decorationsOverviewRuler?.removeAttribute('width');
decorationsOverviewRuler?.removeAttribute('height');
});
watch(
() => value.value,
val => {
monacoEditor?.setValue(val || '');
}
);
watch(
() => props.language,
val => {
monaco.editor.setModelLanguage(monacoEditor?.getModel() as monaco.editor.ITextModel, val);
}
);
defineExpose({
setValue,
insertText
});
</script>
<template>
<NCard
class="h-full"
:class="isActive ? 'code-editor-border' : ''"
@click="() => (isActive = true)"
@mouseout="() => (isActive = false)"
>
<div ref="editContainer" class="azd-code-editor" :style="{ height: height }" />
</NCard>
</template>
<style></style>
<style lang="scss" scoped>
.n-card {
border: 1px solid rgb(224, 224, 230) !important;
:deep(.n-card__content) {
padding: 6px 0 !important;
}
}
.dark {
.n-card {
--n-color-modal: rgb(30, 30, 30, 1) !important;
border: none !important;
}
}
.n-card:hover {
border: 1px solid rgb(var(--primary-color)) !important;
}
.code-editor-border {
border: 1px solid rgb(var(--primary-color)) !important;
box-shadow: 0 0 0 2px rgba(30, 94, 253, 0.2) !important;
}
.azd-code-editor {
--n-border-radius: 3px;
width: 100%;
height: 100%;
min-height: 150px;
border-radius: var(--n-border-radius);
:deep(.monaco-editor) {
height: 100%;
border-radius: var(--n-border-radius) !important;
.overflow-guard {
border-radius: var(--n-border-radius) !important;
}
.decorationsOverviewRuler,
.scrollbar,
.slider {
border: 0 !important;
width: 7px !important;
color: rgba(0, 0, 0, 0.5) !important;
border-radius: 7px !important;
}
.scroll-decoration {
box-shadow: none !important;
}
}
}
</style>

View File

@ -18,6 +18,7 @@ const local: App.I18n.Schema = {
expandColumn: 'Expand Column',
columnSetting: 'Column Setting',
config: 'Config',
login: 'Login',
confirm: 'Confirm',
save: 'Save',
delete: 'Delete',
@ -179,7 +180,7 @@ const local: App.I18n.Schema = {
},
pwdLogin: {
title: 'Password Login',
rememberMe: 'Remember me',
rememberMe: 'Remember password',
forgetPassword: 'Forget password?',
register: 'Register',
otherAccountLogin: 'Other Account Login',

View File

@ -18,6 +18,7 @@ const local: App.I18n.Schema = {
expandColumn: '展开列',
columnSetting: '列设置',
config: '配置',
login: '登录',
confirm: '确认',
save: '保存',
delete: '删除',
@ -179,7 +180,7 @@ const local: App.I18n.Schema = {
},
pwdLogin: {
title: '密码登录',
rememberMe: '记住',
rememberMe: '记住密码',
forgetPassword: '忘记密码?',
register: '注册账号',
otherAccountLogin: '其他账号登录',

View File

@ -76,7 +76,7 @@ export function fetchSynchGenDbList(tableId: CommonType.IdType) {
/** 预览代码 */
export function fetchGetGenPreview(tableId: CommonType.IdType) {
return request<Api.Tool.GenTableDbList>({
return request<Api.Tool.GenTablePreview>({
url: `/tool/gen/preview/${tableId}`,
method: 'get'
});

View File

@ -1,7 +1,6 @@
import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { BACKEND_ERROR_CODE, createFlatRequest } from '@sa/axios';
import { useAuthStore } from '@/store/modules/auth';
import { $t } from '@/locales';
import { localStg, sessionStg } from '@/utils/storage';
import { getServiceBaseURL } from '@/utils/service';
import { decryptBase64, decryptWithAes, encryptBase64, encryptWithAes, generateAesKey } from '@/utils/crypto';
@ -58,7 +57,7 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
const responseCode = String(response.data.code);
function handleLogout() {
authStore.logout();
authStore.resetStore();
}
function logoutAndCleanup() {
@ -69,31 +68,29 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
}
// when the backend response code is in `logoutCodes`, it means the user will be logged out and redirected to login page
const logoutCodes = import.meta.env.VITE_SERVICE_LOGOUT_CODES?.split(',') || [];
if (logoutCodes.includes(responseCode)) {
handleLogout();
return null;
}
// const logoutCodes = import.meta.env.VITE_SERVICE_LOGOUT_CODES?.split(',') || [];
// if (logoutCodes.includes(responseCode)) {
// handleLogout();
// return null;
// }
// when the backend response code is in `modalLogoutCodes`, it means the user will be logged out by displaying a modal
const modalLogoutCodes = import.meta.env.VITE_SERVICE_MODAL_LOGOUT_CODES?.split(',') || [];
if (modalLogoutCodes.includes(responseCode) && !request.state.errMsgStack?.includes(response.data.msg)) {
if (modalLogoutCodes.includes(responseCode)) {
request.state.errMsgStack = [...(request.state.errMsgStack || []), response.data.msg];
// prevent the user from refreshing the page
window.addEventListener('beforeunload', handleLogout);
window.$dialog?.error({
title: $t('common.error'),
content: response.data.msg,
positiveText: $t('common.confirm'),
window.$dialog?.warning({
title: '系统提示',
content: '登录状态已过期,您可以继续留在该页面,或者重新登录',
positiveText: '重新登录',
negativeText: '取消',
maskClosable: false,
closeOnEsc: false,
onPositiveClick() {
logoutAndCleanup();
},
onClose() {
logoutAndCleanup();
}
});

View File

@ -319,7 +319,7 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
setIsInitAuthRoute(true);
} else {
// if fetch user routes failed, reset store
authStore.logout();
authStore.resetStore();
}
}

View File

@ -175,6 +175,9 @@ declare namespace Api {
Pick<GenTable, 'dataName' | 'tableName' | 'tableComment'> & Common.CommonSearchParams
>;
/** gen table preview */
type GenTablePreview = Record<string, string>;
/** gen table db list */
type GenTableDbList = Common.PaginatingQueryRecord<
Common.CommonRecord<Pick<GenTable, 'tableName' | 'tableComment'>>

View File

@ -299,6 +299,7 @@ declare namespace App {
expandColumn: string;
columnSetting: string;
config: string;
login: string;
confirm: string;
save: string;
delete: string;

View File

@ -45,6 +45,7 @@ declare module 'vue' {
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
MenuTreeSelect: typeof import('./../components/custom/menu-tree-select.vue')['default']
MonacoEditor: typeof import('./../components/common/monaco-editor.vue')['default']
NAlert: typeof import('naive-ui')['NAlert']
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']

View File

@ -161,7 +161,7 @@ declare module "@elegant-router/types" {
component: `view.${K}`;
}
: never;
/**
* the center level route
*/
@ -184,7 +184,7 @@ declare module "@elegant-router/types" {
children: (CenterLevelRoute<GetChildRouteKey<K>> | LastLevelRoute<GetChildRouteKey<K>>)[];
}
: never;
/**
* the custom first level route
*/

View File

@ -40,5 +40,7 @@ declare namespace StorageType {
layout: UnionKey.ThemeLayoutMode;
siderCollapse: boolean;
};
/** The login form rember */
loginRember: Api.Auth.PwdLoginForm;
}
}

View File

@ -8,6 +8,7 @@ import { useRouterPush } from '@/hooks/common/router';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { useAuthStore } from '@/store/modules/auth';
import { fetchCaptchaCode, fetchTenantList } from '@/service/api';
import { localStg } from '@/utils/storage';
defineOptions({
name: 'PwdLogin'
@ -26,8 +27,8 @@ const tenantOption = ref<SelectOption[]>([]);
const model: Api.Auth.PwdLoginForm = reactive({
tenantId: '000000',
username: 'admin',
password: 'admin123'
username: '',
password: ''
});
type RuleKey = Extract<keyof Api.Auth.PwdLoginForm, 'username' | 'password' | 'code' | 'tenantId'>;
@ -48,6 +49,14 @@ const rules = computed<Record<RuleKey, App.Global.FormRule[]>>(() => {
async function handleSubmit() {
await validate();
// 勾选了需要记住密码设置在 localStorage 中设置记住用户名和密码
if (remberMe.value) {
const { tenantId, username, password } = model;
localStg.set('loginRember', { tenantId, username, password });
} else {
// 否则移除
localStg.remove('loginRember');
}
try {
await authStore.login(model);
} catch (error) {
@ -57,15 +66,14 @@ async function handleSubmit() {
async function handleFetchTenantList() {
const { data, error } = await fetchTenantList();
if (!error) {
tenantEnabled.value = data.tenantEnabled;
tenantOption.value = data.voList.map(tenant => {
return {
label: tenant.companyName,
value: tenant.tenantId
};
});
}
if (error) return;
tenantEnabled.value = data.tenantEnabled;
tenantOption.value = data.voList.map(tenant => {
return {
label: tenant.companyName,
value: tenant.tenantId
};
});
}
handleFetchTenantList();
@ -84,6 +92,15 @@ async function handleFetchCaptchaCode() {
}
handleFetchCaptchaCode();
function handleLoginRember() {
const loginRember = localStg.get('loginRember');
if (!loginRember) return;
remberMe.value = true;
Object.assign(model, loginRember);
}
handleLoginRember();
</script>
<template>
@ -120,8 +137,8 @@ handleFetchCaptchaCode();
{{ $t('page.login.pwdLogin.forgetPassword') }}
</NButton>
</div>
<NButton type="primary" size="large" round block :loading="authStore.loginLoading" @click="handleSubmit">
{{ $t('common.confirm') }}
<NButton type="primary" size="large" block :loading="authStore.loginLoading" @click="handleSubmit">
{{ $t('common.login') }}
</NButton>
<div class="flex-y-center justify-between gap-12px">
<NButton class="flex-1" block @click="toggleLoginModule('code-login')">

View File

@ -373,8 +373,13 @@ const { record: showHideRecord } = useDict('sys_show_hide');
<NDescriptionsItem v-if="currentMenu.menuType === 'C'" label="组件路径">
{{ currentMenu.component }}
</NDescriptionsItem>
<NDescriptionsItem label="路由地址">{{ currentMenu.path }}</NDescriptionsItem>
<NDescriptionsItem v-if="currentMenu.menuType === 'C'" label="路由参数">
<NDescriptionsItem :label="currentMenu.isFrame !== '0' ? '路由地址' : '外链地址'">
{{ currentMenu.path }}
</NDescriptionsItem>
<NDescriptionsItem
v-if="currentMenu.menuType === 'C'"
:label="currentMenu.isFrame !== '2' ? '路由参数' : 'iframe 地址'"
>
{{ currentMenu.queryParam }}
</NDescriptionsItem>
<NDescriptionsItem v-if="currentMenu.menuType !== 'M'" label="权限标识">

View File

@ -2,6 +2,7 @@
import { NButton, NPopconfirm, NTooltip } from 'naive-ui';
import { useBoolean } from '@sa/hooks';
import { ref } from 'vue';
import { jsonClone } from '@sa/utils';
import {
fetchBatchDeleteGenTable,
fetchGenCode,
@ -16,12 +17,14 @@ import ButtonIcon from '@/components/custom/button-icon.vue';
import SvgIcon from '@/components/custom/svg-icon.vue';
import { useDownload } from '@/hooks/business/download';
import GenTableSearch from './modules/gen-table-search.vue';
import TableImportDrawer from './modules/table-import-drawer.vue';
import GenTableImportDrawer from './modules/gen-table-import-drawer.vue';
import GenTableOperateDrawer from './modules/gen-table-operate-drawer.vue';
import GenTablePreviewDrawer from './modules/gen-table-preview-drawer.vue';
const appStore = useAppStore();
const { zip } = useDownload();
const { bool: importVisible, setTrue: openImportVisible } = useBoolean();
const { bool: previewVisible, setTrue: openPreviewVisible } = useBoolean();
const {
columns,
@ -101,7 +104,13 @@ const {
width: 180,
render: row => (
<div class="flex-center gap-16px">
<ButtonIcon type="primary" text icon="ep:view" tooltipContent="预览" onClick={() => edit(row.tableId!)} />
<ButtonIcon
type="primary"
text
icon="ep:view"
tooltipContent="预览"
onClick={() => handlePreview(row.tableId!)}
/>
<ButtonIcon
type="primary"
text
@ -191,6 +200,12 @@ function handleImport() {
openImportVisible();
}
function handlePreview(id: CommonType.IdType) {
const findItem = data.value.find(item => item.tableId === id) || null;
editingData.value = jsonClone(findItem);
openPreviewVisible();
}
async function handleGenCode(row?: Api.Tool.GenTable) {
const tableIds = row?.tableId || checkedRowKeys.value.join(',');
if (!tableIds || tableIds === '') {
@ -203,7 +218,7 @@ async function handleGenCode(row?: Api.Tool.GenTable) {
if (error) return;
window.$message?.success('生成成功');
} else {
await zip(`/tool/gen/batchGenCode?tableIdStr=${tableIds}`, `ruoyi-${new Date().getTime()}.zip`);
zip(`/tool/gen/batchGenCode?tableIdStr=${tableIds}`, `ruoyi-${new Date().getTime()}.zip`);
}
}
@ -219,7 +234,7 @@ getDataNames();
</script>
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<div class="min-h-500px flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
<GenTableSearch
v-model:model="searchParams"
:options="dataNameOptions"
@ -265,15 +280,20 @@ getDataNames();
:data="data"
size="small"
:flex-height="!appStore.isMobile"
:scroll-x="962"
:scroll-x="1200"
:loading="loading"
remote
:row-key="row => row.tableId"
:pagination="mobilePagination"
class="sm:h-full"
/>
<TableImportDrawer v-model:visible="importVisible" :options="dataNameOptions" @submitted="getDataByPage" />
<GenTableImportDrawer v-model:visible="importVisible" :options="dataNameOptions" @submitted="getDataByPage" />
<GenTableOperateDrawer v-model:visible="drawerVisible" :row-data="editingData" @submitted="getDataByPage" />
<GenTablePreviewDrawer
v-model:visible="previewVisible"
:row-data="editingData"
@submitted="() => handleGenCode(editingData!)"
/>
</NCard>
</div>
</template>

View File

@ -7,7 +7,7 @@ import { useTable, useTableOperate } from '@/hooks/common/table';
import GenTableDbSearch from './gen-table-db-search.vue';
defineOptions({
name: 'TableImportDrawer'
name: 'GenTableImportDrawer'
});
interface Props {

View File

@ -0,0 +1,150 @@
<script setup lang="ts">
import { useLoading } from '@sa/hooks';
import { ref, watch } from 'vue';
import { fetchGetGenPreview } from '@/service/api';
import MonacoEditor from '@/components/common/monaco-editor.vue';
defineOptions({
name: 'GenTablePreviewDrawer'
});
interface Props {
/** the edit row data */
rowData?: Api.Tool.GenTable | null;
}
const props = defineProps<Props>();
interface Emits {
(e: 'submitted'): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
default: false
});
const tab = ref('vm/java/domain.java.vm');
const previewData = ref<Api.Tool.GenTablePreview>({});
const { loading, startLoading, endLoading } = useLoading();
async function getGenPreview() {
if (!props.rowData?.tableId) return;
startLoading();
const { data, error } = await fetchGetGenPreview(props.rowData?.tableId);
if (error) {
endLoading();
closeDrawer();
return;
}
previewData.value = data;
endLoading();
}
function closeDrawer() {
visible.value = false;
}
async function handleSubmit() {
closeDrawer();
emit('submitted');
}
watch(visible, () => {
if (visible.value) {
previewData.value = {};
getGenPreview();
}
});
const genMap: Api.Tool.GenTablePreview = {
'vm/java/domain.java.vm': 'domain.java',
'vm/java/vo.java.vm': 'vo.java',
'vm/java/bo.java.vm': 'bo.java',
'vm/java/mapper.java.vm': 'mapper.java',
'vm/java/service.java.vm': 'service.java',
'vm/java/serviceImpl.java.vm': 'serviceImpl.java',
'vm/java/controller.java.vm': 'controller.java',
'vm/xml/mapper.xml.vm': 'mapper.xml',
'vm/sql/sql.vm': 'sql',
'vm/soybean/soy.api.ts.vm': 'api.ts',
'vm/soybean/typings/soy.api.d.ts.vm': 'type.d.ts',
'vm/soybean/soy.index.vue.vm': 'index.vue'
};
function getGenLanguage(name: string) {
if (name.endsWith('.java')) {
return 'java';
}
if (name.endsWith('.xml')) {
return 'xml';
}
if (name.endsWith('sql')) {
return 'sql';
}
if (name.endsWith('.ts')) {
return 'typescript';
}
if (name.endsWith('.vue')) {
return 'html';
}
return 'plaintext';
}
</script>
<template>
<NDrawer v-model:show="visible" display-directive="show" width="100%">
<NDrawerContent title="代码预览" :native-scrollbar="false" closable>
<NSpin :show="loading" class="h-full" content-class="h-full">
<div class="flex flex-row">
<NTabs v-model:value="tab" type="line" placement="left" class="h-full" pane-class="h-full">
<NTab v-for="(gen, index) in Object.keys(genMap)" :key="index" :name="gen" display-directive="show">
{{ genMap[gen] }}
</NTab>
</NTabs>
<MonacoEditor
v-model:value="previewData[tab]"
class="tab-pane"
read-only
:language="getGenLanguage(genMap[tab])"
height="calc(100vh - 162px)"
/>
</div>
</NSpin>
<template #footer>
<NSpace :size="16">
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
<NButton :disabled="loading" type="primary" @click="handleSubmit">生成代码</NButton>
</NSpace>
</template>
</NDrawerContent>
</NDrawer>
</template>
<style scoped>
:deep(.n-drawer-body-content-wrapper) {
height: 100%;
}
:deep(.n-tabs) {
width: unset !important;
}
:deep(.n-tabs.n-tabs--left .n-tabs-bar) {
width: 5px !important;
}
.tab-pane {
transition:
color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
padding-left: 12px;
}
</style>

View File

@ -106,7 +106,7 @@ async function handleDelete(id: number) {
}
async function edit(id: number) {
handleEdit('#foreach ($column in $columns) #if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) $column.javaField #end #end', id);
handleEdit('#foreach($column in $columns)#if($column.isPk == '1')$column.javaField#end#end', id);
}
</script>

View File

@ -6,11 +6,11 @@
*/
namespace ${ModuleName} {
/** ${businessName} */
type ${ClassName} = Common.CommonRecord<{
#foreach($column in $columns)
/** $column.columnComment */
$column.javaField:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) CommonType.IdType; #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; #elseif($column.javaType == 'Boolean') boolean; #else string; #end
#end
type ${BusinessName} = Common.CommonRecord<{
#foreach($column in $columns)#if($column.insert || $column.edit)
/** $column.columnComment */
$column.javaField:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) CommonType.IdType; #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; #elseif($column.javaType == 'Boolean') boolean; #else string; #end
#end#end
}>;
/** ${businessName} search params */
@ -27,11 +27,11 @@ namespace ${ModuleName} {
#else
#set($comment=$column.columnComment)
#end
#if($foreach.hasNext) | #end ' ${column.javaField}'
#if($foreach.hasNext) |#end '${column.javaField}'
#end
#end
> &
CommonSearchParams
Common.CommonSearchParams
>;
/** ${businessName} list */