2026.09.09 | 端全面重构为 jQuery3 + Bootstrap 5.3 + CodeMirror 5

2026.09.09 | 🎨 前端全面重构为 jQuery3 + Bootstrap 5.3 + CodeMirror 5:全新 `newui3` 现代化界面,`data-bs-theme` 原生深/浅色模式(CodeMirror 双主题跟随),输出区按模板类型动态切换语法高亮(Java/XML/SQL/JSON/JS);新增 `app.js` 独立命名空间 `CodeGenApp`(与旧版 main.js 零冲突),补齐本地 mode 文件(sql/clike/xml/javascript);新增🏺古法印章开关(isHeritage,致敬古法编程非遗传承者);模板选择区改为组内联布局,`template.json` 支持 `tier` 字段分级(main 常驻 / legacy 折叠),折叠与选中状态 localStorage 记忆;修复 JDK23+ 下 Lombok 注解处理不生效的编译问题; |
This commit is contained in:
Moshow郑锴
2026-09-09 22:39:13 +08:00
parent 32f2a40726
commit d6a09de78b
15 changed files with 1044 additions and 10 deletions

View File

@@ -309,6 +309,7 @@ ResultVo.error(message);
| 更新日期 | 更新内容 |
| :--------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 2026.09.09 | 🎨 前端全面重构为 jQuery3 + Bootstrap 5.3 + CodeMirror 5:全新 `newui3` 现代化界面,`data-bs-theme` 原生深/浅色模式(CodeMirror 双主题跟随),输出区按模板类型动态切换语法高亮(Java/XML/SQL/JSON/JS);新增 `app.js` 独立命名空间 `CodeGenApp`(与旧版 main.js 零冲突),补齐本地 mode 文件(sql/clike/xml/javascript);新增🏺古法印章开关(isHeritage,致敬古法编程非遗传承者);模板选择区改为组内联布局,`template.json` 支持 `tier` 字段分级(main 常驻 / legacy 折叠),折叠与选中状态 localStorage 记忆;修复 JDK23+ 下 Lombok 注解处理不生效的编译问题; |
| 2026.06.02 | 📦 新增"一键 ZIP 打包下载"功能:在生成按钮旁新增独立下载按钮,按模板 group 自动分目录打包成 ZIP;<br>支持 `template.json` 中 `fileName` 字段(含 `${className}` 占位符),智能推断文件后缀; |
| 2025.12.09 | 优化Mybatis和Mybatis-Plus模板 |
| 2025.12.08 | 引入单元测试和JaCoCo测试覆盖率,优化代码覆盖率 [UNIT\_TEST\_DOCUMENT.md](UNIT_TEST_DOCUMENT.md) |

29
pom.xml
View File

@@ -183,13 +183,28 @@
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Maven Compiler Plugin:JDK23+ 默认禁用隐式注解处理,需显式声明 Lombok 处理器 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<!-- Maven Surefire Plugin for JUnit 5 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

@@ -19,12 +19,12 @@ public class PageController {
@GetMapping("/")
public ModelAndView defaultPage() {
return new ModelAndView("newui2").addObject("value", valueUtil);
return new ModelAndView("newui3").addObject("value", valueUtil);
}
@GetMapping("/index")
public ModelAndView indexPage() {
return new ModelAndView("newui2").addObject("value", valueUtil);
return new ModelAndView("newui3").addObject("value", valueUtil);
}
}

View File

