当前位置: 首页 > news >正文

OJ在线评测系统 前端创建题目(增) 更新题目(改) 题目列表(查) 以及做题页面的开发 基于VUECLI脚手架画界面

目录

前端创建页面的开发一

创建一个路由

用acro design写

前端创建页面的开发二

题目管理页面

搜索

最終效果

题目更新页面的开发

携带参数的那种

修改路由

页码更新细节

我们先处理菜单项的权限控制和权限隐藏

在这里改

属性绑定一个函数

可以参考聚合搜索项目状态改变和url状态同步

就会触发loadData函数

当分页页号改变时 触发@page-change事件 通过改变searchParams的数值

然后

题目列表搜索页

代码

设置路由

做题页面

代码

路由


前端创建页面的开发一

创建一个路由

  {path: "/add/question",name: "创建题目",component: AddQuestionView,meta: {access: ACCESS_ENUM.USER,},},

用acro design写

<template><div id="addQuestionView" style="max-width: 800px; margin: 0 auto"><h2 style="margin-bottom: 16px">创建题目</h2><a-form :model="form" label-align="left"><a-form-item label="标题"><a-input v-model="form.title" placeholder="" /></a-form-item><a-form-item label="标签"><a-input-tag v-model="form.tags" placeholder="请选择标签" allow-clear /></a-form-item><a-form-item label="题目内容"><MdEditor :value="form.content" :handle-change="onContentChange" /></a-form-item><a-form-item label="答案"><MdEditor :value="form.answer" :handle-change="onAnswerChange" /></a-form-item><a-form-item label="判题配置一"><div><a-form-item label="时间限制"><a-input-numberv-model="form.judgeConfig.timeLimit"placeholder="请输入时间限制"mode="button"min="0"size="large"/></a-form-item></div></a-form-item><a-form-item label="判题配置二"><div><a-form-item label="内存限制"><a-input-numberv-model="form.judgeConfig.memoryLimit"placeholder="请输入内存限制"mode="button"min="0"size="large"/></a-form-item></div></a-form-item><a-form-item label="判题配置三"><div><a-form-item label="堆栈限制"><a-input-numberv-model="form.judgeConfig.stackLimit"placeholder="请输入堆栈限制"mode="button"min="0"size="large"/></a-form-item></div></a-form-item><a-form-item label="测试用例配置"><!-- 使用表格布局或者 flex 布局 --><table><tbody><tr v-for="(judgeCaseItem, index) of form.judgeCase" :key="index"><td><a-form-item :label="`输入用例-${index}`" no-style><a-inputv-model="judgeCaseItem.input"placeholder="请输入测试输入用例"/></a-form-item></td><td><a-form-item :label="`输出用例-${index}`" no-style><a-inputv-model="judgeCaseItem.output"placeholder="请输入测试输出用例"/></a-form-item></td><td><a-button status="danger" @click="handleDelete(index)">删除</a-button></td></tr></tbody></table><div style="margin-top: 16px"><a-button @click="handleAdd" type="outline" status="success">新增测试用例</a-button></div></a-form-item><a-form-item style="text-align: center"><a-button type="primary" style="min-width: 200px" @click="doSubmit">提交</a-button></a-form-item></a-form></div>
</template><script setup lang="ts">
import { onMounted, ref } from "vue";
import MdEditor from "@/components/MdEditor.vue";
import { QuestionControllerService } from "../../../generated";
import message from "@arco-design/web-vue/es/message";
import { useRoute } from "vue-router";const route = useRoute();
// 如果页面地址包含 update,视为更新页面
const updatePage = route.path.includes("update");let form = ref({title: "",tags: [],answer: "",content: "",judgeConfig: {memoryLimit: 1000,stackLimit: 1000,timeLimit: 1000,},judgeCase: [{input: "",output: "",},],
});/*** 根据题目 id 获取老的数据*/
const loadData = async () => {const id = route.query.id;if (!id) {return;}const res = await QuestionControllerService.getQuestionByIdUsingGet(id as any);if (res.code === 0) {form.value = res.data as any;// json 转 js 对象if (!form.value.judgeCase) {form.value.judgeCase = [{input: "",output: "",},];} else {form.value.judgeCase = JSON.parse(form.value.judgeCase as any);}if (!form.value.judgeConfig) {form.value.judgeConfig = {memoryLimit: 1000,stackLimit: 1000,timeLimit: 1000,};} else {form.value.judgeConfig = JSON.parse(form.value.judgeConfig as any);}if (!form.value.tags) {form.value.tags = [];} else {form.value.tags = JSON.parse(form.value.tags as any);}} else {message.error("加载失败," + res.message);}
};onMounted(() => {loadData();
});const doSubmit = async () => {console.log(form.value);// 区分更新还是创建if (updatePage) {const res = await QuestionControllerService.updateQuestionUsingPost(form.value);if (res.code === 0) {message.success("更新成功");} else {message.error("更新失败," + res.message);}} else {const res = await QuestionControllerService.addQuestionUsingPost(form.value);if (res.code === 0) {message.success("创建成功");} else {message.error("创建失败," + res.message);}}
};/*** 新增判题用例*/
const handleAdd = () => {form.value.judgeCase.push({input: "",output: "",});
};/*** 删除判题用例*/
const handleDelete = (index: number) => {form.value.judgeCase.splice(index, 1);
};const onContentChange = (value: string) => {form.value.content = value;
};const onAnswerChange = (value: string) => {form.value.answer = value;
};
</script><style scoped>
#addQuestionView {
}
</style>

