Compare commits

..

3 Commits

16 changed files with 1350 additions and 104 deletions

17
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
version: 2
updates:
- package-ecosystem: "maven"
directory: "/"
open-pull-requests-limit: 10
ignore:
# 忽略通过 BOM 管理的 Vert.x 依赖
# 这些依赖的版本通过 vertx-dependencies BOM 统一管理
# 应该通过更新 pom.xml 中的 vertx.version 属性来更新这些依赖
- dependency-name: "io.vertx:vertx-web"
- dependency-name: "io.vertx:vertx-codegen"
- dependency-name: "io.vertx:vertx-config"
- dependency-name: "io.vertx:vertx-config-yaml"
- dependency-name: "io.vertx:vertx-service-proxy"
- dependency-name: "io.vertx:vertx-web-proxy"
- dependency-name: "io.vertx:vertx-web-client"

2
.gitignore vendored
View File

@@ -29,6 +29,8 @@ target/
/src/logs/ /src/logs/
*.zip *.zip
sdkTest.log sdkTest.log
app.yml
app-local.yml
#some local files #some local files

View File

@@ -128,7 +128,9 @@ public class ReverseProxyVerticle extends AbstractVerticle {
} }
private HttpServer getHttpsServer(JsonObject proxyConf) { private HttpServer getHttpsServer(JsonObject proxyConf) {
HttpServerOptions httpServerOptions = new HttpServerOptions(); HttpServerOptions httpServerOptions = new HttpServerOptions()
.setCompressionSupported(true);
if (proxyConf.containsKey("ssl")) { if (proxyConf.containsKey("ssl")) {
JsonObject sslConfig = proxyConf.getJsonObject("ssl"); JsonObject sslConfig = proxyConf.getJsonObject("ssl");
@@ -182,6 +184,7 @@ public class ReverseProxyVerticle extends AbstractVerticle {
} else { } else {
staticHandler = StaticHandler.create(); staticHandler = StaticHandler.create();
} }
if (staticConf.containsKey("directory-listing")) { if (staticConf.containsKey("directory-listing")) {
staticHandler.setDirectoryListing(staticConf.getBoolean("directory-listing")); staticHandler.setDirectoryListing(staticConf.getBoolean("directory-listing"));
} else if (staticConf.containsKey("index")) { } else if (staticConf.containsKey("index")) {

View File

@@ -244,19 +244,17 @@ public class JsHttpClient {
if (data != null) { if (data != null) {
if (data instanceof String) { if (data instanceof String) {
request.sendBuffer(Buffer.buffer((String) data)); return request.sendBuffer(Buffer.buffer((String) data));
} else if (data instanceof Map) { } else if (data instanceof Map) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, String> mapData = (Map<String, String>) data; Map<String, String> mapData = (Map<String, String>) data;
request.sendForm(MultiMap.caseInsensitiveMultiMap().addAll(mapData)); return request.sendForm(MultiMap.caseInsensitiveMultiMap().addAll(mapData));
} else { } else {
request.sendJson(data); return request.sendJson(data);
} }
} else { } else {
request.send();
}
return request.send(); return request.send();
}
}); });
} }
@@ -276,19 +274,17 @@ public class JsHttpClient {
if (data != null) { if (data != null) {
if (data instanceof String) { if (data instanceof String) {
request.sendBuffer(Buffer.buffer((String) data)); return request.sendBuffer(Buffer.buffer((String) data));
} else if (data instanceof Map) { } else if (data instanceof Map) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, String> mapData = (Map<String, String>) data; Map<String, String> mapData = (Map<String, String>) data;
request.sendForm(MultiMap.caseInsensitiveMultiMap().addAll(mapData)); return request.sendForm(MultiMap.caseInsensitiveMultiMap().addAll(mapData));
} else { } else {
request.sendJson(data); return request.sendJson(data);
} }
} else { } else {
request.send();
}
return request.send(); return request.send();
}
}); });
} }
@@ -322,19 +318,17 @@ public class JsHttpClient {
if (data != null) { if (data != null) {
if (data instanceof String) { if (data instanceof String) {
request.sendBuffer(Buffer.buffer((String) data)); return request.sendBuffer(Buffer.buffer((String) data));
} else if (data instanceof Map) { } else if (data instanceof Map) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, String> mapData = (Map<String, String>) data; Map<String, String> mapData = (Map<String, String>) data;
request.sendForm(MultiMap.caseInsensitiveMultiMap().addAll(mapData)); return request.sendForm(MultiMap.caseInsensitiveMultiMap().addAll(mapData));
} else { } else {
request.sendJson(data); return request.sendJson(data);
} }
} else { } else {
request.send();
}
return request.send(); return request.send();
}
}); });
} }

