mirror of
https://github.com/m-xlsea/ruoyi-plus-soybean.git
synced 2025-09-23 23:39:47 +08:00
feat: 新增代码生成预览抽屉
This commit is contained in:
@ -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;
|
||||
|
7
build/plugins/monaco-editor.ts
Normal file
7
build/plugins/monaco-editor.ts
Normal 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']
|
||||
});
|
||||
}
|
@ -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
20
pnpm-lock.yaml
generated
@ -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
|
||||
|
227
src/components/common/monaco-editor.vue
Normal file
227
src/components/common/monaco-editor.vue
Normal 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>
|
@ -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',
|
||||
|
@ -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: '其他账号登录',
|
||||
|
@ -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'
|
||||
});
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -319,7 +319,7 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
|
||||
setIsInitAuthRoute(true);
|
||||
} else {
|
||||
// if fetch user routes failed, reset store
|
||||
authStore.logout();
|
||||
authStore.resetStore();
|
||||
}
|
||||
}
|
||||
|
||||
|
3
src/typings/api/tool.api.d.ts
vendored
3
src/typings/api/tool.api.d.ts
vendored
@ -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'>>
|
||||
|
1
src/typings/app.d.ts
vendored
1
src/typings/app.d.ts
vendored
@ -299,6 +299,7 @@ declare namespace App {
|
||||
expandColumn: string;
|
||||
columnSetting: string;
|
||||
config: string;
|
||||
login: string;
|
||||
confirm: string;
|
||||
save: string;
|
||||
delete: string;
|
||||
|
1
src/typings/components.d.ts
vendored
1
src/typings/components.d.ts
vendored
@ -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']
|
||||
|
2
src/typings/storage.d.ts
vendored
2
src/typings/storage.d.ts
vendored
@ -40,5 +40,7 @@ declare namespace StorageType {
|
||||
layout: UnionKey.ThemeLayoutMode;
|
||||
siderCollapse: boolean;
|
||||
};
|
||||
/** The login form rember */
|
||||
loginRember: Api.Auth.PwdLoginForm;
|
||||
}
|
||||
}
|
||||
|
@ -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,7 +66,7 @@ async function handleSubmit() {
|
||||
|
||||
async function handleFetchTenantList() {
|
||||
const { data, error } = await fetchTenantList();
|
||||
if (!error) {
|
||||
if (error) return;
|
||||
tenantEnabled.value = data.tenantEnabled;
|
||||
tenantOption.value = data.voList.map(tenant => {
|
||||
return {
|
||||
@ -66,7 +75,6 @@ async function handleFetchTenantList() {
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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')">
|
||||
|
@ -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="权限标识">
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
150
src/views/tool/gen/modules/gen-table-preview-drawer.vue
Normal file
150
src/views/tool/gen/modules/gen-table-preview-drawer.vue
Normal 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>
|
@ -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>
|
||||
|
||||
|
@ -6,11 +6,11 @@
|
||||
*/
|
||||
namespace ${ModuleName} {
|
||||
/** ${businessName} */
|
||||
type ${ClassName} = Common.CommonRecord<{
|
||||
#foreach($column in $columns)
|
||||
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#end
|
||||
}>;
|
||||
|
||||
/** ${businessName} search params */
|
||||
@ -31,7 +31,7 @@ namespace ${ModuleName} {
|
||||
#end
|
||||
#end
|
||||
> &
|
||||
CommonSearchParams
|
||||
Common.CommonSearchParams
|
||||
>;
|
||||
|
||||
/** ${businessName} list */
|
||||
|
Reference in New Issue
Block a user