前端创建页面的开发二

开发的是其他增删改查页面

题目管理页面

查看

搜索

<template><div id="manageQuestionView"><a-table:ref="tableRef":columns="columns":data="dataList":pagination="{showTotal: true,pageSize: searchParams.pageSize,current: searchParams.current,total,}"@page-change="onPageChange"><template #optional="{ record }"><a-space><a-button type="primary" @click="doUpdate(record)"> 修改</a-button><a-button status="danger" @click="doDelete(record)">删除</a-button></a-space></template></a-table></div>
</template><script setup lang="ts">
import { onMounted, ref, watchEffect } from "vue";
import {Page_Question_,Question,QuestionControllerService,
} from "../../../generated";
import message from "@arco-design/web-vue/es/message";
import * as querystring from "querystring";
import { useRouter } from "vue-router";const tableRef = ref();const dataList = ref([]);
const total = ref(0);
const searchParams = ref({pageSize: 10,current: 1,
});const loadData = async () => {const res = await QuestionControllerService.listQuestionByPageUsingPost(searchParams.value);if (res.code === 0) {dataList.value = res.data.records;total.value = res.data.total;} else {message.error("加载失败," + res.message);}
};/*** 监听 searchParams 变量,改变时触发页面的重新加载*/
watchEffect(() => {loadData();
});/*** 页面加载时,请求数据*/
onMounted(() => {loadData();
});// {id: "1", title: "A+ D", content: "新的题目内容", tags: "["二叉树"]", answer: "新的答案", submitNum: 0,…}const columns = [{title: "题目id",dataIndex: "id",},{title: "标题",dataIndex: "title",},{title: "内容",dataIndex: "content",},{title: "标签",dataIndex: "tags",},{title: "答案",dataIndex: "answer",},{title: "提交数",dataIndex: "submitNum",},{title: "通过数",dataIndex: "acceptedNum",},{title: "判题配置",dataIndex: "judgeConfig",},{title: "判题用例",dataIndex: "judgeCase",},{title: "创建者",dataIndex: "userId",},{title: "创建时间",dataIndex: "createTime",},{title: "操作",slotName: "optional",},
];const onPageChange = (page: number) => {searchParams.value = {...searchParams.value,current: page,};
};const doDelete = async (question: Question) => {const res = await QuestionControllerService.deleteQuestionUsingPost({id: question.id,});if (res.code === 0) {message.success("删除成功");loadData();} else {message.error("删除失败");}
};const router = useRouter();const doUpdate = (question: Question) => {router.push({path: "/update/question",query: {id: question.id,},});
};
</script><style scoped>
#manageQuestionView {
}
</style>

最終效果

题目更新页面的开发

写一个动态路由

携带参数的那种

const router = useRouter();const doUpdate = (question: Question) => {router.push({path: "/update/question",query: {id: question.id,},});
};

修改路由

  {path: "/update/question",name: "更新题目",component: AddQuestionView,meta: {access: ACCESS_ENUM.USER,hideInMenu: true,},},

页码更新细节

我们的管理题目页面不能分页

我们先处理菜单项的权限控制和权限隐藏

在这里改

在组件里面定义属性

属性绑定一个函数

const onPageChange = (page: number) => {searchParams.value = {...searchParams.value,current: page,};
};

可以参考聚合搜索项目状态改变和url状态同步

当我们的属性改变了

就会触发loadData函数

当分页页号改变时 触发@page-change事件 通过改变searchParams的数值

并且通过watchEffect监听searchParams的改变

然后执行loadData重新加载速度

实现页号变化时触发数据的重新加载

然后

