请求函数

export function addQuestionService(params = {}) {
  return service({
    url: "/question/add",
    method: "post",
    data: params,
  });
}
export function getQuestionDetailService(questionId) {
  return service({
    url: "/question/detail",
    method: "get",
    params: { questionId },
  });
}
export function editQuestionService(params = {}) {
  return service({
    url: "/question/edit",
    method: "put",
    data: params,
  });
}
export function delQuestionService(questionId) {
  return service({
    url: "/question/delete",
    method: "delete",
    params: { questionId },
  });
}

这个我们已经写了好多次了,我们不再赘述

增加弹框


<script setup>
import { ref, reactive } from 'vue'
import CodeEditor from './CodeEditor.vue';
import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
</script>

细节分析

安装插件

富文本编译器

代码编译器

富文本编译器的使用直接引入就行

代码编译器的引入需要我们引入子组件


<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue"
import ace from "ace-builds"
import "ace-builds/src-noconflict/mode-java"
import "ace-builds/src-noconflict/theme-eclipse"
import "ace-builds/src-noconflict/ext-language_tools";
// 定义选项
const options = {
  theme: `ace/theme/eclipse`,
  mode: `ace/mode/java`,
  maxLines: 20,
  minLines: 10,
  fontSize: 15,
};
// 创建响应式引用
let editor = null;
const emit = defineEmits(['update:value']);
const editorform = ref(null);
// 初始化编辑器
onMounted(() => {
  editor = ace.edit(editorform.value, options);
  editor.setOptions({
    enableBasicAutocompletion: true,
  });
  editor.getSession().on('change', () => {
    // 当编辑器内容变化时,触发自定义事件并传递编辑器的内容
    emit('update:value', editor.getValue());
  });
});
// 销毁编辑器实例
onBeforeUnmount(() => {
  if (editor) {
    editor.destroy();
    editor = null;
  }
});
</script>

这个时候我们去访问前端页面,点击增加题目,发现无反应,因为我们还没有引入抽屉

但是这个时候,我们发现点击之后还是美颜弹出

默认不展示

现在去访问,我们发现有报错,这是因为我们之前写的代码,具有双向绑定,但是它没有找到参数,所以我们创建一个参数(响应式数据)

效果展示

目前当我们一进去之后,就会有弹框出现;

但是目前还有很多问题:

1.弹框的大小有点不符合我们预期,而且上面有很大一片空白

2.弹框是自动弹出的,而不是我们点击添加题目弹出的

3.现在我们点击增加题目,我们发现不会有弹框

解决问题(1)

这个时候,我们突然发现,怎么题目难度框没有选择项呢?

这是因为我们没有引入难度选择组件

解决问题(2)

解决问题(3)

其实也很好想,无非就是增加一个点击事件,当我们点击的时候触发

但是,我们现在是在父组件中操作,但操作参数是子组件的内容,所以我们需要一个ref来引用

在子组件中创建一个函数,然后暴露出来

然后再父组件中实现方法

现在只有我们点击增加题目的时候才会弹出

点击发布的时候会触发一个点击事件

上面这个函数的作用是判断是否有空的数据,如果有,就抛出异常

function validate() {
  let msg = ''
  if (!formQuestion.title) {
    msg = '请添加题目标题'
  } else if (formQuestion.difficulty === '') {
    msg = '请选择题目难度'
  } else if (!formQuestion.timeLimit) {
    msg = '请输入时间限制'
  } else if (!formQuestion.spaceLimit) {
    msg = '请输入空间限制'
  } else if (!formQuestion.content) {
    msg = '请输入题目内容信息'
  } else if (!formQuestion.questionCase) {
    msg = '请输入题目用例名称'
  } else if (!formQuestion.defaultCode) {
    console.log("formQuestion.defaultCode:", formQuestion.defaultCode)
    msg = '请输入默认代码'
  } else if (!formQuestion.mainFuc) {
    msg = '请输入main函数'
  } else {
    msg = ''
  }
  return msg
}
async function onSubmit() {
  const errorMessage = validate()
  if (errorMessage) {
    ElMessage.error(errorMessage);
    return false
  }
  const fd = new FormData()
  for (let key in formQuestion) {
    fd.append(key, formQuestion[key])
  }
  await addQuestionService(fd)
  ElMessage.success('添加成功')
  visibleDrawer.value = false
}

