Vue3使用codemirror

Vue3使用codemirror

    • 设计说明
    • 后端接口的问题
    • 项目下载

设计说明

本意是打算搭建一个在线的代码运行编写平台,方便随时可以在手机上也测试一些简单的代码;
在集成这个插件的时候遇到了不少问题,主要是插件依赖的下载

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、支持的语言太少,且只支持单文件语言;另外,也是非常关键的一个问题,安全性问题;代码拥有的权限太高,不安全

你可能感兴趣的:(vue,javascript,前端,vue.js)