/*** 监听 searchParams 变量,改变时触发页面的重新加载*/
watchEffect(() => {loadData();
});

题目列表搜索页

代码

<template><div id="questionsView"><a-form :model="searchParams" layout="inline"><a-form-item field="title" label="名称" style="min-width: 240px"><a-input v-model="searchParams.title" placeholder="请输入名称" /></a-form-item><a-form-item field="tags" label="标签" style="min-width: 240px"><a-input-tag v-model="searchParams.tags" placeholder="请输入标签" /></a-form-item><a-form-item><a-button type="primary" @click="doSubmit">提交</a-button></a-form-item></a-form><a-divider size="0" /><a-table:ref="tableRef":columns="columns":data="dataList":pagination="{showTotal: true,pageSize: searchParams.pageSize,current: searchParams.current,total,}"@page-change="onPageChange"><template #tags="{ record }"><a-space wrap><a-tag v-for="(tag, index) of record.tags" :key="index" color="green">{{ tag }}</a-tag></a-space></template><template #acceptedRate="{ record }">{{`${record.submitNum ? record.acceptedNum / record.submitNum : "0"}% (${record.acceptedNum}/${record.submitNum})`}}</template><template #createTime="{ record }">{{ moment(record.createTime).format("YYYY-MM-DD") }}</template><template #optional="{ record }"><a-space><a-button type="primary" @click="toQuestionPage(record)">做题</a-button></a-space></template></a-table></div>
</template><script setup lang="ts">
import { onMounted, ref, watchEffect } from "vue";
import {Page_Question_,Question,QuestionControllerService,QuestionQueryRequest,
} from "../../../generated";
import message from "@arco-design/web-vue/es/message";
import * as querystring from "querystring";
import { useRouter } from "vue-router";
import moment from "moment";const tableRef = ref();const dataList = ref([]);
const total = ref(0);
const searchParams = ref<QuestionQueryRequest>({title: "",tags: [],pageSize: 8,current: 1,
});const loadData = async () => {const res = await QuestionControllerService.listQuestionVoByPageUsingPost(searchParams.value);if (res.code === 0) {dataList.value = res.data.records;total.value = res.data.total;} else {message.error("加载失败," + res.message);}
};/*** 监听 searchParams 变量,改变时触发页面的重新加载*/
watchEffect(() => {loadData();
});/*** 页面加载时,请求数据*/
onMounted(() => {loadData();
});// {id: "1", title: "A+ D", content: "新的题目内容", tags: "["二叉树"]", answer: "新的答案", submitNum: 0,…}const columns = [{title: "题号",dataIndex: "id",},{title: "题目名称",dataIndex: "title",},{title: "标签",slotName: "tags",},{title: "通过率",slotName: "acceptedRate",},{title: "创建时间",slotName: "createTime",},{slotName: "optional",},
];const onPageChange = (page: number) => {searchParams.value = {...searchParams.value,current: page,};
};const router = useRouter();/*** 跳转到做题页面* @param question*/
const toQuestionPage = (question: Question) => {router.push({path: `/view/question/${question.id}`,});
};/*** 确认搜索,重新加载数据*/
const doSubmit = () => {// 这里需要重置搜索页号searchParams.value = {...searchParams.value,current: 1,};
};
</script><style scoped>
#questionsView {max-width: 1280px;margin: 0 auto;
}
</style>

设置路由

  {path: "/questions",name: "浏览题目",component: QuestionsView,},

做题页面

代码