这里我们可以发现和之前传递参数有些不同

这是因为我们这里是一个post请求,而且参数是在Body里面

先将用不上的去掉,得到下面

这是因为我们是在子组件中修改的参数,但是还没有同步到父组件

我们在这里定义一个事件

当监听器发现事件触发的时候

父组件定义一个事件处理器,当事件触发的时候进行调用,编译器中参数传给事件处理器

function handleEditorContent(content) {
  formQuestion.defaultCode = content
}
function handleEditorMainFunc(content) {
  formQuestion.mainFuc = content
}

这是因为我们传输的是一个json数据

所以我们增加一个全局的请求头添加配置

这样我们后端如果是接受json类型的时候,我们就可以进行转化了

特殊情况:GET/DELETE 请求通常不受影响

  • GET/DELETE 请求一般通过 URL 参数(params)传递数据,而非请求体(data),因此 Content-Type 对它们的影响很小(服务器通常会忽略这些请求的 Content-Type)。
  • 例如获取用户信息的 GET 请求,即使带上 application/json 头,只要参数通过 params 正确传递,后端仍能正常解析。

所以我们之前的不受影响

这时候我们就可以添加成功了,但是我们需要刷新一下才能显示,这是因为我们没有进行请求题目列表

这里我们子组件增加成功之后告诉父组件(和之前的方式一样)

现在增加成功后就会显示了

但是当我们如果切换到第二页,然后进行添加操作的时候,我们会发现没有显示,因为我们还是在第二页,所以我们继续修改

我们这个时候再次点击添加,我们发现,并没有清空上次的内容

这时候我们添加配置

关闭的时候自动清空

但是我们发现,上面输入框里面的并没有清空,这是因为双向绑定的原因

所以我们在打开页面的时候,将数据进行清空

这样增加前端就解决了

修改题目

增加一个点击事件

当我们点击编辑的时候,调用点击事件

由于我们需要获得参数questionId,所以我们需要使用插槽进行获取

由于是我们在子组件中进行获取详细信息操作,但是又需要父组件中的参数,那么我们可以使用之前的方法

在open中添加questionId,如果参数传了id,那么进行获得详情操作

由于我们进行了双向绑定,所以:我们需要进行赋值,但是由于我们的内容是封装为一个R的返回类型,所以我们需要使用questionDeatil.data

然后在父组件中调用open方法

此时点击编辑的时候,会报后端错误

这个是因为我们后端使用了雪花算法,但是我们后端定义的参数是Long类型的,溢出了,导致qusetionId有了问题

将ID的类型变为String类型

我们可以看到我们的前端题目创建时间不太好,所以进行格式修改

我们现在发现下面的富文本和代码部分没有展示详情

我们可以发现,后端是正确地返回了结果的,所以是前端问题

首先,富文本编译器那里是因为我们没有增加文本类型

现在展示出了题目内容,但是我们发现题目内容不对

查看数据库中的Content

这个原因是因为我们当时存的时候也没有指定类型,所以没有存对

现在我们新增一个

现在我们可以发现,代码块区域还是没有显示代码

我们观察代码

发现和前面的不一样,前面都是直接v-model双向绑定,而我们这里是调用的子组件,所以无法显示,那么我们和之前一样(子组件接收父组件的参数)

前情回顾

正式操作

下面就是修改完之后的发布

如果我们点击发布,我们之前的代码是不知道到底是增加还是删除

但是我们知道增加的时候我们是不知道它的questionId的,但是修改的时候知道,所以根据有无Id进行判断,如果有ID进行编辑

但是还有一些瑕疵:我们修改完之后会和增加一样跳到第一页,导致可能无法看到当前修改的信息

所以,可以在emit中传入参数,然后在父组件中进行判断是否跳到第一页

删除题目

前面俩个难的已经解决,剩下一个简单的

我们这里增加一个确认删除,防止误删