View File

@@ -111,7 +111,7 @@ public class JsPlaygroundExecutor {
playgroundLogger.infoJava("✅ Fetch API和Promise polyfill注入成功"); playgroundLogger.infoJava("✅ Fetch API和Promise polyfill注入成功");
} }
playgroundLogger.infoJava("🔒 安全的JavaScript引擎初始化成功演练场"); playgroundLogger.infoJava("初始化成功");
// 执行JavaScript代码 // 执行JavaScript代码
engine.eval(jsCode); engine.eval(jsCode);

View File

@@ -549,6 +549,176 @@ public class JsHttpClientTest {
} }
} }
@Test
public void testPostWithJsonString() {
System.out.println("\n[测试16] POST请求JSON字符串 - httpbin.org/post");
System.out.println("测试修复POST请求发送JSON字符串时请求体是否正确发送");
try {
String url = "https://httpbin.org/post";
System.out.println("请求URL: " + url);
// 模拟阿里云盘登录请求格式
String jsonData = "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"test_token_123\"}";
System.out.println("POST数据JSON字符串: " + jsonData);
// 设置Content-Type为application/json
httpClient.putHeader("Content-Type", "application/json");
System.out.println("设置Content-Type: application/json");
System.out.println("开始请求...");
long startTime = System.currentTimeMillis();
JsHttpClient.JsHttpResponse response = httpClient.post(url, jsonData);
long endTime = System.currentTimeMillis();
System.out.println("请求完成,耗时: " + (endTime - startTime) + "ms");
System.out.println("状态码: " + response.statusCode());
String body = response.body();
System.out.println("响应体前500字符: " + (body != null && body.length() > 500 ? body.substring(0, 500) + "..." : body));
// 验证结果
assertNotNull("响应不能为null", response);
assertEquals("状态码应该是200", 200, response.statusCode());
assertNotNull("响应体不能为null", body);
// 验证请求体是否正确发送httpbin会回显请求数据
assertTrue("响应体应该包含发送的JSON数据",
body.contains("grant_type") || body.contains("refresh_token"));
System.out.println("✓ 测试通过 - POST请求体已正确发送");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("POST JSON字符串请求测试失败: " + e.getMessage());
}
}
@Test
public void testAlipanTokenApi() {
System.out.println("\n[测试20] 阿里云盘Token接口测试 - auth.aliyundrive.com/v2/account/token");
System.out.println("参考 alipan.js 中的登录逻辑,测试请求格式是否正确");
try {
String tokenUrl = "https://auth.aliyundrive.com/v2/account/token";
System.out.println("请求URL: " + tokenUrl);
// 参考 alipan.js 中的请求格式
// setJsonHeaders(http) 设置 Content-Type: application/json 和 User-Agent
httpClient.putHeader("Content-Type", "application/json");
httpClient.putHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
// 参考 alipan.js: JSON.stringify({grant_type: "refresh_token", refresh_token: REFRESH_TOKEN})
String jsonData = "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"\"}";
System.out.println("POST数据JSON字符串: " + jsonData);
System.out.println("注意使用无效token测试错误响应格式");
System.out.println("开始请求...");
long startTime = System.currentTimeMillis();
JsHttpClient.JsHttpResponse response = httpClient.post(tokenUrl, jsonData);
long endTime = System.currentTimeMillis();
System.out.println("请求完成,耗时: " + (endTime - startTime) + "ms");
System.out.println("状态码: " + response.statusCode());
String body = response.body();
System.out.println("响应体: " + body);
// 验证结果
assertNotNull("响应不能为null", response);
// 使用无效token应该返回400或401等错误状态码但请求格式应该是正确的
assertTrue("状态码应该是4xx无效token或200如果token有效",
response.statusCode() >= 200 && response.statusCode() < 500);
assertNotNull("响应体不能为null", body);
// 验证响应格式阿里云盘API通常返回JSON
try {
Object jsonResponse = response.json();
System.out.println("响应JSON解析成功: " + jsonResponse);
assertNotNull("JSON响应不能为null", jsonResponse);
} catch (Exception e) {
System.out.println("警告响应不是有效的JSON格式");
}
// 验证请求头是否正确设置
System.out.println("验证请求头设置...");
Map<String, String> headers = httpClient.getHeaders();
assertTrue("应该设置了Content-Type", headers.containsKey("Content-Type"));
assertEquals("Content-Type应该是application/json",
"application/json", headers.get("Content-Type"));
System.out.println("✓ 测试通过 - 请求格式正确已成功发送到阿里云盘API");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
// 如果是超时或其他网络错误,说明请求格式可能有问题
if (e.getMessage() != null && e.getMessage().contains("超时")) {
fail("请求超时,可能是请求格式问题或网络问题: " + e.getMessage());
} else {
fail("阿里云盘Token接口测试失败: " + e.getMessage());
}
}
}
@Test
public void testAlipanTokenApiWithValidFormat() {
System.out.println("\n[测试21] 阿里云盘Token接口格式验证 - 使用httpbin验证请求格式");
System.out.println("通过httpbin回显验证请求格式是否与alipan.js中的格式一致");
try {
// 使用httpbin来验证请求格式
String testUrl = "https://httpbin.org/post";
System.out.println("测试URL: " + testUrl);
// 参考 alipan.js 中的请求格式
httpClient.clearHeaders(); // 清空之前的头
httpClient.putHeader("Content-Type", "application/json");
httpClient.putHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
// 完全模拟 alipan.js 中的请求体格式
String jsonData = "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"test_refresh_token_12345\"}";
System.out.println("POST数据JSON字符串: " + jsonData);
System.out.println("开始请求...");
long startTime = System.currentTimeMillis();
JsHttpClient.JsHttpResponse response = httpClient.post(testUrl, jsonData);
long endTime = System.currentTimeMillis();
System.out.println("请求完成,耗时: " + (endTime - startTime) + "ms");
System.out.println("状态码: " + response.statusCode());
String body = response.body();
System.out.println("响应体前800字符: " + (body != null && body.length() > 800 ? body.substring(0, 800) + "..." : body));
// 验证结果
assertNotNull("响应不能为null", response);
assertEquals("状态码应该是200", 200, response.statusCode());
assertNotNull("响应体不能为null", body);
// httpbin会回显请求数据验证请求体是否正确发送
assertTrue("响应体应该包含grant_type字段", body.contains("grant_type"));
assertTrue("响应体应该包含refresh_token字段", body.contains("refresh_token"));
assertTrue("响应体应该包含发送的refresh_token值", body.contains("test_refresh_token_12345"));
// 验证Content-Type是否正确
assertTrue("响应体应该包含Content-Type信息", body.contains("application/json"));
// 验证User-Agent是否正确
assertTrue("响应体应该包含User-Agent信息", body.contains("Mozilla"));
System.out.println("✓ 测试通过 - 请求格式与alipan.js中的格式完全一致");
System.out.println(" - JSON请求体正确发送");
System.out.println(" - Content-Type正确设置");
System.out.println(" - User-Agent正确设置");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("阿里云盘Token接口格式验证失败: " + e.getMessage());
}
}
@Test @Test
public void testSetTimeout() { public void testSetTimeout() {
System.out.println("\n[测试15] 设置超时时间 - setTimeout方法"); System.out.println("\n[测试15] 设置超时时间 - setTimeout方法");

View File

@@ -3,6 +3,7 @@ module.exports = {
'@vue/cli-plugin-babel/preset' '@vue/cli-plugin-babel/preset'
], ],
plugins: [ plugins: [
'@vue/babel-plugin-transform-vue-jsx' '@vue/babel-plugin-transform-vue-jsx',
'@babel/plugin-transform-class-static-block'
] ]
} }