<template><div id="viewQuestionView"><a-row :gutter="[24, 24]"><a-col :md="12" :xs="24"><a-tabs default-active-key="question"><a-tab-pane key="question" title="题目"><a-card v-if="question" :title="question.title"><a-descriptionstitle="判题条件":column="{ xs: 1, md: 2, lg: 3 }"><a-descriptions-item label="时间限制">{{ question.judgeConfig.timeLimit ?? 0 }}</a-descriptions-item><a-descriptions-item label="内存限制">{{ question.judgeConfig.memoryLimit ?? 0 }}</a-descriptions-item><a-descriptions-item label="堆栈限制">{{ question.judgeConfig.stackLimit ?? 0 }}</a-descriptions-item></a-descriptions><MdViewer :value="question.content || ''" /><template #extra><a-space wrap><a-tagv-for="(tag, index) of question.tags":key="index"color="green">{{ tag }}</a-tag></a-space></template></a-card></a-tab-pane><a-tab-pane key="comment" title="评论" disabled> 评论区</a-tab-pane><a-tab-pane key="answer" title="答案"> 暂时无法查看答案</a-tab-pane></a-tabs></a-col><a-col :md="12" :xs="24"><a-form :model="form" layout="inline"><a-form-itemfield="language"label="编程语言"style="min-width: 240px"><a-selectv-model="form.language":style="{ width: '320px' }"placeholder="选择编程语言"><a-option>java</a-option><a-option>cpp</a-option><a-option>go</a-option><a-option>html</a-option></a-select></a-form-item></a-form><CodeEditor:value="form.code as string":language="form.language":handle-change="changeCode"/><a-divider size="0" /><a-button type="primary" style="min-width: 200px" @click="doSubmit">提交代码</a-button></a-col></a-row></div>
</template><script setup lang="ts">
import { onMounted, ref, watchEffect, withDefaults, defineProps } from "vue";
import message from "@arco-design/web-vue/es/message";
import CodeEditor from "@/components/CodeEditor.vue";
import MdViewer from "@/components/MdViewer.vue";import {QuestionControllerService,QuestionSubmitAddRequest,QuestionVO,
} from "../../../generated";interface Props {id: string;
}const props = withDefaults(defineProps<Props>(), {id: () => "",
});const question = ref<QuestionVO>();const loadData = async () => {const res = await QuestionControllerService.getQuestionVoByIdUsingGet(props.id as any);if (res.code === 0) {question.value = res.data;} else {message.error("加载失败," + res.message);}
};const form = ref<QuestionSubmitAddRequest>({language: "java",code: "",
});/*** 提交代码*/
const doSubmit = async () => {if (!question.value?.id) {return;}const res = await QuestionControllerService.doQuestionSubmitUsingPost({...form.value,questionId: question.value.id,});if (res.code === 0) {message.success("提交成功");} else {message.error("提交失败," + res.message);}
};/*** 页面加载时,请求数据*/
onMounted(() => {loadData();
});const changeCode = (value: string) => {form.value.code = value;
};
</script><style>
#viewQuestionView {max-width: 1400px;margin: 0 auto;
}#viewQuestionView .arco-space-horizontal .arco-space-item {margin-bottom: 0 !important;
}
</style>

路由

  {path: "/view/question/:id",name: "在线做题",component: ViewQuestionView,props: true,meta: {access: ACCESS_ENUM.USER,hideInMenu: true,},},

相关文章:

  • Docker部署MongoDB教程
  • Spring 事务管理-AOP
  • python学习-13【网络编程】
  • SBB Usage Parameters
  • Supervision 计算机视觉工具
  • 【网络安全】网络基础第一阶段——第四节:网络协议基础---- VRRP与网络架构设计
  • 基站定位与Wi-Fi定位?看这篇就够了
  • C++——认识STL及使用及实现第一个容器string
  • 关于若依前端界面缓存问题
  • 【Tomcat】常见面试题整理 共34题
  • Docker本地部署Chatbot Ollama搭建AI聊天机器人并实现远程交互
  • 如何在 macOS 上恢复未保存的 Excel 文件 – 文件恢复的最佳方法
  • IP地址与智能家居能够碰撞出什么样的火花呢?
  • more、less 命令:阅读文本
  • 记录一次排查sql server 服务调用异常的问题
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • 【剑指offer】让抽象问题具体化
  • 2017届校招提前批面试回顾
  • 30秒的PHP代码片段(1)数组 - Array
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • Android Volley源码解析
  • ERLANG 网工修炼笔记 ---- UDP
  • flask接收请求并推入栈
  • Java IO学习笔记一
  • JavaScript 基础知识 - 入门篇(一)
  • JavaScript服务器推送技术之 WebSocket
  • js作用域和this的理解
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • spring-boot List转Page
  • vue的全局变量和全局拦截请求器
  • 闭包--闭包作用之保存(一)
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 排序算法学习笔记
  • 收藏好这篇,别再只说“数据劫持”了
  • 手写一个CommonJS打包工具(一)
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 小程序开发中的那些坑
  • 新书推荐|Windows黑客编程技术详解
  • 回归生活:清理微信公众号
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • ​如何在iOS手机上查看应用日志
  • ‌分布式计算技术与复杂算法优化:‌现代数据处理的基石
  • # windows 运行框输入mrt提示错误:Windows 找不到文件‘mrt‘。请确定文件名是否正确后,再试一次
  • #include<初见C语言之指针(5)>
  • #pragam once 和 #ifndef 预编译头
  • $jQuery 重写Alert样式方法
  • (1)虚拟机的安装与使用,linux系统安装
  • (145)光线追踪距离场柔和阴影
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (附源码)ssm智慧社区管理系统 毕业设计 101635
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统