Bladeren bron

feat: demo

haha 3 jaren geleden
bovenliggende
commit
5d815dbe57

+ 3 - 0
.browserslistrc

@@ -0,0 +1,3 @@
+> 1%
+last 2 versions
+not dead

+ 17 - 0
.eslintrc.js

@@ -0,0 +1,17 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true
+  },
+  'extends': [
+    'plugin:vue/essential',
+    'eslint:recommended'
+  ],
+  parserOptions: {
+    parser: 'babel-eslint'
+  },
+  rules: {
+    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
+  }
+}

+ 1 - 1
README.md

@@ -1,4 +1,4 @@
-# seec-oj-frontend
+# oj-frontend
 
 ## Project setup
 ```

+ 0 - 19
jsconfig.json

@@ -1,19 +0,0 @@
-{
-  "compilerOptions": {
-    "target": "es5",
-    "module": "esnext",
-    "baseUrl": "./",
-    "moduleResolution": "node",
-    "paths": {
-      "@/*": [
-        "src/*"
-      ]
-    },
-    "lib": [
-      "esnext",
-      "dom",
-      "dom.iterable",
-      "scripthost"
-    ]
-  }
-}

File diff suppressed because it is too large
+ 300 - 507
package-lock.json


+ 20 - 34
package.json

@@ -1,5 +1,5 @@
 {
-  "name": "seec-oj-frontend",
+  "name": "oj-frontend",
   "version": "0.1.0",
   "private": true,
   "scripts": {
@@ -9,39 +9,25 @@
   },
   "dependencies": {
     "axios": "^0.27.2",
-    "core-js": "^3.8.3",
-    "element-plus": "^2.2.17",
-    "vue": "^3.2.13",
-    "vue-router": "^4.1.5"
+    "core-js": "^3.6.5",
+    "element-ui": "^2.15.10",
+    "github-markdown-css": "^5.1.0",
+    "marked": "^4.1.0",
+    "vue": "^2.6.11",
+    "vue-markdown": "^2.2.4",
+    "vue-router": "^3.2.0",
+    "vuex": "^3.4.0"
   },
   "devDependencies": {
-    "@babel/core": "^7.12.16",
-    "@babel/eslint-parser": "^7.12.16",
-    "@vue/cli-plugin-babel": "~5.0.0",
-    "@vue/cli-plugin-eslint": "~5.0.0",
-    "@vue/cli-service": "~5.0.0",
-    "eslint": "^7.32.0",
-    "eslint-plugin-vue": "^8.0.3",
-    "vue-template-compiler": "^2.6.11"
-  },
-  "eslintConfig": {
-    "root": true,
-    "env": {
-      "node": true
-    },
-    "extends": [
-      "plugin:vue/vue3-essential",
-      "eslint:recommended"
-    ],
-    "parserOptions": {
-      "parser": "@babel/eslint-parser"
-    },
-    "rules": {}
-  },
-  "browserslist": [
-    "> 1%",
-    "last 2 versions",
-    "not dead",
-    "not ie 11"
-  ]
+    "@vue/cli-plugin-babel": "~4.5.4",
+    "@vue/cli-plugin-eslint": "~4.5.4",
+    "@vue/cli-plugin-router": "~4.5.4",
+    "@vue/cli-plugin-vuex": "~4.5.4",
+    "@vue/cli-service": "~4.5.4",
+    "babel-eslint": "^10.1.0",
+    "eslint": "^6.7.2",
+    "eslint-plugin-vue": "^6.2.2",
+    "vue-loader": "^17.0.0",
+    "vue-template-compiler": "^2.7.10"
+  }
 }

+ 17 - 11
src/App.vue

@@ -1,16 +1,9 @@
 <template>
-  <router-view/>
+  <div id="app">
+    <router-view/>
+  </div>
 </template>
 
-<script>
-
-export default {
-  name: 'App',
-  components: {
-  }
-}
-</script>
-
 <style>
 #app {
   font-family: Avenir, Helvetica, Arial, sans-serif;
@@ -18,6 +11,19 @@ export default {
   -moz-osx-font-smoothing: grayscale;
   text-align: center;
   color: #2c3e50;
-  margin-top: 60px;
+  background-color: green;
+}
+
+#nav {
+  padding: 30px;
+}
+
+#nav a {
+  font-weight: bold;
+  color: #2c3e50;
+}
+
+#nav a.router-link-exact-active {
+  color: #42b983;
 }
 </style>

+ 16 - 0
src/api/judgeAPI.js

@@ -0,0 +1,16 @@
+import {axios} from "../utils/request";
+
+const api = {
+    pre: "judge",
+};
+
+export function codeJudgeAPI(problemId,data) {
+    return axios({
+        url: `${api.pre}/code`,
+        method: "POST",
+        params:{
+            problemId:problemId
+        },
+        data
+    });
+}

+ 0 - 12
src/api/problem.js

@@ -1,12 +0,0 @@
-import {axios} from "../utils/request";
-
-const api = {
-    pre: "problem",
-};
-
-export function getProblemsAPI() {
-    return axios({
-        url: `${api.pre}`,
-        method: "GET",
-    });
-}

+ 96 - 0
src/api/problemAPI.js

@@ -0,0 +1,96 @@
+import {axios} from "../utils/request";
+
+const api = {
+    pre: "problem",
+};
+
+export function getProblemsAPI() {
+    return axios({
+        url: `${api.pre}`,
+        method: "GET",
+    });
+}
+
+export function getProblemManageListAPI() {
+    return axios({
+        url: `${api.pre}/manage`,
+        method: "GET",
+    });
+}
+
+export function getProblemManageAPI(problemId) {
+    return axios({
+        url: `${api.pre}/manage/`+problemId,
+        method: "GET",
+    });
+}
+
+export function getProblemDetailAPI(problemId) {
+    return axios({
+        url: `${api.pre}/`+problemId,
+        method: "GET",
+    });
+}
+
+export function addProblemAPI(data) {
+    return axios({
+        url: `${api.pre}`,
+        method: "POST",
+        data
+    });
+}
+
+export function deleteProblemAPI(problemId){
+    return axios({
+        url: `${api.pre}`,
+        method: "DELETE",
+        params:{
+            problemId:problemId
+        }
+    });
+}
+
+
+
+export function getTestCasesByProblemIdAPI(problemId){
+    return axios({
+        url: `${api.pre}/testcase`,
+        method: "GET",
+        params:{
+            problemId:problemId
+        }
+    });
+}
+
+export function addTestCaseAPI(data) {
+    return axios({
+        url: `${api.pre}/testcase`,
+        method: "POST",
+        data
+    });
+}
+
+export function batchAddTestCaseAPI(data) {
+    return axios({
+        url: `${api.pre}/batchTestcase`,
+        method: "POST",
+        data
+    });
+}
+
+export function delTestCaseAPI(testCaseId) {
+    return axios({
+        url: `${api.pre}/testcase`,
+        method: "DELETE",
+        params:{
+            testCaseId:testCaseId
+        }
+    });
+}
+
+
+
+
+
+
+

+ 60 - 0
src/components/HelloWorld.vue

@@ -0,0 +1,60 @@
+<template>
+  <div class="hello">
+    <h1>{{ msg }}</h1>
+    <p>
+      For a guide and recipes on how to configure / customize this project,<br>
+      check out the
+      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
+    </p>
+    <h3>Installed CLI Plugins</h3>
+    <ul>
+      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
+      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
+      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
+      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
+    </ul>
+    <h3>Essential Links</h3>
+    <ul>
+      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
+      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
+      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
+      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
+      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
+    </ul>
+    <h3>Ecosystem</h3>
+    <ul>
+      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
+      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
+      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
+      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
+      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
+    </ul>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'HelloWorld',
+  props: {
+    msg: String
+  }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+h3 {
+  margin: 40px 0 0;
+}
+ul {
+  list-style-type: none;
+  padding: 0;
+}
+li {
+  display: inline-block;
+  margin: 0 10px;
+}
+a {
+  color: #42b983;
+}
+</style>

+ 13 - 7
src/main.js

@@ -1,10 +1,16 @@
-import { createApp } from 'vue'
+import Vue from 'vue'
 import App from './App.vue'
-import ElementPlus from 'element-plus'
-import 'element-plus/dist/index.css'
-import router from "./router"
+import router from './router'
+import ElementUI from "element-ui";
+import "element-ui/lib/theme-chalk/index.css";
+import 'github-markdown-css/github-markdown-light.css'
+import store from './store'
 
+Vue.config.productionTip = false
 
-const app = createApp(App)
-app.use(router).use(ElementPlus)
-app.mount('#app')
+Vue.use(ElementUI)
+new Vue({
+  router,
+  store,
+  render: h => h(App)
+}).$mount('#app')

+ 59 - 8
src/router/index.js

@@ -1,14 +1,65 @@
-import { createRouter, createWebHashHistory } from "vue-router"
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import ProblemPage from "@/views/ProblemPage";
+import MainPage from "@/views/MainPage";
+import ProblemListPage from "@/views/ProblemListPage";
+import ManageListPage from "@/views/ManageListPage";
+import ManagePage from "@/views/ManagePage";
+import UploadProblemPage from "@/views/UploadProblemPage";
+
+Vue.use(VueRouter)
 
 const routes = [
-    {
-        path: '/',
-        component: () => import('@/views/ProblemListPage')
-    },
+  {
+    path: '/',
+    name: 'Main',
+    component: MainPage,
+    children:[
+      {
+        path: '/problem',
+        name: 'problemList',
+        component: ProblemListPage,
+      },
+      {
+        path: '/problem/:id',
+        name: 'ProblemDetail',
+        component: ProblemPage,
+      },
+      {
+        path: '/manage',
+        name: 'manageList',
+        component: ManageListPage
+      },
+      {
+        path: '/manage/upload',
+        name: 'uploadProblem',
+        component: UploadProblemPage,
+      },
+      {
+        path: '/manage/:id',
+        name: 'manageDetail',
+        component: ManagePage,
+      },
+
+
+    ]
+  },
+
+
+  // {
+  //   path: '/about',
+  //   name: 'About',
+  //   // route level code-splitting
+  //   // this generates a separate chunk (about.[hash].js) for this route
+  //   // which is lazy-loaded when the route is visited.
+  //   component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
+  // }
 ]
-export const router = createRouter({
-    history: createWebHashHistory(),
-    routes: routes
+
+const router = new VueRouter({
+  mode: 'history',
+  base: process.env.BASE_URL,
+  routes
 })
 
 export default router

+ 15 - 0
src/store/index.js

@@ -0,0 +1,15 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+  state: {
+  },
+  mutations: {
+  },
+  actions: {
+  },
+  modules: {
+  }
+})

+ 42 - 2
src/views/MainPage.vue

@@ -1,13 +1,53 @@
 <template>
-<h3>mainpage</h3>
+  <div>
+    <el-container>
+      <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
+        <el-menu :default-active="systemActive" router @select="handleSystemSelect">
+          <el-menu-item index="/problem">答题</el-menu-item>
+          <el-menu-item index="/manage">题目管理</el-menu-item>
+        </el-menu>
+      </el-aside>
+      <el-main>
+        <router-view/>
+      </el-main>
+    </el-container>
+  </div>
 </template>
 
 <script>
 export default {
-name: "MainPage"
+  name: "MainPage",
+  components: {},
+  data() {
+    return {
+      systemActive: '/problem',
+    }
+  },
+  methods: {
+    handleSystemSelect(key) {
+      this.systemActive = key;
+      console.log(this.systemActive);
+    }
+  },
 }
 </script>
 
 <style scoped>
+.el-aside {
+  display: block;
+  position: absolute;
+  left: 0;
+  top: 0;
+  bottom: 0;
+}
 
+.el-main {
+  position: absolute;
+  left: 200px;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  margin: 10px;
+  overflow-y: scroll;
+}
 </style>

+ 74 - 0
src/views/ManageListPage.vue

@@ -0,0 +1,74 @@
+<template>
+  <div>
+    <div style="text-align: left;display: flex;align-items: center;justify-content: space-between">
+      <h2>题目列表</h2>
+      <el-button type="primary" @click="handleUploadClick">新增题目</el-button>
+    </div>
+    <el-table
+        :data="problemList">
+      <el-table-column prop="id" label="序号"></el-table-column>
+      <el-table-column prop="name" label="题目">
+        <template slot-scope="scope">
+          <el-button type="text" @click="handleNameClick(scope.row)">{{ scope.row.name }}</el-button>
+        </template>
+      </el-table-column>
+      <el-table-column prop="createTime" label="上传时间" :formatter="dateTimeFormat"></el-table-column>
+      <el-table-column prop="modifiedTime" label="修改时间" :formatter="modifiedTimeFormat"></el-table-column>
+      <el-table-column width="100px" label="操作">
+        <template slot-scope="scope">
+          <el-button type="primary" size="mini" @click="handleNameClick(scope.row)">详情</el-button>
+        </template>
+      </el-table-column>
+
+    </el-table>
+  </div>
+</template>
+
+<script>
+import {getProblemManageListAPI} from "@/api/problemAPI";
+
+export default {
+  name: "ManageListPage",
+  data() {
+    return {
+      problemList: []
+    }
+  },
+  methods: {
+    getProblemList() {
+      getProblemManageListAPI().then((res) => {
+        this.problemList = res.result;
+      })
+    },
+    handleNameClick(row) {
+      this.$router.push({
+        path: '/manage/' + row.id,
+      });
+    },
+    handleUploadClick() {
+      this.$router.push({
+        path: '/manage/upload',
+      });
+    },
+    dateTimeFormat(row) {
+      if (row.createTime == null) {
+        return '--';
+      }
+      return new Date(row.createTime).toLocaleString();
+    },
+    modifiedTimeFormat(row) {
+      if (row.modifiedTime == null) {
+        return '--';
+      }
+      return new Date(row.modifiedTime).toLocaleString();
+    }
+  },
+  mounted() {
+    this.getProblemList();
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 281 - 0
src/views/ManagePage.vue

@@ -0,0 +1,281 @@
+<template>
+  <div>
+
+
+    <el-descriptions :title="manageData.name">
+      <template slot="extra">
+        <el-button type="danger" size="small" @click="deleteDialogVisible=true" plain>删除题目</el-button>
+      </template>
+      <el-descriptions-item label="题目序号">{{ manageData.id }}</el-descriptions-item>
+      <el-descriptions-item label="题目名称">{{ manageData.name }}</el-descriptions-item>
+      <el-descriptions-item label="时间限制(ms)">{{ manageData.timeLimit }}</el-descriptions-item>
+      <el-descriptions-item label="空间限制(KB)">{{ manageData.memoryLimit }}</el-descriptions-item>
+      <el-descriptions-item label="上传时间" :formatter="dateTimeFormat">{{ manageData.createTime }}</el-descriptions-item>
+      <el-descriptions-item label="修改时间">{{ manageData.modifiedTime }}</el-descriptions-item>
+      <el-descriptions-item label="题目描述">
+        <el-button type="primary" size="mini" @click="handleDescriptionDialog">查看</el-button>
+      </el-descriptions-item>
+    </el-descriptions>
+
+    <el-divider></el-divider>
+    <div style="text-align: left;display: flex;align-items: center">
+      <h4>测试用例</h4>
+      &nbsp;
+      <span><el-button type="primary" size="mini" @click="tcDialogVisible = true;">新增测试用例</el-button></span>
+    </div>
+
+    <el-table
+        :data="testcases">
+      <el-table-column prop="id" label="序号"></el-table-column>
+      <el-table-column prop="input" label="输入">
+        <template slot-scope="scope">
+          <div style="white-space: pre-wrap">{{ scope.row.input }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="output" label="输出">
+        <template slot-scope="scope">
+          <div style="white-space: pre-wrap">{{ scope.row.output }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column width="100px">
+        <template slot-scope="scope">
+          <el-popconfirm title="确定删除该测试用例吗?" @confirm="deleteTestCase(scope.row)">
+
+            <el-button type="danger" size="mini" slot="reference">删除</el-button>
+          </el-popconfirm>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!--    <el-dialog-->
+    <!--        title="新增测试用例"-->
+    <!--        :visible.sync="tcDialogVisible"-->
+    <!--    >-->
+    <!--      <el-form ref="form" :model="form">-->
+    <!--        <el-form-item label="输入">-->
+    <!--          <el-input v-model="form.input" type="textarea" :row="3"></el-input>-->
+    <!--        </el-form-item>-->
+    <!--        <el-form-item label="输出">-->
+    <!--          <el-input v-model="form.output" type="textarea" :row="3"></el-input>-->
+    <!--        </el-form-item>-->
+    <!--        <el-form-item>-->
+    <!--          <el-button type="primary" @click="uploadTestCase">提交</el-button>-->
+    <!--        </el-form-item>-->
+
+    <!--      </el-form>-->
+
+    <!--    </el-dialog>-->
+
+    <el-dialog
+        title="新增测试用例"
+        :visible.sync="tcDialogVisible"
+    >
+
+      <el-form ref="form" :model="batchForm">
+        <el-row
+            v-for="(tc,index) in batchForm.testCaseList"
+            :key="tc.key"
+            style="display: flex;align-items: center;justify-content: space-between"
+        >
+          <el-form-item :label="'输入'+index" label-width="60px" style="width: 40%">
+            <el-input v-model="tc.input" type="textarea" :row="1"></el-input>
+          </el-form-item>
+          <el-form-item :label="'输出'+index" label-width="60px" style="width: 40%">
+            <el-input v-model="tc.output" type="textarea" :row="1"></el-input>
+          </el-form-item>
+          <div style="width: 120px">
+            <div style="display: flex;justify-content: left">
+              <el-form-item label-width="10px">
+                <el-button type="danger" @click.prevent="removeFormTestcase(tc)" icon="el-icon-delete" size="mini"
+                ></el-button>
+                <el-button type="primary" v-if="index===batchForm.testCaseList.length-1" @click="addFormTestcase"
+                           icon="el-icon-plus" size="mini"></el-button>
+              </el-form-item>
+            </div>
+
+          </div>
+
+        </el-row>
+
+        <el-form-item>
+          <el-button type="primary" @click="uploadBatchTestCases">提交</el-button>
+        </el-form-item>
+
+      </el-form>
+
+    </el-dialog>
+
+    <el-dialog
+        title="题目描述"
+        :visible.sync="dialogVisible"
+    >
+      <div style="text-align: left" class="markdown-body">
+        <VueMarkdown :source="manageData.description"></VueMarkdown>
+      </div>
+    </el-dialog>
+
+    <el-dialog
+        title="确认删除题目"
+        :visible.sync="deleteDialogVisible"
+    >
+      <div>
+        <h2>确认删除题目吗?</h2>
+      </div>
+      <div>
+        题目序号:{{ manageId }}
+      </div>
+      <div>
+        题目名称:{{ manageData.name }}
+      </div>
+
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false">取 消</el-button>
+        <el-button type="danger" @click="deleteProblem">删除</el-button>
+      </span>
+
+    </el-dialog>
+
+
+  </div>
+
+</template>
+
+<script>
+import {
+  addTestCaseAPI, batchAddTestCaseAPI,
+  deleteProblemAPI,
+  delTestCaseAPI,
+  getProblemManageAPI,
+  getTestCasesByProblemIdAPI
+} from "@/api/problemAPI";
+import VueMarkdown from 'vue-markdown'
+
+export default {
+  name: "ManagePage",
+  components: {
+    VueMarkdown
+  },
+  data() {
+    return {
+      manageId: undefined,
+      manageData: {
+        name: ''
+      },
+      form: {},
+      batchForm: {
+        testCaseList: [
+          {
+            input: undefined,
+            output: undefined
+          }
+        ]
+      },
+      testcases: undefined,
+      dialogVisible: false,
+      tcDialogVisible: false,
+      deleteDialogVisible: false,
+    }
+  },
+  methods: {
+    getProblemManageById(problemId) {
+      getProblemManageAPI(problemId).then((res) => {
+        this.manageData = res.result;
+        this.manageData.createTime = this.dateTimeFormat(this.manageData.createTime);
+        this.manageData.modifiedTime = this.dateTimeFormat(this.manageData.modifiedTime);
+      })
+    },
+    getTestCasesByProblemId(problemId) {
+      getTestCasesByProblemIdAPI(problemId).then((res) => {
+        this.testcases = res.result;
+      })
+    },
+    handleDescriptionDialog() {
+      this.dialogVisible = true;
+    },
+    deleteProblem() {
+      deleteProblemAPI(this.manageId).then((res) => {
+        console.log(res);
+        this.$message.success("删除题目" + "[" + this.manageId + "]" + "成功");
+        this.deleteDialogVisible = false;
+        this.$router.push({
+          path: '/manage',
+        });
+      })
+    },
+    uploadTestCase() {
+      this.form.problemId = this.manageId;
+      console.log(this.form);
+      addTestCaseAPI(this.form).then((res) => {
+        console.log(res);
+        this.getTestCasesByProblemId(this.manageId);
+        this.$message.success("新增测试用例成功");
+        this.tcDialogVisible = false;
+        this.form = {};
+      })
+    },
+    deleteTestCase(row) {
+      delTestCaseAPI(row.id).then((res) => {
+        console.log(res);
+        this.getTestCasesByProblemId(this.manageId);
+        this.$message.success("删除测试用例成功!");
+      })
+    },
+
+    removeFormTestcase(item) {
+      let index = this.batchForm.testCaseList.indexOf(item);
+      if (this.batchForm.testCaseList.length === 1) {
+        this.$message.error("请至少填写一个子条件!")
+        return;
+      }
+      if (index !== -1) {
+        this.batchForm.testCaseList.splice(index, 1)
+      }
+    },
+
+    addFormTestcase() {
+      this.batchForm.testCaseList.push({
+        input: undefined,
+        output: undefined
+      })
+    },
+
+    uploadBatchTestCases() {
+      for (let i = 0; i < this.batchForm.testCaseList.length; i++) {
+        this.batchForm.testCaseList[i].problemId = this.manageId;
+      }
+      console.log(this.batchForm.testCaseList);
+      batchAddTestCaseAPI(this.batchForm.testCaseList).then((res) => {
+        console.log(res);
+        this.getTestCasesByProblemId(this.manageId);
+        this.$message.success("新增测试用例成功");
+        this.tcDialogVisible = false;
+        this.batchForm = {
+          testCaseList: [
+            {
+              input: undefined,
+              output: undefined
+            }
+          ]
+        };
+      })
+    },
+
+    dateTimeFormat(time) {
+      if (time == null) {
+        return '--';
+      }
+      return new Date(time).toLocaleString();
+    },
+
+  },
+  mounted() {
+    this.manageId = this.$route.params.id;
+    this.getProblemManageById(this.manageId);
+    this.getTestCasesByProblemId(this.manageId);
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 25 - 9
src/views/ProblemListPage.vue

@@ -1,27 +1,43 @@
 <template>
   <div>
-    {{problemList}}
+    <div style="text-align: left">
+      <h2>OJ</h2>
+    </div>
     <div>
-      <el-tooltip></el-tooltip>
+      <el-table
+          :data="problemList">
+        <el-table-column prop="id" label="序号"></el-table-column>
+        <el-table-column prop="name" label="题目">
+          <template slot-scope="scope">
+            <el-button type="text" @click="handleNameClick(scope.row)">{{ scope.row.name }}</el-button>
+          </template>
+        </el-table-column>
+
+      </el-table>
     </div>
   </div>
 </template>
 
 <script>
-import {getProblemsAPI} from "@/api/problem";
+import {getProblemsAPI} from "@/api/problemAPI";
 
 export default {
   name: "ProblemListPage",
   data() {
-    return{
-      problemList:[]
+    return {
+      problemList: []
     }
   },
-  methods:{
-    getProblemList(){
-      getProblemsAPI().then((res)=>{
-        this.problemList = res;
+  methods: {
+    getProblemList() {
+      getProblemsAPI().then((res) => {
+        this.problemList = res.result;
       })
+    },
+    handleNameClick(row){
+      this.$router.push({
+        path:'/problem/'+row.id,
+      });
     }
   },
   mounted() {

+ 144 - 0
src/views/ProblemPage.vue

@@ -0,0 +1,144 @@
+<template>
+  <div>
+    <div>
+      <h1>
+        <span>{{ problemId }}</span>.&nbsp;<span>{{ name }}</span>
+      </h1>
+    </div>
+    <el-divider></el-divider>
+    <div>时间限制:{{ timeLimit }}ms, 空间限制:{{ memoryLimit }}KB</div>
+    <el-divider></el-divider>
+    <div style="text-align: left" class="markdown-body">
+      <VueMarkdown :source="description"></VueMarkdown>
+    </div>
+    <el-main v-loading="loading">
+      <el-select v-model="language" placeholder="请选择编程语言">
+        <el-option
+            v-for="(item,index) in languageOptions"
+            :key="index"
+            :label="item" :value="item"></el-option>
+      </el-select>
+      <el-input
+          type="textarea"
+          :rows="30"
+          placeholder="请输入代码"
+          v-model="code">
+
+      </el-input>
+      <el-button type="primary" @click="submitCode">提交</el-button>
+    </el-main>
+    <el-dialog
+        title="提交结果"
+        :visible.sync="dialogVisible"
+        width="30%"
+    >
+      <el-table :data="judgeResultList">
+        <el-table-column prop="result" label="结果" width="200px"></el-table-column>
+        <el-table-column prop="timeused" label="时间(ms)" :formatter="timeUsedFormat"></el-table-column>
+        <el-table-column prop="memoryused" label="空间(KB)" :formatter="memoryUsedFormat"></el-table-column>
+      </el-table>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
+      </span>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import {getProblemDetailAPI} from "@/api/problemAPI";
+import {codeJudgeAPI} from "@/api/judgeAPI";
+import VueMarkdown from 'vue-markdown'
+
+export default {
+  name: "ProblemPage",
+  components: {
+    VueMarkdown
+  },
+  data() {
+    return {
+      problemId: undefined,
+      code: '',
+      language: 'java',
+      timeLimit: undefined,
+      memoryLimit: undefined,
+      description: undefined,
+      name: undefined,
+      loading: false,
+      dialogVisible: false,
+      judgeResultList: [],
+      TestCaseNum:undefined,
+      languageOptions: [
+        'java', 'python', 'c'
+      ]
+    }
+  },
+  methods: {
+    getProblemDetail() {
+      getProblemDetailAPI(this.problemId).then((res) => {
+        let problemDetail = res.result;
+        this.problemId = problemDetail.id;
+        this.timeLimit = problemDetail.timeLimit;
+        this.memoryLimit = problemDetail.memoryLimit;
+        this.description = problemDetail.description;
+        // this.description = problemDetail.description;
+        this.name = problemDetail.name;
+      })
+    },
+    submitCode() {
+      this.loading = true;
+      let codeData = {
+        code: this.code,
+        language: this.language
+      }
+      codeJudgeAPI(this.problemId, codeData).then((res) => {
+        this.loading = false;
+        // this.$alert(res.result, '提交结果', {
+        //   confirmButtonText: '确定'
+        // });
+        this.judgeResultList = res.result.result_list;
+        this.dialogVisible = true;
+      })
+    },
+    // 类型格式化
+    timeUsedFormat (row) {
+      let showProp = null;
+      row.timeused ? showProp = row.timeused : showProp = '---'
+      return showProp
+    },
+    memoryUsedFormat (row) {
+      let showProp = null;
+      row.memoryused ? showProp = row.memoryused : showProp = '---'
+      return showProp
+    },
+
+  },
+  mounted() {
+    this.problemId = this.$route.params.id;
+    this.getProblemDetail();
+  }
+
+}
+</script>
+
+<style scoped>
+.el-divider--horizontal {
+  margin: 5px 0;
+}
+
+.markdown-body {
+  box-sizing: border-box;
+  /*min-width: 200px;*/
+  /*max-width: 980px;*/
+  margin: 0 auto;
+  padding: 0 45px;
+}
+
+/*@media (max-width: 767px) {*/
+/*  .markdown-body {*/
+/*    padding: 15px;*/
+/*  }*/
+/*}*/
+
+
+</style>

+ 55 - 0
src/views/UploadProblemPage.vue

@@ -0,0 +1,55 @@
+<template>
+<div>
+  <div style="text-align: left">
+    <h2>上传题目</h2>
+  </div>
+  <el-form ref="form" :model="form">
+    <el-form-item label="题目名称">
+      <el-input v-model="form.name"></el-input>
+    </el-form-item>
+    <el-form-item label="时间限制(ms)">
+      <el-input v-model="form.timeLimit"></el-input>
+    </el-form-item>
+    <el-form-item label="空间限制(KB)">
+      <el-input v-model="form.memoryLimit"></el-input>
+    </el-form-item>
+    <el-form-item label="题目描述">
+      <el-input v-model="form.description" type="textarea" :row="30"></el-input>
+    </el-form-item>
+    <el-form-item>
+      <el-button type="primary" @click="uploadProblem">上传</el-button>
+    </el-form-item>
+
+  </el-form>
+</div>
+</template>
+
+<script>
+import {addProblemAPI} from "@/api/problemAPI";
+
+export default {
+  name: "UploadProblemPage",
+  data(){
+    return{
+      form:{}
+    }
+  },
+  methods:{
+    uploadProblem(){
+      addProblemAPI(this.form).then((res)=>{
+        console.log(res);
+        this.$router.push({
+          path: '/manage/' + res.result.id,
+        });
+      })
+    }
+
+  },
+  mounted() {
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

Some files were not shown because too many files changed in this diff