View File

@@ -5,7 +5,8 @@
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"dev": "vue-cli-service serve", "dev": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build && node scripts/compress-vs.js",
"build:no-compress": "vue-cli-service build",
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
@@ -28,6 +29,7 @@
"@babel/core": "^7.26.0", "@babel/core": "^7.26.0",
"@babel/eslint-parser": "^7.25.9", "@babel/eslint-parser": "^7.25.9",
"@babel/plugin-transform-class-properties": "^7.26.0", "@babel/plugin-transform-class-properties": "^7.26.0",
"@babel/plugin-transform-class-static-block": "^7.26.0",
"@vue/babel-plugin-transform-vue-jsx": "^1.4.0", "@vue/babel-plugin-transform-vue-jsx": "^1.4.0",
"@vue/cli-plugin-babel": "~5.0.8", "@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8", "@vue/cli-plugin-eslint": "~5.0.8",
@@ -35,7 +37,8 @@
"compression-webpack-plugin": "^11.1.0", "compression-webpack-plugin": "^11.1.0",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-plugin-vue": "^9.30.0", "eslint-plugin-vue": "^9.30.0",
"filemanager-webpack-plugin": "8.0.0" "filemanager-webpack-plugin": "8.0.0",
"monaco-editor-webpack-plugin": "^7.1.1"
}, },
"eslintConfig": { "eslintConfig": {
"root": true, "root": true,

124
web-front/scripts/compress-vs.js Executable file
View File

@@ -0,0 +1,124 @@
#!/usr/bin/env node
const path = require("path");
const fs = require("fs");
const zlib = require("zlib");
const { promisify } = require("util");
const gzip = promisify(zlib.gzip);
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
// 递归压缩目录下的所有文件
async function compressDirectory(dirPath, threshold = 1024) {
if (!fs.existsSync(dirPath)) {
console.warn(`目录不存在: ${dirPath}`);
return;
}
const files = await readdir(dirPath, { withFileTypes: true });
let compressedCount = 0;
let totalOriginalSize = 0;
let totalCompressedSize = 0;
for (const file of files) {
const filePath = path.join(dirPath, file.name);
if (file.isDirectory()) {
await compressDirectory(filePath, threshold);
} else if (file.isFile()) {
const stats = await stat(filePath);
// 只压缩超过阈值且不是已压缩的文件
if (stats.size > threshold && !filePath.endsWith('.gz') && !filePath.endsWith('.map')) {
try {
const content = await readFile(filePath);
const compressed = await gzip(content);
await writeFile(filePath + '.gz', compressed);
compressedCount++;
totalOriginalSize += stats.size;
totalCompressedSize += compressed.length;
console.log(`${file.name} (${(stats.size / 1024).toFixed(2)}KB -> ${(compressed.length / 1024).toFixed(2)}KB)`);
} catch (error) {
console.warn(`⚠ 压缩失败: ${filePath}`, error.message);
}
}
}
}
if (compressedCount > 0) {
console.log(`\n压缩完成: ${compressedCount} 个文件`);
console.log(`原始大小: ${(totalOriginalSize / 1024 / 1024).toFixed(2)}MB`);
console.log(`压缩后大小: ${(totalCompressedSize / 1024 / 1024).toFixed(2)}MB`);
console.log(`压缩率: ${((1 - totalCompressedSize / totalOriginalSize) * 100).toFixed(1)}%`);
}
}
// 删除未使用的 worker 文件
function deleteUnusedWorkers() {
const jsDir = path.join(__dirname, '../nfd-front/js');
const workers = ['editor.worker.js', 'editor.worker.js.gz', 'json.worker.js', 'json.worker.js.gz', 'ts.worker.js', 'ts.worker.js.gz'];
let deletedCount = 0;
for (const worker of workers) {
const filePath = path.join(jsDir, worker);
if (fs.existsSync(filePath)) {
try {
fs.unlinkSync(filePath);
deletedCount++;
console.log(`✓ 已删除未使用的文件: ${worker}`);
} catch (error) {
console.warn(`⚠ 删除失败: ${worker}`, error.message);
}
}
}
if (deletedCount > 0) {
console.log(`\n已删除 ${deletedCount} 个未使用的 worker 文件\n`);
}
}
// 复制到 webroot
function copyToWebroot() {
const source = path.join(__dirname, '../nfd-front');
const dest = path.join(__dirname, '../../webroot/nfd-front');
// 使用 FileManagerPlugin 的方式,这里用简单的复制
const { execSync } = require('child_process');
try {
// 删除目标目录
if (fs.existsSync(dest)) {
execSync(`rm -rf "${dest}"`, { stdio: 'inherit' });
}
// 复制整个目录
execSync(`cp -R "${source}" "${dest}"`, { stdio: 'inherit' });
console.log('\n✓ 已复制到 webroot');
} catch (error) {
console.error('\n✗ 复制到 webroot 失败:', error.message);
process.exit(1);
}
}
// 主函数
async function main() {
// 先删除未使用的 worker 文件
deleteUnusedWorkers();
// 然后压缩 vs 目录
const vsPath = path.join(__dirname, '../nfd-front/js/vs');
console.log('开始压缩 vs 目录下的文件...\n');
try {
await compressDirectory(vsPath, 1024); // 只压缩超过1KB的文件
console.log('\n✓ vs 目录压缩完成');
} catch (error) {
console.error('\n✗ vs 目录压缩失败:', error);
process.exit(1);
}
// 最后复制到 webroot
copyToWebroot();
}
main();

View File

@@ -34,6 +34,7 @@ export default {
const editorContainer = ref(null); const editorContainer = ref(null);
let editor = null; let editor = null;
let monaco = null; let monaco = null;
let touchHandlers = { start: null, move: null };
const defaultOptions = { const defaultOptions = {
value: props.modelValue, value: props.modelValue,
@@ -94,13 +95,18 @@ export default {
return; return;
} }
// 配置Monaco Editor使用国内CDN (npmmirror) // 配置Monaco Editor使用本地打包的文件而不是CDN
// npmmirror的路径格式: https://registry.npmmirror.com/包名/版本号/files/文件路径 if (loader.config) {
const vsPath = process.env.NODE_ENV === 'production'
? './js/vs' // 生产环境使用相对路径
: '/js/vs'; // 开发环境使用绝对路径
loader.config({ loader.config({
paths: { paths: {
vs: 'https://registry.npmmirror.com/monaco-editor/0.55.1/files/min/vs' vs: vsPath
} }
}); });
}
// 初始化Monaco Editor // 初始化Monaco Editor
monaco = await loader.init(); monaco = await loader.init();
@@ -131,6 +137,46 @@ export default {
if (editorContainer.value) { if (editorContainer.value) {
editorContainer.value.style.height = props.height; editorContainer.value.style.height = props.height;
} }
// 移动端:添加触摸缩放来调整字体大小
if (window.innerWidth <= 768 && editorContainer.value) {
let initialDistance = 0;
let initialFontSize = defaultOptions.fontSize || 14;
const minFontSize = 8;
const maxFontSize = 24;
const getTouchDistance = (touch1, touch2) => {
const dx = touch1.clientX - touch2.clientX;
const dy = touch1.clientY - touch2.clientY;
return Math.sqrt(dx * dx + dy * dy);
};
touchHandlers.start = (e) => {
if (e.touches.length === 2 && editor) {
initialDistance = getTouchDistance(e.touches[0], e.touches[1]);
initialFontSize = editor.getOption(monaco.editor.EditorOption.fontSize);
}
};
touchHandlers.move = (e) => {
if (e.touches.length === 2 && editor) {
e.preventDefault(); // 防止页面缩放
const currentDistance = getTouchDistance(e.touches[0], e.touches[1]);
const scale = currentDistance / initialDistance;
const newFontSize = Math.round(initialFontSize * scale);
// 限制字体大小范围
const clampedFontSize = Math.max(minFontSize, Math.min(maxFontSize, newFontSize));
if (clampedFontSize !== editor.getOption(monaco.editor.EditorOption.fontSize)) {
editor.updateOptions({ fontSize: clampedFontSize });
}
}
};
editorContainer.value.addEventListener('touchstart', touchHandlers.start, { passive: false });
editorContainer.value.addEventListener('touchmove', touchHandlers.move, { passive: false });
}
} catch (error) { } catch (error) {
console.error('Monaco Editor初始化失败:', error); console.error('Monaco Editor初始化失败:', error);
console.error('错误详情:', error.stack); console.error('错误详情:', error.stack);
@@ -174,6 +220,11 @@ export default {
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
// 清理触摸事件监听器
if (editorContainer.value && touchHandlers.start && touchHandlers.move) {
editorContainer.value.removeEventListener('touchstart', touchHandlers.start);
editorContainer.value.removeEventListener('touchmove', touchHandlers.move);
}
if (editor) { if (editor) {
editor.dispose(); editor.dispose();
} }
@@ -195,10 +246,26 @@ export default {
border: 1px solid #dcdfe6; border: 1px solid #dcdfe6;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
/* 允许用户选择文本 */
-webkit-user-select: text;
user-select: text;
} }
.monaco-editor-container :deep(.monaco-editor) { .monaco-editor-container :deep(.monaco-editor) {
border-radius: 4px; border-radius: 4px;
} }
/* 移动端:禁用页面缩放,只允许编辑器字体缩放 */
@media (max-width: 768px) {
.monaco-editor-container {
/* 禁用页面级别的缩放,只允许编辑器内部字体缩放 */
touch-action: pan-x pan-y;
}
.monaco-editor-container :deep(.monaco-editor) {
/* 禁用页面级别的缩放 */
touch-action: pan-x pan-y;
}
}
</style> </style>

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,10 @@ const path = require("path");
function resolve(dir) { function resolve(dir) {
return path.join(__dirname, dir) return path.join(__dirname, dir)
} }
const CompressionPlugin = require('compression-webpack-plugin'); const CompressionPlugin = require('compression-webpack-plugin');
const FileManagerPlugin = require('filemanager-webpack-plugin') const FileManagerPlugin = require('filemanager-webpack-plugin');
const MonacoEditorPlugin = require('monaco-editor-webpack-plugin');
module.exports = { module.exports = {
productionSourceMap: false, // 是否在构建生产包时生成sourceMap文件false将提高构建速度 productionSourceMap: false, // 是否在构建生产包时生成sourceMap文件false将提高构建速度
@@ -43,7 +45,7 @@ module.exports = {
'@': resolve('src') '@': resolve('src')
} }
}, },
// Monaco Editor配置 - 使用国内CDN // Monaco Editor配置 - 使用本地打包
module: { module: {
rules: [ rules: [
{ {
@@ -53,9 +55,18 @@ module.exports = {
] ]
}, },
plugins: [ plugins: [
new MonacoEditorPlugin({
languages: ['javascript', 'typescript', 'json'],
features: ['coreCommands', 'find', 'format', 'suggest', 'quickCommand'],
publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
// Worker 文件输出路径
filename: 'js/[name].worker.js'
}),
new CompressionPlugin({ new CompressionPlugin({
test: /\.js$|\.html$|\.css/, // 匹配文件 test: /\.js$|\.html$|\.css/, // 匹配文件
threshold: 10240 // 对超过10k文件压缩 threshold: 10240, // 对超过10k文件压缩
// 排除 js 目录下的 worker 文件Monaco Editor 使用 vs/assets 下的)
exclude: /js\/.*\.worker\.js$/
}), }),
new FileManagerPlugin({ //初始化 filemanager-webpack-plugin 插件实例 new FileManagerPlugin({ //初始化 filemanager-webpack-plugin 插件实例
events: { events: {
@@ -70,7 +81,11 @@ module.exports = {
{ source: '../webroot/nfd-front/view/.gitignore', options: { force: true } }, { source: '../webroot/nfd-front/view/.gitignore', options: { force: true } },
], ],
copy: [ copy: [
{ source: './nfd-front', destination: '../webroot/nfd-front' } // 复制 Monaco Editor 的 vs 目录到 js/vs
{
source: './node_modules/monaco-editor/min/vs',
destination: './nfd-front/js/vs'
}
], ],
archive: [ //然后我们选择dist文件夹将之打包成dist.zip并放在根目录 archive: [ //然后我们选择dist文件夹将之打包成dist.zip并放在根目录
{ {

View File

@@ -16,9 +16,9 @@ proxyConf: server-proxy
# JS演练场配置 # JS演练场配置
playground: playground:
# 是否启用演练场默认false不启用 # 是否启用演练场默认false不启用
enabled: false enabled: true
# 公开模式默认false需要密码访问设为true则无需密码 # 公开模式默认false需要密码访问设为true则无需密码
public: false public: true
# 访问密码,建议修改默认密码! # 访问密码,建议修改默认密码!
password: 'nfd_playground_2024' password: 'nfd_playground_2024'