feat(projects): 使用 highlight.js 替换 monaco-editor

This commit is contained in:
xlsea
2025-12-08 16:37:30 +08:00
parent 90c52d979e
commit 7dd7a936b9
11 changed files with 102 additions and 683 deletions
+1 -3
View File
@@ -7,7 +7,6 @@ import { setupUnocss } from './unocss';
import { setupUnplugin } from './unplugin';
import { setupHtmlPlugin } from './html';
import { setupDevtoolsPlugin } from './devtools';
import { setupMonacoEditorPlugin } from './monaco-editor';
export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) {
const plugins: PluginOption = [
@@ -18,8 +17,7 @@ export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) {
setupUnocss(viteEnv),
...setupUnplugin(viteEnv),
progress(),
setupHtmlPlugin(buildTime),
setupMonacoEditorPlugin()
setupHtmlPlugin(buildTime)
];
return plugins;
-7
View File
@@ -1,7 +0,0 @@
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
export function setupMonacoEditorPlugin() {
return (monacoEditorPlugin as any).default({
languageWorkers: ['editorWorkerService', 'css', 'html', 'json', 'typescript']
});
}
+22 -388
View File
@@ -1,389 +1,23 @@
package org.dromara.generator.util;
//引入依赖和语言
import 'highlight.js/styles/stackoverflow-light.css'
import hljs from "highlight.js/lib/core";
import hljsVuePlugin from "@highlightjs/vue-plugin";
//import "highlight.js/lib/common"; //单一加载
//按需引入语言
import javascript from "highlight.js/lib/languages/javascript";
import java from "highlight.js/lib/languages/java";
import sql from "highlight.js/lib/languages/sql";
import xml from "highlight.js/lib/languages/xml";
import html from "highlight.js/lib/languages/vbscript-html";
import json from "highlight.js/lib/languages/json";
import yaml from "highlight.js/lib/languages/json";
hljs.registerLanguage("javascript", javascript);
hljs.registerLanguage("java", java);
hljs.registerLanguage("yaml", yaml);
hljs.registerLanguage("json", json);
hljs.registerLanguage("sql", sql);
hljs.registerLanguage("xml", xml);
hljs.registerLanguage("html", html);
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.velocity.VelocityContext;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.enums.DataBaseType;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.generator.constant.GenConstants;
import org.dromara.generator.domain.GenTable;
import org.dromara.generator.domain.GenTableColumn;
import java.util.*;
/**
* 模板处理工具类
*
* @author ruoyi
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class VelocityUtils {
/**
* 项目空间路径
*/
private static final String PROJECT_PATH = "main/java";
/**
* mybatis空间路径
*/
private static final String MYBATIS_PATH = "main/resources/mapper";
/**
* 默认上级菜单,系统工具
*/
private static final String DEFAULT_PARENT_MENU_ID = "3";
/**
* 设置模板变量信息
*
* @return 模板列表
*/
public static VelocityContext prepareContext(GenTable genTable) {
String moduleName = genTable.getModuleName();
String businessName = genTable.getBusinessName();
String packageName = genTable.getPackageName();
String tplCategory = genTable.getTplCategory();
String functionName = genTable.getFunctionName();
VelocityContext velocityContext = new VelocityContext();
velocityContext.put("tplCategory", genTable.getTplCategory());
velocityContext.put("tableName", genTable.getTableName());
velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
velocityContext.put("ClassName", genTable.getClassName());
velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));
velocityContext.put("moduleName", StrUtil.toSymbolCase(genTable.getModuleName(), '-'));
velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));
velocityContext.put("businessName", genTable.getBusinessName());
velocityContext.put("business_name", StrUtil.toUnderlineCase(genTable.getBusinessName()));
velocityContext.put("business__name", StrUtil.toSymbolCase(genTable.getBusinessName(), '-'));
velocityContext.put("businessname", StrUtil.toSymbolCase(genTable.getBusinessName(), ' '));
velocityContext.put("basePackage", getPackagePrefix(packageName));
velocityContext.put("packageName", packageName);
velocityContext.put("author", genTable.getFunctionAuthor());
velocityContext.put("datetime", DateUtils.getDate());
velocityContext.put("pkColumn", genTable.getPkColumn());
velocityContext.put("importList", getImportList(genTable));
velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
velocityContext.put("dicts", getDicts(genTable));
velocityContext.put("dictList", getDictList(genTable));
velocityContext.put("pkColumn", genTable.getPkColumn());
velocityContext.put("columns", genTable.getColumns());
velocityContext.put("table", genTable);
velocityContext.put("StrUtil", new StrUtil());
setMenuVelocityContext(velocityContext, genTable);
if (GenConstants.TPL_TREE.equals(tplCategory)) {
setTreeVelocityContext(velocityContext, genTable);
}
return velocityContext;
}
public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) {
String options = genTable.getOptions();
Dict paramsObj = JsonUtils.parseMap(options);
String parentMenuId = getParentMenuId(paramsObj);
context.put("parentMenuId", parentMenuId);
}
public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) {
String options = genTable.getOptions();
Dict paramsObj = JsonUtils.parseMap(options);
String treeCode = getTreecode(paramsObj);
String treeParentCode = getTreeParentCode(paramsObj);
String treeName = getTreeName(paramsObj);
context.put("treeCode", treeCode);
context.put("treeParentCode", treeParentCode);
context.put("treeName", treeName);
context.put("expandColumn", getExpandColumn(genTable));
if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE));
}
if (paramsObj.containsKey(GenConstants.TREE_NAME)) {
context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME));
}
}
/**
* 获取模板信息
*
* @return 模板列表
*/
public static List<String> getTemplateList(String tplCategory) {
List<String> templates = new ArrayList<>();
templates.add("vm/java/domain.java.vm");
templates.add("vm/java/vo.java.vm");
templates.add("vm/java/bo.java.vm");
templates.add("vm/java/mapper.java.vm");
templates.add("vm/java/service.java.vm");
templates.add("vm/java/serviceImpl.java.vm");
templates.add("vm/java/controller.java.vm");
templates.add("vm/xml/mapper.xml.vm");
DataBaseType dataBaseType = DataBaseHelper.getDataBaseType();
if (dataBaseType.isOracle()) {
templates.add("vm/sql/oracle/sql.vm");
} else if (dataBaseType.isPostgreSql()) {
templates.add("vm/sql/postgres/sql.vm");
} else if (dataBaseType.isSqlServer()) {
templates.add("vm/sql/sqlserver/sql.vm");
} else {
templates.add("vm/sql/sql.vm");
}
templates.add("vm/soy/typings/api.d.ts.vm");
templates.add("vm/soy/api/api.ts.vm");
templates.add("vm/soy/modules/search.vue.vm");
templates.add("vm/soy/modules/operate-drawer.vue.vm");
if (GenConstants.TPL_CRUD.equals(tplCategory)) {
templates.add("vm/soy/index.vue.vm");
} else if (GenConstants.TPL_TREE.equals(tplCategory)) {
templates.add("vm/soy/index-tree.vue.vm");
}
return templates;
}
/**
* 获取文件名
*/
public static String getFileName(String template, GenTable genTable) {
// 文件名称
String fileName = "";
// 包路径
String packageName = genTable.getPackageName();
// 模块名
String moduleName = genTable.getModuleName();
// 大写类名
String className = genTable.getClassName();
// 业务名称
String businessName = genTable.getBusinessName();
String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/");
String mybatisPath = MYBATIS_PATH + "/" + moduleName;
String soybeanPath = "soy";
String soybeanModuleName = StrUtil.toSymbolCase(moduleName, '-');
if (template.contains("domain.java.vm")) {
fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);
}
if (template.contains("vo.java.vm")) {
fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className);
}
if (template.contains("bo.java.vm")) {
fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className);
}
if (template.contains("mapper.java.vm")) {
fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
} else if (template.contains("service.java.vm")) {
fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
} else if (template.contains("serviceImpl.java.vm")) {
fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
} else if (template.contains("controller.java.vm")) {
fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className);
} else if (template.contains("mapper.xml.vm")) {
fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);
} else if (template.contains("sql.vm")) {
fileName = businessName + "Menu.sql";
} else if (template.contains("index.vue.vm")) {
fileName = StringUtils.format("{}/views/{}/{}/index.vue", soybeanPath, soybeanModuleName, StrUtil.toSymbolCase(businessName, '-'));
} else if (template.contains("index-tree.vue.vm")) {
fileName = StringUtils.format("{}/views/{}/{}/index.vue", soybeanPath, soybeanModuleName, StrUtil.toSymbolCase(businessName, '-'));
} else if (template.contains("api.d.ts.vm")) {
fileName = StringUtils.format("{}/typings/api/{}.{}.api.d.ts", soybeanPath, soybeanModuleName, StrUtil.toSymbolCase(businessName, '-'));
} else if (template.contains("api.ts.vm")) {
fileName = StringUtils.format("{}/service/api/{}/{}.ts", soybeanPath, soybeanModuleName, StrUtil.toSymbolCase(businessName, '-'));
} else if (template.contains("search.vue.vm")) {
fileName = StringUtils.format("{}/views/{}/{}/modules/{}-search.vue", soybeanPath, soybeanModuleName, StrUtil.toSymbolCase(businessName, '-'), StrUtil.toSymbolCase(businessName, '-'));
} else if (template.contains("operate-drawer.vue.vm")) {
fileName = StringUtils.format("{}/views/{}/{}/modules/{}-operate-drawer.vue", soybeanPath, soybeanModuleName, StrUtil.toSymbolCase(businessName, '-'), StrUtil.toSymbolCase(businessName, '-'));
}
return fileName;
}
/**
* 获取包前缀
*
* @param packageName 包名称
* @return 包前缀名称
*/
public static String getPackagePrefix(String packageName) {
int lastIndex = packageName.lastIndexOf(".");
return StringUtils.substring(packageName, 0, lastIndex);
}
/**
* 根据列类型获取导入包
*
* @param genTable 业务表对象
* @return 返回需要导入的包列表
*/
public static HashSet<String> getImportList(GenTable genTable) {
List<GenTableColumn> columns = genTable.getColumns();
HashSet<String> importList = new HashSet<>();
for (GenTableColumn column : columns) {
if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) {
importList.add("java.util.Date");
importList.add("com.fasterxml.jackson.annotation.JsonFormat");
} else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
importList.add("java.math.BigDecimal");
} else if (!column.isSuperColumn() && "imageUpload".equals(column.getHtmlType())) {
importList.add("org.dromara.common.translation.annotation.Translation");
importList.add("org.dromara.common.translation.constant.TransConstant");
}
}
return importList;
}
/**
* 根据列类型获取字典组
*
* @param genTable 业务表对象
* @return 返回字典组
*/
public static String getDicts(GenTable genTable) {
List<GenTableColumn> columns = genTable.getColumns();
Set<String> dicts = new HashSet<>();
addDicts(dicts, columns);
return StringUtils.join(dicts, ", ");
}
/**
* 添加字典列表
*
* @param dicts 字典列表
* @param columns 列集合
*/
public static void addDicts(Set<String> dicts, List<GenTableColumn> columns) {
for (GenTableColumn column : columns) {
if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(
column.getHtmlType(),
new String[]{GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX})) {
dicts.add("'" + column.getDictType() + "'");
}
}
}
/**
* 根据列类型获取字典组
*
* @param genTable 业务表对象
* @return 返回字典组
*/
public static Set<Map<String, Object>> getDictList(GenTable genTable) {
List<GenTableColumn> columns = genTable.getColumns();
Set<Map<String, Object>> dicts = new HashSet<>();
addDictList(dicts, columns);
return dicts;
}
/**
* 添加字典列表
*
* @param dicts 字典列表
* @param columns 列集合
*/
public static void addDictList(Set<Map<String, Object>> dicts, List<GenTableColumn> columns) {
for (GenTableColumn column : columns) {
if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(
column.getHtmlType(),
new String[]{GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX})) {
Map<String, Object> dict = new HashMap<>();
dict.put("type", column.getDictType());
dict.put("name", StringUtils.toCamelCase(column.getDictType()));
dict.put("immediate", !column.isList());
dicts.add(dict);
}
}
}
/**
* 获取权限前缀
*
* @param moduleName 模块名称
* @param businessName 业务名称
* @return 返回权限前缀
*/
public static String getPermissionPrefix(String moduleName, String businessName) {
return StringUtils.format("{}:{}", moduleName, businessName);
}
/**
* 获取上级菜单ID字段
*
* @param paramsObj 生成其他选项
* @return 上级菜单ID字段
*/
public static String getParentMenuId(Dict paramsObj) {
if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)
&& StringUtils.isNotEmpty(paramsObj.getStr(GenConstants.PARENT_MENU_ID))) {
return paramsObj.getStr(GenConstants.PARENT_MENU_ID);
}
return DEFAULT_PARENT_MENU_ID;
}
/**
* 获取树编码
*
* @param paramsObj 生成其他选项
* @return 树编码
*/
public static String getTreecode(Map<String, Object> paramsObj) {
if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) {
return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)));
}
return StringUtils.EMPTY;
}
/**
* 获取树父编码
*
* @param paramsObj 生成其他选项
* @return 树父编码
*/
public static String getTreeParentCode(Dict paramsObj) {
if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_PARENT_CODE));
}
return StringUtils.EMPTY;
}
/**
* 获取树名称
*
* @param paramsObj 生成其他选项
* @return 树名称
*/
public static String getTreeName(Dict paramsObj) {
if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) {
return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_NAME));
}
return StringUtils.EMPTY;
}
/**
* 获取需要在哪一列上面显示展开按钮
*
* @param genTable 业务表对象
* @return 展开按钮列序号
*/
public static int getExpandColumn(GenTable genTable) {
String options = genTable.getOptions();
Dict paramsObj = JsonUtils.parseMap(options);
String treeName = paramsObj.getStr(GenConstants.TREE_NAME);
int num = 0;
for (GenTableColumn column : genTable.getColumns()) {
if (column.isList()) {
num++;
String columnName = column.getColumnName();
if (columnName.equals(treeName)) {
break;
}
}
}
return num;
}
}
const app = createApp(App)
app.use(hljsVuePlugin).mount('#app'<
+1 -1
View File
@@ -33,7 +33,7 @@ INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`
UPDATE `sys_menu` SET `component` = 'FrameView', `query_param` = 'https://ruoyi.xlsea.cn/admin/', `is_frame` = 2, `icon` = 'bx:bxl-spring-boot', `menu_name` = 'menu.monitor_admin' WHERE `menu_id` = 117;
UPDATE `sys_menu` SET `component` = 'FrameView', `query_param` = 'https://preview.snailjob.opensnail.com/', `is_frame` = 2, `icon` = 'gridicons:scheduled', `menu_name` = 'menu.monitor_snail-job' WHERE `menu_id` = 120;
-- 外链类型
UPDATE `sys_menu` SET `menu_name` = 'RuoYi-Vue-Plus', `order_num` = 100, `path` = 'https://gitee.com/dromara/RuoYi-Vue-Plus', `component` = 'FrameView', `remark` = 'RuoYi-Vue-Plus 仓库地址' WHERE `menu_id` = 4;
UPDATE `sys_menu` SET `menu_name` = 'RuoYi-Vue-Plus', `order_num` = 100, `path` = 'https://gitee.com/dromara/RuoYi-Vue-Plus', `component` = 'FrameView', `icon` = 'local-icon-gitee', `remark` = 'RuoYi-Vue-Plus 仓库地址' WHERE `menu_id` = 4;
INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (7, 'Soybean Admin', 0, 100, 'https://github.com/soybeanjs', 'FrameView', '', 0, 0, 'M', '0', '0', '', 'mdi:github', 103, 1, sysdate(), null, null, 'Soybean Admin 仓库地址') ON DUPLICATE KEY UPDATE `update_time` = sysdate();
INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (8, 'RuoYi-Plus-Soybean', 0, 100, 'https://gitee.com/xlsea/ruoyi-plus-soybean', 'FrameView', '', 0, 0, 'M', '0', '0', '', 'local-icon-gitee', 103, 1, sysdate(), null, null, 'RuoYi-Plus-Soybean 仓库地址') ON DUPLICATE KEY UPDATE `update_time` = sysdate();
+1 -1
View File
@@ -126,8 +126,8 @@ watch(visible, () => {
restoreValidation();
}
});
#if($tplCategory == 'tree')
const treeOptions = computed(() => {
return [
{
+1 -1
View File
@@ -69,8 +69,8 @@ async function search() {
#end
emit('search');
}
#if($tplCategory == 'tree')
const treeOptions = computed(() => {
return [
{
-2
View File
@@ -75,7 +75,6 @@
"highlight.js": "^11.11.1",
"jsencrypt": "^3.5.4",
"json5": "2.2.3",
"monaco-editor": "^0.55.1",
"naive-ui": "2.43.2",
"nprogress": "0.2.0",
"pinia": "3.0.4",
@@ -114,7 +113,6 @@
"unplugin-icons": "22.5.0",
"unplugin-vue-components": "30.0.0",
"vite": "7.2.6",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "8.0.5",
-37
View File
@@ -59,9 +59,6 @@ importers:
json5:
specifier: 2.2.3
version: 2.2.3
monaco-editor:
specifier: ^0.55.1
version: 0.55.1
naive-ui:
specifier: 2.43.2
version: 2.43.2(vue@3.5.25(typescript@5.9.3))
@@ -171,9 +168,6 @@ importers:
vite:
specifier: 7.2.6
version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(sass@1.94.2)(tsx@4.21.0)(yaml@2.8.2)
vite-plugin-monaco-editor:
specifier: ^1.1.0
version: 1.1.0(monaco-editor@0.55.1)
vite-plugin-progress:
specifier: 0.0.7
version: 0.0.7(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(sass@1.94.2)(tsx@4.21.0)(yaml@2.8.2))
@@ -2921,9 +2915,6 @@ packages:
resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
engines: {node: '>= 4'}
dompurify@3.2.7:
resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==}
dompurify@3.3.0:
resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==}
@@ -3949,11 +3940,6 @@ packages:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
marked@14.0.0:
resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==}
engines: {node: '>= 18'}
hasBin: true
marked@16.4.2:
resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==}
engines: {node: '>= 20'}
@@ -4036,9 +4022,6 @@ packages:
mlly@1.8.0:
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
monaco-editor@0.55.1:
resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==}
mrmime@2.0.1:
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
engines: {node: '>=10'}
@@ -5129,11 +5112,6 @@ 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'}
@@ -8215,10 +8193,6 @@ snapshots:
dependencies:
domelementtype: 2.3.0
dompurify@3.2.7:
optionalDependencies:
'@types/trusted-types': 2.0.7
dompurify@3.3.0:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -9363,8 +9337,6 @@ snapshots:
punycode.js: 2.3.1
uc.micro: 2.1.0
marked@14.0.0: {}
marked@16.4.2: {}
math-intrinsics@1.1.0: {}
@@ -9471,11 +9443,6 @@ snapshots:
pkg-types: 1.3.1
ufo: 1.6.1
monaco-editor@0.55.1:
dependencies:
dompurify: 3.2.7
marked: 14.0.0
mrmime@2.0.1: {}
ms@2.0.0: {}
@@ -10744,10 +10711,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
vite-plugin-monaco-editor@1.1.0(monaco-editor@0.55.1):
dependencies:
monaco-editor: 0.55.1
vite-plugin-progress@0.0.7(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(sass@1.94.2)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
picocolors: 1.1.1
-227
View File
@@ -1,227 +0,0 @@
<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>
+2
View File
@@ -68,6 +68,7 @@ declare module 'vue' {
MenuTreeSelect: typeof import('./../components/custom/menu-tree-select.vue')['default']
MonacoEditor: typeof import('./../components/common/monaco-editor.vue')['default']
NA: typeof import('naive-ui')['NA']
NAffix: typeof import('naive-ui')['NAffix']
NAlert: typeof import('naive-ui')['NAlert']
NAvatar: typeof import('naive-ui')['NAvatar']
NBadge: typeof import('naive-ui')['NBadge']
@@ -217,6 +218,7 @@ declare global {
const MenuTreeSelect: typeof import('./../components/custom/menu-tree-select.vue')['default']
const MonacoEditor: typeof import('./../components/common/monaco-editor.vue')['default']
const NA: typeof import('naive-ui')['NA']
const NAffix: typeof import('naive-ui')['NAffix']
const NAlert: typeof import('naive-ui')['NAlert']
const NAvatar: typeof import('naive-ui')['NAvatar']
const NBadge: typeof import('naive-ui')['NBadge']
@@ -2,9 +2,22 @@
import { ref, watch } from 'vue';
import { useClipboard } from '@vueuse/core';
import { useLoading } from '@sa/hooks';
import hljs from 'highlight.js/lib/core';
import java from 'highlight.js/lib/languages/java';
import xml from 'highlight.js/lib/languages/xml';
import sql from 'highlight.js/lib/languages/sql';
import typescript from 'highlight.js/lib/languages/typescript';
import html from 'highlight.js/lib/languages/vbscript-html';
import plaintext from 'highlight.js/lib/languages/plaintext';
import { fetchGetGenPreview } from '@/service/api/tool';
import { $t } from '@/locales';
import MonacoEditor from '@/components/common/monaco-editor.vue';
hljs.registerLanguage('java', java);
hljs.registerLanguage('xml', xml);
hljs.registerLanguage('sql', sql);
hljs.registerLanguage('typescript', typescript);
hljs.registerLanguage('html', html);
hljs.registerLanguage('plaintext', plaintext);
defineOptions({
name: 'GenTablePreviewDrawer'
@@ -115,31 +128,73 @@ function getGenLanguage(name: string) {
}
if (name.endsWith('.vue')) {
return 'html';
return 'vue';
}
return 'plaintext';
}
function splitVueSFC(source: string) {
const lineNumbers = source.split('\n').length;
const scriptOpenTag = '<script setup';
const scriptCloseTag = `<${'/'}script>`;
// 找到 <script setup ...> 的起始位置
const startIdx = source.indexOf(scriptOpenTag);
if (startIdx === -1) throw new Error('未找到 <script setup>');
// 找到 <script ...> 标签的结束位置(即第一个 >)
const scriptTagEndIdx = source.indexOf('>', startIdx);
const part1 = source.slice(startIdx, scriptTagEndIdx + 1);
// 找到 <\/script>
const endIdx = source.indexOf(scriptCloseTag);
if (endIdx === -1) throw new Error(`未找到 <${'/'}script>`);
// 中间脚本内容
const part2 = source.slice(scriptTagEndIdx + 1, endIdx).trim();
// script 后的内容
const part3 = source.slice(endIdx).trim();
return { part1, part2, part3, lineNumbers };
}
</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">
<div class="flex flex-col">
<NTabs
v-model:value="tab"
type="card"
class="sticky top-0 h-full bg-white dark:bg-#2f2e32"
pane-class="h-full"
>
<NTab v-for="(gen, index) in Object.keys(previewData)" :key="index" :name="gen" display-directive="show">
{{ genMap[gen] }}
</NTab>
</NTabs>
<MonacoEditor
v-model:value="previewData[tab]"
class="tab-pane"
read-only
<div v-if="tab.endsWith('.vue.vm')" class="flex p-16px">
<pre class="code-line-numbers">{{
Array.from({ length: splitVueSFC(previewData[tab]).lineNumbers }, (_, i) => i + 1).join('\n')
}}</pre>
<div>
<NCode :hljs="hljs" :code="splitVueSFC(previewData[tab]).part1" language="xml" />
<NCode :hljs="hljs" :code="splitVueSFC(previewData[tab]).part2" language="typescript" />
<NCode :hljs="hljs" :code="splitVueSFC(previewData[tab]).part3" language="xml" />
</div>
</div>
<NCode
v-else
class="p-16px"
:hljs="hljs"
:code="previewData[tab]"
:language="getGenLanguage(genMap[tab])"
height="calc(100vh - 162px)"
show-line-numbers
/>
<div class="position-absolute right-42px top-2px">
<div class="position-absolute right-42px top-58px">
<NButton text :focusable="false" class="flex-center" @click="handleCopyCode">
<template #icon>
<icon-ep-copy-document class="text-14px" />
@@ -172,11 +227,14 @@ function getGenLanguage(name: string) {
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;
.code-line-numbers {
margin: 0;
line-height: inherit;
font-size: inherit;
user-select: none;
padding-right: 12px;
text-align: right;
transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
color: rgb(118, 124, 130);
}
</style>