设计说明
本意是打算搭建一个在线的代码运行编写平台,方便随时可以在手机上也测试一些简单的代码;
在集成这个插件的时候遇到了不少问题,主要是插件依赖的下载
npm install axios
npm install vue-router@4
npm install element-plus --save
npm install codemirror vue-codemirror --save
npm install @codemirror/lang-javascript
npm install @codemirror/lang-python
npm install @codemirror/lang-cpp
npm install @codemirror/lang-java
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import VueCodemirror from 'vue-codemirror'
import { basicSetup } from "codemirror";
const app = createApp(App)
app.use(router)
app.use(ElementPlus)
app.use(VueCodemirror, {
autofocus: true,
disabled: false,
indentWithTab: true,
tabSize: 4,
placeholder: '在这里编写代码',
extensions: [basicSetup],
})
app.mount("#app")
<template>
<div class="content">
<VueCodemirror :extensions="extensionsOfPython" v-model="data.content" v-if="data.language == 'python'" />
<VueCodemirror :extensions="extensionsOfJavascript" v-model="data.content" v-if="data.language == 'javascript'" />
</div>
<div class="set-font">
<span>当前字体(px) <el-input size="small" class="input-font" v-model="data.fontSize" placeholder="设置字体大小" @change="changeFontSize" /></span>
</div>
<el-button class="exec" size="small" @click="exec">运行</el-button>
<el-select v-model="data.language" class="language" size="small">
<el-option label="python" value="python"></el-option>
<el-option label="javascript" value="javascript"></el-option>
</el-select>
<span @click="writeTip" class="writeTip">书写提示</span>
<div class="result-display">
{{data.resultContent}}
</div>
</template>
<script>
import { reactive } from "vue";
import { basicSetup } from "codemirror";
import { python } from "@codemirror/lang-python";
import { javascript } from "@codemirror/lang-javascript";
import { message, postRequest } from '@/utils/api';
export default {
setup() {
const data = reactive({
content: "\n\n\n\n\n\n\n\n\n",
language: "python",
extensions: [basicSetup, python()],
fontSize: 20,
resultContent: ""
});
const extensionsOfPython = [basicSetup, python()];
const extensionsOfJavascript = [basicSetup, javascript()];
function changeFontSize() {
const inputFont = document.getElementsByClassName("content")[0];
inputFont.style.fontSize = data.fontSize + "px";
}
function exec() {
message("运行请求已提交, 请稍后", "success");
postRequest("/exec", {
content: data.content,
execType: data.language
}).then((res) => {
if (res.data.code == 200) {
message(res.data.msg, "info");
data.resultContent = res.data.data;
} else if (res.data.code == 500) {
message(res.data.msg, "error");
}
});
}
function writeTip() {
message("只支持单文件简单脚本运行", "info");
}
return {
data,
extensionsOfPython,
extensionsOfJavascript,
changeFontSize,
exec,
writeTip,
};
},
};
</script>
<style scoped>
/* 代码编辑区域样式 */
.content {
font-size: 20px;
max-height: 400px;
overflow: scroll;
padding-bottom: 2px;
}
.content::-webkit-scrollbar {
width: 0;
}
/* 切换语言模式下拉框样式 */
.language {
position: absolute;
top: 20px;
right: 30px;
width: 130px;
}
/* 设置字体的样式 */
.set-font {
position: absolute;
top: 70px;
right: 30px;
width: 150px;
height: 30px;
line-height: 30px;
font-size: 14px;
}
.input-font {
width: 50px;
position: relative;
top: -2px;
}
/* 运行按钮样式 */
.exec {
position: absolute;
top: 120px;
right: 30px;
}
/* 结果显示区域样式 */
.result-display {
margin: 0 auto;
margin-top: 10px;
height: calc(100vh - 440px);
width: 90vw;
display: block;
border: 1px solid #212121;
overflow: scroll;
padding: 20px;
box-sizing: border-box;
}
.result-display::-webkit-scrollbar {
width: 0;
}
/* 书写提示样式 */
.writeTip {
position: absolute;
top: 170px;
right: 30px;
cursor: pointer;
color: crimson;
}
</style>
import { ElMessage } from 'element-plus';
import axios from "axios";
axios.defaults.baseURL = "http://127.0.0.1:8900";
export function message(msg, type) {
ElMessage({
message: msg,
showClose: true,
type: type,
center: true
})
}
export const postRequest = (url, data) => {
return axios({
method: 'post',
url: url,
data: data
})
}
后端接口的问题
在使用cmd运行时出现许多问题,一是cd命令无法切换工作目录;另一个是cpp文件与.c文件一直无法运行出exe程序,且运行出的exe程序无法再次被生成(更新)
所以只能采用简单的JavaScript和python语言进行编写
采用的是简单地将文件写入到固定文件下,然后运行cmd命令(或者/bash/sh 命令,产生结果,将结果写回到文件中)(突然想到实际可以采用输出重定向来简化一些书写,并尝试解决一些之前的问题)
package com.boot.controller;
import com.boot.entity.Result;
import com.boot.util.ExecUtil;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Objects;
/**
* @author bbyh
* @date 2023/3/22 0022 16:31
* @description
*/
@RestController
@CrossOrigin(origins = "*", maxAge = 3600)
public class CommandController {
public static final String PYTHON = "python";
public static final String JAVASCRIPT = "javascript";
public static final String USERNAME = "BBYH_";
public static final String OUTPUT_FILENAME = USERNAME + "output.txt";
public static final String INPUT_PYTHON = USERNAME + "input.py";
public static final String INPUT_JS = USERNAME + "input.js";
private static final Integer MAX_SIZE = 1024 * 1024;
@PostMapping("/exec")
public Result exec(@RequestBody Map<String, String> map) {
String content = map.get("content");
String execType = map.get("execType");
if (content.trim().length() == 0 || execType.trim().length() == 0) {
return Result.error(null, "代码内容或代码类型为空, 运行出错");
}
Result result;
switch (execType) {
case PYTHON:
result = initInput(content, INPUT_PYTHON);
break;
case JAVASCRIPT:
result = initInput(content, INPUT_JS);
break;
default:
return Result.error(null, "暂只支持 python、JavaScript程序运行, 且只支持单文件运行");
}
if (Objects.equals(result.getCode(), Result.ERROR)) {
return result;
}
try {
ExecUtil.exec(execType);
} catch (Exception e) {
e.printStackTrace();
return Result.error(null, "命令运行出现错误, 运行出错");
}
ExecUtil.sleep(2000);
char[] buffer = new char[MAX_SIZE];
int read;
try (FileReader reader = new FileReader(OUTPUT_FILENAME)) {
read = reader.read(buffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
if (read <= 0) {
return Result.success(null, "程序执行成功, 但是未打印任何内容, 可加上输出语句, 方便观察程序执行结果");
}
return Result.success(new String(buffer, 0, read), "运行成功");
}
private static Result initInput(String content, String fileName) {
BufferedWriter writer;
try {
writer = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(Paths.get(fileName))));
writer.write(content);
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
return Result.success(null, "");
}
}
package com.boot.util;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Scanner;
import static com.boot.controller.CommandController.*;
/**
* @author bbyh
* @date 2023/2/27 0027 15:10
* @description
*/
public class ExecUtil {
public static void exec(String type) throws Exception {
Process exec = null;
if (judgeLinux()) {
exec = execLinux(type);
} else if (judgeWindows()) {
exec = execWindows(type);
}
if (exec == null) {
return;
}
Scanner scanner = new Scanner(exec.getInputStream());
StringBuilder builder = new StringBuilder();
while (scanner.hasNextLine()) {
builder.append(scanner.nextLine());
}
scanner.close();
try (PrintWriter writer = new PrintWriter(OUTPUT_FILENAME)) {
writer.write(builder.toString());
}
}
private static Process execLinux(String type) throws IOException {
Process exec = null;
ArrayList<String> command = new ArrayList<>();
command.add("/bin/sh");
command.add("-c");
switch (type) {
case PYTHON:
command.add("python " + INPUT_PYTHON);
exec = Runtime.getRuntime().exec(command.toArray(new String[0]));
break;
case JAVASCRIPT:
command.add("node " + INPUT_JS);
exec = Runtime.getRuntime().exec(command.toArray(new String[0]));
break;
}
return exec;
}
private static Process execWindows(String type) throws Exception {
Process exec = null;
switch (type) {
case PYTHON:
exec = Runtime.getRuntime().exec("cmd /c python " + INPUT_PYTHON);
break;
case JAVASCRIPT:
exec = Runtime.getRuntime().exec("cmd /c node " + INPUT_JS);
break;
}
return exec;
}
public static Boolean judgeWindows() {
return System.getProperty("os.name").toLowerCase().contains("windows");
}
public static Boolean judgeLinux() {
return System.getProperty("os.name").toLowerCase().contains("linux");
}
public static void sleep(int millSecondCount) {
try {
Thread.sleep(millSecondCount);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
package com.boot.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author bbyh
* @date 2023/3/21 0021 10:48
* @description
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
public static final Integer SUCCESS = 200;
public static final Integer ERROR = 500;
private Object data;
private Integer code;
private String msg;
public static Result success(Object data, String msg) {
return new Result(data, SUCCESS, msg);
}
public static Result error(Object data, String msg) {
return new Result(data, ERROR, msg);
}
}
项目下载
链接:https://pan.baidu.com/s/142lXYv6BLRSITT74s6MzeQ
提取码:0925
当前项目遇到的问题
1、代码格式化问题,这个是严重需要解决的;且有不小的难度,因为我没有找到很好的插件;解决起来可能需要手动写脚本去格式化,考虑到的问题会非常多;
2、支持的语言太少,且只支持单文件语言;另外,也是非常关键的一个问题,安全性问题;代码拥有的权限太高,不安全