@@ -0,0 +1,534 @@
/**
* CodeGenApp — 大狼狗代码生成器前端应用(jQuery3 + Bootstrap 5.3 + CodeMirror 5)
*
* 独立命名空间实现,与旧版 main.js 完全隔离,可共存互不影响。
* 页面约定:
* - [data-action] 按钮行为(generate / copy / download-zip / toggle-theme)
* - [data-cg-opt] 生成选项控件(name 即 options key)
* - [data-template] 模板按钮(name 即模板名)
* - #cg-input/#cg-output CodeMirror 挂载点
*/
(function (window, document) {
'use strict';
var $ = window.jQuery;
var axios = window.axios;
var CodeMirror = window.CodeMirror;
var API = {
generate: (window.basePath || '') + '/code/generate',
generateZip: (window.basePath || '') + '/code/generate-zip',
templates: (window.basePath || '') + '/template/all'
};
var COOKIE_KEYS = ['authorName', 'packageName', 'returnUtilSuccess', 'returnUtilFailure',
'ignorePrefix', 'tinyintTransType', 'timeTransType'];
var COOKIE_AGE = 365 * 24 * 3600;
var HISTORY_LIMIT = 9;
var HISTORY_PREFIX = 'cg:history:';
var THEME_KEY = 'cg:theme';
var FOLD_KEY = 'cg:templates-fold';
var TEMPLATE_KEY = 'cg:template';
var SAMPLE_SQL = 'CREATE TABLE \'sys_user_info\' (\n' +
' \'user_id\' int(11) NOT NULL AUTO_INCREMENT COMMENT \'用户编号\',\n' +
' \'user_name\' varchar(255) NOT NULL COMMENT \'用户名\',\n' +
' \'status\' tinyint(1) NOT NULL COMMENT \'状态\',\n' +
' \'create_time\' datetime NOT NULL COMMENT \'创建时间\',\n' +
' PRIMARY KEY (\'user_id\')\n' +
') ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=\'用户信息\'';
var state = {
options: {
dataType: 'sql',
authorName: '',
packageName: '',
returnUtilSuccess: '',
returnUtilFailure: '',
ignorePrefix: 'sys_',
tinyintTransType: 'int',
nameCaseType: 'CamelCase',
timeTransType: 'Date',
isPackageType: true,
isSwagger: false,
isAutoImport: false,
isWithPackage: false,
isComment: true,
isLombok: true,
isHeritage: true
},
templates: [],
fileNameMap: {},
history: [],
currentTemplate: 'plusentity',
outputJson: {},
oemOutputStr: '',
foldOpen: false
};
var els = {};
var editors = { input: null, output: null };
/* ============================== utils ============================== */
function notify(type, msg) {
if (window.toastr) {
toastr[type === 'warning' ? 'warning' : (type === 'error' ? 'error' : 'success')](msg);
} else {
window.alert(msg);
}
}
function escapeHtml(str) {
return String(str == null ? '' : str).replace(/[&<>"']/g, function (c) {
return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
});
}
/* ============================== cookies ============================== */
function writeCookie(key, val) {
try {
document.cookie = key + '=' + encodeURIComponent(val == null ? '' : val) +
';path=' + (window.basePath || '/') + ';max-age=' + COOKIE_AGE;
} catch (e) { /* ignore */ }
}
function readCookie(key) {
try {
var pairs = document.cookie.split(';');
for (var i = 0; i < pairs.length; i++) {
var kv = pairs[i].split('=');
if (kv[0].replace(/^\s+/, '') === key) {
return decodeURIComponent(kv.slice(1).join('='));
}
}
} catch (e) { /* ignore */ }
return '';
}
function saveCookies() {
COOKIE_KEYS.forEach(function (key) {
writeCookie(key, state.options[key]);
});
}
function loadCookies() {
COOKIE_KEYS.forEach(function (key) {
var val = readCookie(key);
if (val !== '') {
state.options[key] = val;
}
});
}
/* ============================== theme ============================== */
function resolveInitialTheme() {
try {
var saved = localStorage.getItem(THEME_KEY);
if (saved === 'dark' || saved === 'light') {
return saved;
}
} catch (e) { /* ignore */ }
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
}
function editorTheme() {
return document.documentElement.getAttribute('data-bs-theme') === 'dark'
? 'material-darker' : 'idea';
}
function applyTheme(mode, persist) {
document.documentElement.setAttribute('data-bs-theme', mode);
if (editors.input) {
editors.input.setOption('theme', editorTheme());
}
if (editors.output) {
editors.output.setOption('theme', editorTheme());
}
if (els.themeBtn.length) {
els.themeBtn.html(mode === 'dark' ? '<i class="bi bi-sun"></i>' : '<i class="bi bi-moon-stars"></i>');
}
if (persist) {
try { localStorage.setItem(THEME_KEY, mode); } catch (e) { /* ignore */ }
}
}
function toggleTheme() {
applyTheme(document.documentElement.getAttribute('data-bs-theme') === 'dark' ? 'light' : 'dark', true);
}
/* ============================== editors ============================== */
function initEditors() {
editors.input = CodeMirror.fromTextArea(document.getElementById('cg-input'), {
mode: 'text/x-sql',
theme: editorTheme(),
lineNumbers: true,
smartIndent: true,
autoCloseBrackets: true,
styleActiveLine: true
});
editors.output = CodeMirror.fromTextArea(document.getElementById('cg-output'), {
mode: 'text/x-java',
theme: editorTheme(),
lineNumbers: true,
smartIndent: true,
readOnly: false,
autoCloseBrackets: true
});
editors.input.setValue(SAMPLE_SQL);
}
function modeForTemplate(name) {
var fileName = (state.fileNameMap[name] || name).toLowerCase();
if (/\.(xml|html|vue)$/.test(fileName)) { return 'application/xml'; }
if (/\.sql$/.test(fileName)) { return 'text/x-sql'; }
if (/\.json$/.test(fileName)) { return 'application/json'; }
if (/\.(jjs|js)$/.test(fileName)) { return 'text/javascript'; }
return 'text/x-java';
}
function setOutput(text) {
editors.output.setOption('mode', modeForTemplate(state.currentTemplate));
editors.output.setValue(text == null ? '' : text);
}
/* ============================== options ============================== */
function applyOptionsToDom() {
$('[data-cg-opt]').each(function () {
var key = this.getAttribute('data-cg-opt');
var val = state.options[key];
if (this.type === 'checkbox') {
this.checked = val === true || val === 'true';
} else {
this.value = val == null ? '' : val;
}
});
}
function collectOptions() {
$('[data-cg-opt]').each(function () {
state.options[this.getAttribute('data-cg-opt')] =
this.type === 'checkbox' ? this.checked : this.value;
});
}
/* ============================== templates ============================== */
function loadTemplates() {
axios.post(API.templates, {}).then(function (res) {
state.templates = (res.data && res.data.data) || [];
state.templates.forEach(function (group) {
(group.templates || []).forEach(function (t) {
state.fileNameMap[t.name] = t.fileName || '';
});
});
renderTemplates();
}).catch(function () {
els.templates.html('<div class="text-danger small">模板加载失败,请刷新页面重试</div>');
});
}
function renderGroup(group) {
var html = '<div class="cg-template-row">' +
'<span class="cg-template-label">' + escapeHtml(group.group) + '</span>' +
'<div class="d-flex flex-wrap gap-1">';
(group.templates || []).forEach(function (t) {
var active = t.name === state.currentTemplate;
html += '<button type="button" class="btn btn-sm cg-template-btn ' +
(active ? 'btn-primary' : 'btn-outline-secondary') + '" data-template="' +
escapeHtml(t.name) + '"' + (active ? ' aria-pressed="true"' : '') + ' title="' +
escapeHtml(t.description || t.name) + '">' + escapeHtml(t.name) + '</button>';
});
html += '</div></div>';
return html;
}
function renderTemplates() {
if (!els.templates.length) { return; }
var mainHtml = '';
var legacyHtml = '';
var legacyCount = 0;
var currentInLegacy = false;
state.templates.forEach(function (group) {
var rowHtml = renderGroup(group);
if (group.tier === 'legacy') {
legacyHtml += rowHtml;
legacyCount++;
(group.templates || []).forEach(function (t) {
if (t.name === state.currentTemplate) { currentInLegacy = true; }
});
} else {
mainHtml += rowHtml;
}
});
var html = mainHtml;
if (legacyHtml) {
var open = state.foldOpen || currentInLegacy;
html += '<details class="cg-fold" id="cgFold"' + (open ? ' open' : '') + '>' +
'<summary><i class="bi bi-archive me-1"></i>更多模板 · 低频/遗产组(' + legacyCount + ' 组)</summary>' +
'<div class="cg-fold-body">' + legacyHtml + '</div></details>';
}
els.templates.html(html || '<div class="text-body-secondary small">未找到模板</div>');
var fold = document.getElementById('cgFold');
if (fold) {
fold.addEventListener('toggle', function () {
state.foldOpen = fold.open;
try { localStorage.setItem(FOLD_KEY, fold.open ? 'open' : 'closed'); } catch (e) { /* ignore */ }
});
}
}
function selectTemplate(name) {
state.currentTemplate = name;
try { localStorage.setItem(TEMPLATE_KEY, name); } catch (e) { /* ignore */ }
renderTemplates();
var content = state.outputJson[name];
setOutput(typeof content === 'string' ? content.trim() : '');
if (els.currentTemplate.length) {
if (state.outputJson && state.outputJson.tableName) {
els.currentTemplate.text(name).show();
} else {
els.currentTemplate.hide();
}
}
}
/* ============================== history ============================== */
function saveHistory(tableName) {
if (!tableName) { return; }
try {
sessionStorage.setItem(HISTORY_PREFIX + tableName, JSON.stringify(state.outputJson));
} catch (e) { /* ignore */ }
var idx = state.history.indexOf(tableName);
if (idx >= 0) { state.history.splice(idx, 1); }
state.history.unshift(tableName);
if (state.history.length > HISTORY_LIMIT) {
var removed = state.history.splice(HISTORY_LIMIT);
removed.forEach(function (t) {
try { sessionStorage.removeItem(HISTORY_PREFIX + t); } catch (e) { /* ignore */ }
});
}
renderHistory();
}
function renderHistory() {
if (!els.historyChips.length) { return; }
if (!state.history.length) {
els.historyBar.hide();
return;
}
els.historyBar.show();
els.historyChips.html(state.history.map(function (name) {
return '<button type="button" class="btn btn-outline-secondary btn-sm cg-chip" data-history="' +
escapeHtml(name) + '"><i class="bi bi-clock-history me-1"></i>' + escapeHtml(name) + '</button>';
}).join(''));
}
function selectHistory(tableName) {
var raw = null;
try { raw = sessionStorage.getItem(HISTORY_PREFIX + tableName); } catch (e) { /* ignore */ }
if (!raw) {
notify('error', '历史记录已失效');
return;
}
try {
state.outputJson = JSON.parse(raw);
} catch (e) {
notify('error', '历史记录解析失败');
return;
}
selectTemplate(state.currentTemplate);
notify('success', '已切换到 ' + tableName);
}
/* ============================== actions ============================== */
function setBusy(busy) {
els.generateBtn.prop('disabled', busy);
els.zipBtn.prop('disabled', busy);
}
function generate() {
var tableSql = editors.input.getValue();
if (!tableSql || tableSql.trim().length < 5) {
notify('warning', '请先输入 SQL / JSON 表结构');
return;
}
collectOptions();
setBusy(true);
axios.post(API.generate, { tableSql: tableSql, options: state.options }).then(function (res) {
if (res.status === 500 || (res.data && res.data.code === 500)) {
notify('error', '生成失败,请检查 SQL 语句!' + (res.data && res.data.msg ? ' ' + res.data.msg : ''));
return;
}
state.outputJson = (res.data && res.data.data) || {};
saveCookies();
saveHistory(state.outputJson.tableName);
selectTemplate(state.currentTemplate);
notify('success', '生成成功');
}).catch(function (err) {
notify('error', '生成失败:' + (err && err.message ? err.message : '网络异常'));
}).finally(function () {
setBusy(false);
});
}
function fallbackCopy(text) {
var ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try {
document.execCommand('copy');
notify('success', '已复制');
} catch (e) {
notify('error', '复制失败,请手动选择复制');
}
document.body.removeChild(ta);
}
function copyOutput() {
var text = editors.output.getValue();
if (!text || !text.trim()) {
notify('warning', '输出内容为空');
return;
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function () {
notify('success', '已复制');
}).catch(function () {
fallbackCopy(text);
});
} else {
fallbackCopy(text);
}
}
function downloadZip() {
var tableSql = editors.input.getValue();
if (!tableSql || tableSql.trim().length < 5) {
notify('warning', '请先输入 SQL / JSON 表结构');
return;
}
collectOptions();
setBusy(true);
axios.post(API.generateZip, { tableSql: tableSql, options: state.options },
{ responseType: 'blob', timeout: 60000 }).then(function (res) {
if (res.status !== 200) {
notify('error', '下载失败,HTTP 状态码:' + res.status);
return;
}
var dispo = res.headers && (res.headers['content-disposition'] || res.headers['Content-Disposition']);
var fileName = 'code-generator.zip';
if (dispo) {
var matchStar = /filename\*=UTF-8''([^;]+)/i.exec(dispo);
var matchQuoted = /filename="?([^";]+)"?/i.exec(dispo);
if (matchStar && matchStar[1]) {
fileName = decodeURIComponent(matchStar[1]);
} else if (matchQuoted && matchQuoted[1]) {
fileName = matchQuoted[1];
}
}
var blob = new Blob([res.data], { type: 'application/zip' });
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
notify('success', '已下载:' + fileName);
}).catch(function (err) {
notify('error', '下载失败:' + (err && err.message ? err.message : '未知错误'));
}).finally(function () {
setBusy(false);
});
}
/* ============================== bootstrap ============================== */
var actions = {
'generate': generate,
'copy': copyOutput,
'download-zip': downloadZip,
'toggle-theme': toggleTheme
};
function cacheEls() {
els.themeBtn = $('#cgThemeBtn');
els.templates = $('#cgTemplates');
els.historyBar = $('#cgHistoryBar');
els.historyChips = $('#cgHistoryChips');
els.currentTemplate = $('#cgCurrentTemplate');
els.generateBtn = $('[data-action="generate"]');
els.zipBtn = $('[data-action="download-zip"]');
}
function bindEvents() {
$(document).on('click', '[data-action]', function (e) {
e.preventDefault();
var fn = actions[$(this).data('action')];
if (fn) { fn(); }
});
els.templates.on('click', 'button[data-template]', function () {
selectTemplate($(this).data('template'));
});
els.historyChips.on('click', 'button[data-history]', function () {
selectHistory($(this).data('history'));
});
}
function init() {
cacheEls();
if (window.toastr) {
toastr.options = { positionClass: 'toast-top-center', timeOut: 2200, preventDuplicates: true };
}
initEditors();
applyTheme(resolveInitialTheme(), false);
loadCookies();
try { state.foldOpen = localStorage.getItem(FOLD_KEY) === 'open'; } catch (e) { /* ignore */ }
try {
var savedTemplate = localStorage.getItem(TEMPLATE_KEY);
if (savedTemplate) { state.currentTemplate = savedTemplate; }
} catch (e) { /* ignore */ }
applyOptionsToDom();
bindEvents();
loadTemplates();
if (state.oemOutputStr) {
setOutput(state.oemOutputStr);
}
}
/* ============================== public api ============================== */
window.CodeGenApp = {
boot: function (config) {
config = config || {};
['authorName', 'packageName', 'returnUtilSuccess', 'returnUtilFailure'].forEach(function (k) {
if (config[k]) { state.options[k] = config[k]; }
});
state.oemOutputStr = config.outputStr || '';
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
},
generate: generate,
copy: copyOutput,
downloadZip: downloadZip,
toggleTheme: toggleTheme
};
})(window, document);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.cm-s-material-darker.CodeMirror{background-color:#212121;color:#eff}.cm-s-material-darker .CodeMirror-gutters{background:#212121;color:#545454;border:none}.cm-s-material-darker .CodeMirror-guttermarker,.cm-s-material-darker .CodeMirror-guttermarker-subtle,.cm-s-material-darker .CodeMirror-linenumber{color:#545454}.cm-s-material-darker .CodeMirror-cursor{border-left:1px solid #fc0}.cm-s-material-darker div.CodeMirror-selected{background:rgba(97,97,97,.2)}.cm-s-material-darker.CodeMirror-focused div.CodeMirror-selected{background:rgba(97,97,97,.2)}.cm-s-material-darker .CodeMirror-line::selection,.cm-s-material-darker .CodeMirror-line>span::selection,.cm-s-material-darker .CodeMirror-line>span>span::selection{background:rgba(128,203,196,.2)}.cm-s-material-darker .CodeMirror-line::-moz-selection,.cm-s-material-darker .CodeMirror-line>span::-moz-selection,.cm-s-material-darker .CodeMirror-line>span>span::-moz-selection{background:rgba(128,203,196,.2)}.cm-s-material-darker .CodeMirror-activeline-background{background:rgba(0,0,0,.5)}.cm-s-material-darker .cm-keyword{color:#c792ea}.cm-s-material-darker .cm-operator{color:#89ddff}.cm-s-material-darker .cm-variable-2{color:#eff}.cm-s-material-darker .cm-type,.cm-s-material-darker .cm-variable-3{color:#f07178}.cm-s-material-darker .cm-builtin{color:#ffcb6b}.cm-s-material-darker .cm-atom{color:#f78c6c}.cm-s-material-darker .cm-number{color:#ff5370}.cm-s-material-darker .cm-def{color:#82aaff}.cm-s-material-darker .cm-string{color:#c3e88d}.cm-s-material-darker .cm-string-2{color:#f07178}.cm-s-material-darker .cm-comment{color:#545454}.cm-s-material-darker .cm-variable{color:#f07178}.cm-s-material-darker .cm-tag{color:#ff5370}.cm-s-material-darker .cm-meta{color:#ffcb6b}.cm-s-material-darker .cm-attribute{color:#c792ea}.cm-s-material-darker .cm-property{color:#c792ea}.cm-s-material-darker .cm-qualifier{color:#decb6b}.cm-s-material-darker .cm-type,.cm-s-material-darker .cm-variable-3{color:#decb6b}.cm-s-material-darker .cm-error{color:#fff;background-color:#ff5370}.cm-s-material-darker .CodeMirror-matchingbracket{text-decoration:underline;color:#fff!important}

View File

@@ -1,5 +1,6 @@
[{
"group": "ui",
"tier": "main",
"templates": [{
"id": "10",
"name": "swagger-ui",
@@ -34,6 +35,7 @@
},
{
"group": "mybatis",
"tier": "main",
"templates": [{
"id": "20",
"name": "controller",
@@ -80,6 +82,7 @@
},
{
"group": "jpa",
"tier": "main",
"templates": [{
"id": "30",
"name": "entity",
@@ -103,6 +106,7 @@
{
"group": "jdbc-template",
"tier": "main",
"templates": [{
"id": "40",
"name": "jtdao",
@@ -120,6 +124,7 @@
{
"group": "beetlsql",
"tier": "legacy",
"templates": [{
"id": "50",
"name": "beetlmd",
@@ -143,6 +148,7 @@
{
"group": "mybatis-plus",
"tier": "main",
"templates": [{
"id": "60",
"name": "pluscontroller",
@@ -172,6 +178,7 @@
{
"group": "util",
"tier": "main",
"templates": [{
"id": "70",
"name": "beanutil",
@@ -207,6 +214,7 @@
{
"group": "common-mapper",
"tier": "legacy",
"templates": [{
"id": "81",
"name": "tkentity",
@@ -224,6 +232,7 @@
{
"group": "renren-fast",
"tier": "legacy",
"templates": [{
"id": "91",
"name": "menu-sql",
@@ -276,6 +285,7 @@
},
{
"group": "jpa-starp",
"tier": "legacy",
"templates": [{
"id": "101",
"name": "starp-entity",
@@ -298,6 +308,7 @@
},
{
"group": "bi",
"tier": "legacy",
"templates": [{
"id": "201",
"name": "qliksense",
@@ -307,6 +318,7 @@
},
{
"group": "cloud",
"tier": "legacy",
"templates": [
{
"id": "301",
@@ -324,6 +336,7 @@
},
{
"group": "tk-mybatis",
"tier": "legacy",
"templates": [
{
"id": "401",

View File

@@ -0,0 +1,23 @@
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="keywords" content="${(value.keywords)!!}">
<script>
/*统计代码,便于统计流量,请勿移除,谢谢!*/
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?97fd5ca1a4298ac8349c7e0de9029a0f";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
//set base path for fixing the revertProxy forwarding path issue
var basePath = "${request.contextPath}";
</script>
<#if value.mode=='local'>
<#include "/newui3-import-local.html">
<#else>
<#include "/newui3-import-CDN.html">
</#if>

View File

@@ -0,0 +1,22 @@
<!--###################################################-->
<!--### CDN version : jQuery3 + Bootstrap5.3 + CodeMirror5 -->
<!--###################################################-->
<!--jquery | axios | bootstrap5 | icons | toastr-->
<script src="//cdn.staticfile.net/jquery/3.6.0/jquery.min.js"></script>
<script src="//cdn.staticfile.net/axios/0.26.0/axios.min.js"></script>
<script src="//cdn.staticfile.net/bootstrap/5.3.3/js/bootstrap.bundle.min.js"></script>
<link href="//cdn.staticfile.net/bootstrap/5.3.3/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
<link href="//cdn.staticfile.net/bootstrap-icons/1.8.1/font/bootstrap-icons.min.css" type="text/css" rel="stylesheet" />
<script src="//cdn.staticfile.net/toastr.js/2.1.4/toastr.min.js"></script>
<link href="//cdn.staticfile.net/toastr.js/2.1.4/toastr.min.css" type="text/css" rel="stylesheet" />
<!-- import codemirror : core + modes(sql/clike/xml/javascript) + themes(idea/material-darker) -->
<script src="//cdn.staticfile.net/codemirror/5.65.2/codemirror.min.js"></script>
<script src="//cdn.staticfile.net/codemirror/5.65.2/mode/sql/sql.min.js"></script>
<script src="//cdn.staticfile.net/codemirror/5.65.2/mode/clike/clike.min.js"></script>
<script src="//cdn.staticfile.net/codemirror/5.65.2/mode/xml/xml.min.js"></script>
<script src="//cdn.staticfile.net/codemirror/5.65.2/mode/javascript/javascript.min.js"></script>
<link href="//cdn.staticfile.net/codemirror/5.65.2/codemirror.min.css" type="text/css" rel="stylesheet" />
<link href="//cdn.staticfile.net/codemirror/5.65.2/theme/idea.min.css" type="text/css" rel="stylesheet" />
<link href="//cdn.staticfile.net/codemirror/5.65.2/theme/material-darker.min.css" type="text/css" rel="stylesheet" />

View File

@@ -0,0 +1,22 @@
<!--###################################################-->
<!--### Local mode : jQuery3 + Bootstrap5.3 + CodeMirror5 -->
<!--###################################################-->
<!--jquery | axios | bootstrap5 | icons | toastr-->
<script src="${request.contextPath}/statics/libs/jquery/jquery.min.js"></script>
<script src="${request.contextPath}/statics/libs/axios/axios.min.js"></script>
<script src="${request.contextPath}/statics/libs/bootstrap5/js/bootstrap.bundle.min.js"></script>
<link href="${request.contextPath}/statics/libs/bootstrap5/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
<link href="${request.contextPath}/statics/libs/bootstrap-icons/bootstrap-icons.min.css" type="text/css" rel="stylesheet" />
<script src="${request.contextPath}/statics/libs/toastr.js/toastr.min.js"></script>
<link href="${request.contextPath}/statics/libs/toastr.js/toastr.min.css" type="text/css" rel="stylesheet" />
<!-- import codemirror : core + modes(sql/clike/xml/javascript) + themes(idea/material-darker) -->
<script src="${request.contextPath}/statics/libs/codemirror/codemirror.min.js"></script>
<script src="${request.contextPath}/statics/libs/codemirror/mode/sql/sql.min.js"></script>
<script src="${request.contextPath}/statics/libs/codemirror/mode/clike/clike.min.js"></script>
<script src="${request.contextPath}/statics/libs/codemirror/mode/xml/xml.min.js"></script>
<script src="${request.contextPath}/statics/libs/codemirror/mode/javascript/javascript.min.js"></script>
<link href="${request.contextPath}/statics/libs/codemirror/codemirror.min.css" type="text/css" rel="stylesheet" />
<link href="${request.contextPath}/statics/libs/codemirror/theme/idea.min.css" type="text/css" rel="stylesheet" />
<link href="${request.contextPath}/statics/libs/codemirror/theme/material-darker.min.css" type="text/css" rel="stylesheet" />

View File

@@ -0,0 +1,388 @@
<!DOCTYPE html>
<html lang="zh-CN" data-bs-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<#include "/newui3-header.html">
<title>${(value.title)!!}</title>
<style>
:root {
--cg-radius: 12px;
--cg-shadow: 0 1px 2px rgba(var(--bs-emphasis-color-rgb), .06), 0 2px 8px rgba(var(--bs-emphasis-color-rgb), .04);
}
body {
min-height: 100vh;
display: flex;
flex-direction: column;
background-color: var(--bs-tertiary-bg);
}
main { flex: 1 0 auto; }
/* ===== navbar ===== */
.cg-navbar {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
background-color: color-mix(in srgb, var(--bs-body-bg) 85%, transparent);
border-bottom: 1px solid var(--bs-border-color-translucent);
}
.cg-navbar .navbar-brand {
font-weight: 700;
letter-spacing: .3px;
}
.cg-navbar .cg-links a {
color: var(--bs-secondary-color);
text-decoration: none;
margin-left: var(--bs-spacer-3, 1rem);
transition: color .15s ease;
}
.cg-navbar .cg-links a:hover { color: var(--bs-link-color); }
/* ===== cards ===== */
.card {
border: none;
border-radius: var(--cg-radius);
box-shadow: var(--cg-shadow);
}
.card-header {
background-color: transparent;
border-bottom: 1px solid var(--bs-border-color-translucent);
font-weight: 600;
padding: .75rem 1rem;
}
/* ===== callout for OEM description ===== */
.cg-callout {
border-left: 3px solid var(--bs-primary);
border-radius: .5rem;
background-color: var(--bs-secondary-bg);
padding: .75rem 1rem;
font-size: .875rem;
color: var(--bs-secondary-color);
}
/* ===== form ===== */
.cg-opt-label {
font-size: .75rem;
font-weight: 600;
color: var(--bs-secondary-color);
margin-bottom: .25rem;
display: block;
}
.form-select, .form-control { font-size: .875rem; }
.form-switch .form-check-input { cursor: pointer; }
/* ===== template groups (inline label + foldable legacy) ===== */
.cg-template-row {
display: flex;
gap: .75rem;
padding: .2rem 0;
align-items: flex-start;
}
.cg-template-label {
flex: 0 0 92px;
font-size: .75rem;
font-weight: 600;
color: var(--bs-secondary-color);
padding-top: .25rem;
}
.cg-template-btn {
--bs-btn-font-size: .8125rem;
--bs-btn-padding-x: .6rem;
--bs-btn-padding-y: .18rem;
}
.cg-fold {
margin-top: .6rem;
border-top: 1px dashed var(--bs-border-color);
padding-top: .5rem;
}
.cg-fold summary {
cursor: pointer;
font-size: .8125rem;
font-weight: 600;
color: var(--bs-secondary-color);
user-select: none;
list-style: none;
}
.cg-fold summary::-webkit-details-marker { display: none; }
.cg-fold summary::before { content: '\25B8\00a0'; }
.cg-fold[open] summary::before { content: '\25BE\00a0'; }
.cg-fold summary:hover { color: var(--bs-primary); }
.cg-fold-body { padding-top: .4rem; }
/* ===== history chips ===== */
.cg-chip {
--bs-btn-font-size: .75rem;
--bs-btn-padding-x: .55rem;
--bs-btn-padding-y: .1rem;
border-radius: 50rem;
}
/* ===== CodeMirror ===== */
.CodeMirror {
height: 360px;
font-family: var(--bs-font-monospace);
font-size: 13px;
border-radius: .5rem;
border: 1px solid var(--bs-border-color);
}
/* 输入区编辑器:flex 填满整个卡片,min-height 保底(小屏单列时) */
.cg-input-card .card-body {
display: flex;
flex-direction: column;
min-height: 0;
}
.cg-input-card .card-body .CodeMirror {
height: auto;
flex: 1 1 auto;
min-height: 320px;
}
.CodeMirror-gutters {
border-right: 1px solid var(--bs-border-color-translucent);
background-color: var(--bs-secondary-bg);
}
.CodeMirror-cursor { border-left: 2px solid var(--bs-primary); }
/* ===== footer ===== */
.cg-footer {
padding: 1rem 0;
text-align: center;
font-size: .8125rem;
color: var(--bs-secondary-color);
}
[v-cloak] { display: none; }
</style>
</head>
<body>
<!-- ===== Navbar ===== -->
<nav class="navbar navbar-expand sticky-top cg-navbar">
<div class="container-xxl">
<a class="navbar-brand d-flex align-items-center gap-2" href="#">
<i class="bi bi-braces-asterisk fs-4 text-primary"></i>
<span>${(value.title)!!}</span>
</a>
<div class="d-none d-md-block ms-3 small text-body-secondary">${(value.slogan)!!}</div>
<div class="ms-auto d-flex align-items-center">
<div class="cg-links d-none d-sm-block">
<a href="https://github.com/moshowgame/SpringBootCodeGenerator/" target="_blank"><i class="bi bi-github me-1"></i>GitHub</a>
<a href="https://zhengkai.blog.csdn.net/" target="_blank"><i class="bi bi-journal-text me-1"></i>CSDN</a>
</div>
<button type="button" class="btn btn-outline-secondary btn-sm ms-3" id="cgThemeBtn"
data-action="toggle-theme" title="切换深色/浅色模式" aria-label="切换主题">
<i class="bi bi-moon-stars"></i>
</button>
</div>
</div>
</nav>
<main class="container-xxl py-4">
<!-- OEM description -->
<div class="cg-callout mb-4">${(value.description)!!}</div>
<div class="row g-4">
<!-- ===== Input & actions ===== -->
<div class="col-lg-8">
<div class="card h-100 cg-input-card">
<div class="card-header d-flex flex-wrap align-items-center gap-2">
<span><i class="bi bi-terminal me-2 text-primary"></i>输入 SQL / JSON</span>
<div class="ms-auto d-flex flex-wrap gap-2">
<button type="button" class="btn btn-primary btn-sm px-3" data-action="generate">
<i class="bi bi-play-fill me-1"></i>生成
</button>
<button type="button" class="btn btn-outline-primary btn-sm" data-action="copy">
<i class="bi bi-clipboard me-1"></i>复制
</button>
<button type="button" class="btn btn-outline-success btn-sm" data-action="download-zip">
<i class="bi bi-file-earmark-zip me-1"></i>下载 ZIP
</button>
</div>
</div>
<div class="card-body">
<textarea id="cg-input" class="d-none"
placeholder="粘贴建表 DDL / INSERT SQL / SELECT SQL 或 JSON 表结构..."></textarea>
</div>
<div class="card-footer bg-transparent d-flex flex-wrap align-items-center gap-2"
id="cgHistoryBar" style="display:none;">
<span class="text-body-secondary small"><i class="bi bi-clock-history me-1"></i>历史记录</span>
<div class="d-flex flex-wrap gap-1" id="cgHistoryChips"></div>
</div>
</div>
</div>
<!-- ===== Options ===== -->
<div class="col-lg-4">
<div class="card h-100">
<div class="card-header"><i class="bi bi-sliders me-2 text-primary"></i>生成设置</div>
<div class="card-body">
<div class="row g-3">
<div class="col-12">
<label class="cg-opt-label" for="opt-dataType">解析引擎</label>
<select id="opt-dataType" class="form-select" data-cg-opt="dataType">
<option value="sql" selected>DDL SQL · 自研解析引擎</option>
<option value="select-sql">SELECT SQL · JSqlParser</option>
<option value="create-sql">CREATE SQL · JSqlParser</option>
<option value="json">JSON (Beta)</option>
<option value="insert-sql">INSERT SQL</option>
</select>
</div>
<div class="col-6">
<label class="cg-opt-label" for="opt-authorName">作者</label>
<input id="opt-authorName" type="text" class="form-control" data-cg-opt="authorName" placeholder="author">
</div>
<div class="col-6">
<label class="cg-opt-label" for="opt-packageName">包名</label>
<input id="opt-packageName" type="text" class="form-control" data-cg-opt="packageName" placeholder="com.example">
</div>
<div class="col-6">
<label class="cg-opt-label" for="opt-returnUtilSuccess">返回 (成功)</label>
<input id="opt-returnUtilSuccess" type="text" class="form-control" data-cg-opt="returnUtilSuccess" placeholder="ResponseUtil.success">
</div>
<div class="col-6">
<label class="cg-opt-label" for="opt-returnUtilFailure">返回 (失败)</label>
<input id="opt-returnUtilFailure" type="text" class="form-control" data-cg-opt="returnUtilFailure" placeholder="ResponseUtil.error">
</div>
<div class="col-6">
<label class="cg-opt-label" for="opt-ignorePrefix">忽略前缀</label>
<input id="opt-ignorePrefix" type="text" class="form-control" data-cg-opt="ignorePrefix" placeholder="sys_">
</div>
<div class="col-6">
<label class="cg-opt-label" for="opt-tinyintTransType">TinyInt 转换</label>
<select id="opt-tinyintTransType" class="form-select" data-cg-opt="tinyintTransType">
<option value="int" selected>int</option>
<option value="Integer">Integer</option>
<option value="boolean">boolean</option>
<option value="Boolean">Boolean</option>
<option value="String">String</option>
<option value="Short">Short</option>
</select>
</div>
<div class="col-6">
<label class="cg-opt-label" for="opt-timeTransType">时间类型</label>
<select id="opt-timeTransType" class="form-select" data-cg-opt="timeTransType">
<option value="Date" selected>Date</option>
<option value="DateTime">DateTime</option>
<option value="Time">Time</option>
<option value="Timestamp">Timestamp</option>
<option value="Calendar">Calendar</option>
<option value="LocalDate">LocalDate</option>
<option value="LocalDateTime">LocalDateTime</option>
<option value="LocalTime">LocalTime</option>
</select>
</div>
<div class="col-6">
<label class="cg-opt-label" for="opt-nameCaseType">命名类型</label>
<select id="opt-nameCaseType" class="form-select" data-cg-opt="nameCaseType">
<option value="CamelCase" selected>驼峰</option>
<option value="UnderScoreCase">下划线</option>
</select>
</div>
<div class="col-12">
<hr class="my-1">
</div>
<div class="col-6">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="opt-isPackageType" data-cg-opt="isPackageType" checked>
<label class="form-check-label small" for="opt-isPackageType">包装类型</label>
</div>
</div>
<div class="col-6">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="opt-isComment" data-cg-opt="isComment" checked>
<label class="form-check-label small" for="opt-isComment">字段注释</label>
</div>
</div>
<div class="col-6">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="opt-isLombok" data-cg-opt="isLombok" checked>
<label class="form-check-label small" for="opt-isLombok">Lombok</label>
</div>
</div>
<div class="col-6">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="opt-isSwagger" data-cg-opt="isSwagger">
<label class="form-check-label small" for="opt-isSwagger">Swagger UI</label>
</div>
</div>
<div class="col-6">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="opt-isAutoImport" data-cg-opt="isAutoImport">
<label class="form-check-label small" for="opt-isAutoImport">自动引包</label>
</div>
</div>
<div class="col-6">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="opt-isWithPackage" data-cg-opt="isWithPackage">
<label class="form-check-label small" for="opt-isWithPackage">带包路径</label>
</div>
</div>
<div class="col-12">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="opt-isHeritage" data-cg-opt="isHeritage" checked>
<label class="form-check-label small" for="opt-isHeritage">🏺 古法印章 · 非遗传承</label>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ===== Templates ===== -->
<div class="col-12">
<div class="card">
<div class="card-header"><i class="bi bi-collection me-2 text-primary"></i>模板选择</div>
<div class="card-body" id="cgTemplates">
<div class="text-body-secondary small">模板加载中...</div>
</div>
</div>
</div>
<!-- ===== Output ===== -->
<div class="col-12">
<div class="card cg-output-card">
<div class="card-header d-flex align-items-center gap-2">
<span><i class="bi bi-filetype-java me-2 text-primary"></i>输出代码</span>
<span class="badge text-bg-primary" id="cgCurrentTemplate" style="display:none;"></span>
<button type="button" class="btn btn-outline-primary btn-sm ms-auto" data-action="copy">
<i class="bi bi-clipboard me-1"></i>复制
</button>
</div>
<div class="card-body">
<textarea id="cg-output" class="d-none" placeholder="点击上方模板,查看生成的代码..."></textarea>
</div>
</div>
</div>
</div>
</main>
<!-- ===== Footer ===== -->
<footer class="cg-footer border-top border-opacity-10">
<div class="container-xxl">${(value.copyright)!!}</div>
</footer>
<script src="${request.contextPath}/statics/js/app.js?v=20260909"></script>
<script>
CodeGenApp.boot({
author: "${(value.author)!!}",
packageName: "${(value.packageName)!!}",
returnUtilSuccess: "${(value.returnUtilSuccess)!!}",
returnUtilFailure: "${(value.returnUtilFailure)!!}",
outputStr: "${(value.outputStr)!!}"
});
</script>
</body>
</html>