目录
在实际项目中学语言技术,学得最透彻。也先说遇到这几天遇到得问题。
【功能描述】
题库系统的一个小功能,就是通过网页表单录入一个题目,有内容、答案、解析、知识点等字段。其中知识点字段用下拉选择框,选择项从后台获取。效果如下,左图是页面加载后,右图是点击知识点下拉框的选项。
select组件的用法
于是我参考了element-plus官网的select组件Demo,选项的关键代码如下:
const options = [
{
value: 'Option1',
label: 'Option1',
},
……略去部分代码
{
value: 'Option5',
label: 'Option5',
},
]
即用非响应式变量options作为选择内容的数据来源。
接下来,我先说下我错误的做法,读者可以试着思考找错,我到底犯了什么错误(表扬挑错成功的同学)。以下是关键代码,完整代码在最后附上。
实现思路
思路是:①编写template模板(确定无误),②script setup部分编写钩子函数onMounted:读取后台“知识点”数据,并把数组赋值给全局变量knowledgepointOption,用于显示选择下拉框的数组。
代码实现
以下是template的部分代码
<el-form :model="state.questionForm" :rules="state.rules" ref="questionRef" label-width="100px" class="questionForm">
<el-form-item label="题目:" prop="content">
<div ref='editor' style="width:100%"></div>
</el-form-item>
<template v-if="state.knowledgeId ==0">
<el-form-item label="知识点:" prop="knowledgepoint">
<el-select v-model="state.questionForm.knowledgepoint" placeholder="知识点" size="small" class="m-2">
<el-option
v-for="item in knowledgepointOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</template>
<el-form-item label="答案:" prop="answer">
<el-input style="width: 300px" v-model="state.questionForm.answer" placeholder="请输入参考答案"></el-input>
</el-form-item>
</el-form>
以下是<script setup>部分代码。总共2处思路错误,语法没有问题的哦,你能找出来吗?
<script setup>
const knowledgepointOption = []
const difficultyLevelOption = [
{
value : 1,
label : '入门',
},
//略去部分代码
{
value : 5,
label : '压轴',
},
]
onMounted(() => {
getChoiceList()
//其他代码略
})
// 后台读取知识点列表
const getChoiceList =()=>{
axios.get(`/knowledgepoints?size=100`).then(res =>{
if (res.code == 200){
const temp_list = res.data.results
knowledgepointOption =[] //清空数组内容
for (let i=0; i<temp_list.length; i++){
knowledgepointOption.push({value:temp_list[i].id, label:temp_list[i].name})
}
}else{
console.log(data.message)
}
})
}
</script>
好了,接下来公布答案了。
答案
第一处错误是:const knowledgepointOption =[ ]
应该改成const knowledgepointOption = reactive([]) 否则下拉选择框的数据不会更新。
第二处错误是knowledgepointOption =[] //清空数组内容,提示常量不能被赋值。
应该改成knowledgepointOption.splice(0),也可以用其他方法,度娘里很多,我也是第一次。
平时看教材的时候也注意到reactive()和ref()响应式的用法,但是实际项目的具体应用总是忘记。
所以这个不算vue的坑,是自己没有真正学懂vue的响应式前端的精髓。
下次再见,我会回来的
(找不到哪里上传代码文件,知道的给我留言,栓Q)
以下是完整的代码:
<template>
<div class="add">
<el-card class="add-container">
<el-form :model="state.questionForm" :rules="state.rules" ref="questionRef" label-width="100px" class="questionForm">
<el-form-item label="题目:" prop="content">
<div ref='editor' style="width:100%"></div>
</el-form-item>
<template v-if="state.knowledgeId ==0">
<el-form-item label="知识点:" prop="knowledgepoint">
<el-select v-model="state.questionForm.knowledgepoint" placeholder="知识点" size="small" class="m-2">
<el-option
v-for="item in knowledgepointOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</template>
<el-form-item label="答案:" prop="answer">
<el-input style="width: 300px" v-model="state.questionForm.answer" placeholder="请输入参考答案"></el-input>
</el-form-item>
<el-form-item label="解析:" prop="explain">
<el-input v-model="state.questionForm.explain" placeholder="解析:"></el-input>
</el-form-item>
<el-form-item label="来自:" prop="reference">
<el-input style="width: 300px" v-model="state.questionForm.reference" placeholder="来自什么资料"></el-input>
</el-form-item>
<el-form-item label="题型:" prop="type">
<el-select v-model="state.questionForm.type" class="m-2" placeholder="题型" size="small">
<el-option
v-for="item in questionTypeOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="难度:" prop="difficulty_level">
<el-select v-model="state.questionForm.difficulty_level" class="m-2" placeholder="难度:" size="small">
<el-option
v-for="item in difficultyLevelOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="公开:" prop="open_level">
<el-select v-model="state.questionForm.open_level" class="m-2" placeholder="公开/私密" size="small">
<el-option
v-for="item in openLevelOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitAddOneQuestion">{{ state.questionId ?'立即修改' : '立即录入' }}</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup>
import WangEditor from 'wangeditor'
import axios from '@/utils/axios'
import { ElMessage } from 'element-plus'
import { reactive, ref, toRefs, onMounted, onBeforeUnmount, onBeforeMount } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { localGet, uploadImgServer, uploadImgsServer } from '@/utils'
const editor = ref(null) // 富文本编辑器 ref
const questionRef = ref(null)
const route = useRoute()
const router = useRouter()
const { questionId } =route.query
const knowledgepointOption = reactive([])
const questionTypeOption = [
{
value : '选择题',
label : '选择题',
},
{
value : '填空题',
label : '填空题',
},
{
value : '简答题',
label : '简答题',
},
]
const difficultyLevelOption = [
{
value : 1,
label : '入门',
},
{
value : 2,
label : '中级',
},
{
value : 3,
label : '较难',
},
{
value : 4,
label : '高级',
},
{
value : 5,
label : '压轴',
},
]
const openLevelOption = [
{
value : 'public',
label : '所有人可见',
},
{
value : 'private',
label : '仅自己可见',
},
{
value : 'friend',
label : '好友可见',
},
]
const state = reactive({
uploadImgServer, // 上传图片的接口地址,单图上传
token: localGet('token') || '', // 存在本地的 token
knowledgeId: 0,
pictures: "",
questionForm:{ //题目表单
content: "",
answer: "",
explain: "",
reference: "",
type: "填空题",
difficulty_level: "中级",
open_level: "public",
knowledgepoint: 0,
},
rules: { // 规则
content: [
{required: 'true', message: '请填写题目内容', trigger: ['change']}
],
knowledgepoint: [
{required: 'true', message: '请选择知识点', trigger: ['change']}
],
answer: [
{ required: 'true', message: '请填写参考答案', trigger: ['change'] }
],
explain: [
{ required: 'true', message: '请填写解析', trigger: ['change'] }
],
type: [
{ required: 'true', message: '请选择题型', trigger: ['change'] }
],
difficulty_level: [
{ required: 'true', message: '请选择难度', trigger: ['change'] }
],
},
})
let instance // wangEditor 实例
onMounted(() => {
console.log("路由参数:", route.query)
let { knowledgepointId } = route.query
console.log("传递过来的知识点id:", knowledgepointId)
if (knowledgepointId){
state.knowledgeId = knowledgepointId
state.questionForm.knowledgepoint = knowledgepointId
}else{
getChoiceList()
}
instance = new WangEditor(editor.value) // 初始化 wangEditor
instance.config.showLinkImg = false
instance.config.showLinkImgAlt = false
instance.config.showLinkImgHref = false
instance.config.uploadImgMaxSize = 2 * 1024 * 1024 // 最大上传大小 2M
instance.config.uploadFileName = 'images' // 上传时,key 值自定义
instance.config.height = 300
instance.config.placeholder = "请输入题目内容:"
// 自定义菜单
instance.config.menus = [
// 'head',
'bold', //字体加粗
// 'fontSize',//字号
'fontName',//字体
// 'italic',
'underline',//下划线
// 'strikeThrough',//删除线
// 'indent',
// 'lineHeight',
'foreColor',
// 'backColor',
'link',
// 'list',//列表
// 'todo',
'justify',//对齐
'quote',// 引用
'emoticon',
'image', //图片
// 'video',//视频
'table',//表格
'code',//计算机代码
'splitLine',
'undo',//撤销
'redo',//恢复
];
instance.config.uploadImgHeaders = {
token: state.token // 添加 token,否则没有权限调用上传接口
}
// 图片返回格式不同,需要自定义返回格式
instance.config.uploadImgHooks = {
// 图片上传并返回了结果,想要自己把图片插入到编辑器中
// 例如服务器端返回的不是 { errno: 0, data: [...] } 这种格式,可使用 customInsert
customInsert: function(insertImgFn, result) {
// console.log('result', result)
// result 即服务端返回的接口
// insertImgFn 可把图片插入到编辑器,传入图片 src ,执行函数即可
if (result.data && result.data.length) {
insertImgFn(result.data)
state.pictures = state.pictures + result.data + '-'
//多张图片的时候用循环遍历
// result.data.forEach((item) => {
// insertImgFn(item)
// state.pictures.push(item)
// })
}
}
}
instance.config.uploadImgServer = uploadImgServer // 上传接口地址配置
Object.assign(instance.config, {
onchange() {
console.log('change')
state.questionForm.content = editor.value
},
})
instance.create()
if (questionId){
axios.get(`/questions/${questionId}`).then(res =>{
if (res.code == 200){
const { question } = res.data
state.questionForm = {
answer: question.answer,
explain: question.explain,
reference: question.reference,
type: question.type,
difficulty_level: question.difficulty_level,
open_level: question.open_level,
knowledgepoint: question.knowledgepoint,
pictures: question.pictures
}
if(instance){
//初始化题目内容 html
instance.txt.html(question.content)
}
}else{
console.log(data.message)
}
})
}
})
onBeforeUnmount(() => {
// 组件销毁之前,销毁 wangEditor 实例
instance.destroy()
instance = null
})
// 录入单题方法
const submitAddOneQuestion = () => {
questionRef.value.validate((vaild) => {
if (vaild) {
// 默认新增用 post 方法
let httpOption = axios.post
let params = {
answer: state.questionForm.answer,
explain: state.questionForm.explain,
content: instance.txt.html(),
reference: state.questionForm.reference,
type: state.questionForm.type,
difficulty_level: state.questionForm.difficulty_level,
knowledgepoint: state.questionForm.knowledgepoint,
open_level: state.questionForm.open_level,
top: state.questionForm.top,
pictures: state.pictures
}
console.log('params', params)
if (questionId) {
params.id = questionId
// 修改商品使用 put 方法
httpOption = axios.put
}
httpOption('/questions/', params).then((result) => {
console.log(result)
ElMessage.success(questionId ? '修改成功' : '添加成功')
router.go(-1)
})
}
})
}
// 后台读取知识点列表
const getChoiceList =()=>{
axios.get(`/knowledgepoints?size=100`).then(res =>{
if (res.code == 200){
const temp_list = res.data.results
knowledgepointOption.splice(0)
for (let i=0; i<temp_list.length; i++){
knowledgepointOption.push({value:temp_list[i].id, label:temp_list[i].name})
}
}else{
console.log(data.message)
}
})
}
</script>
<style scope>
.add {
display: flex;
padding: 0;
margin: 0;
}
.add-container {
margin: 0 0;
padding:0 0;
flex: 1;
}
.avatar-uploader {
width: 100px;
height: 100px;
color: #ddd;
font-size: 30px;
}
.avatar-uploader-icon {
display: block;
width: 100%;
height: 100%;
border: 1px solid #e9e9e9;
padding: 32px 17px;
}
.affix-container {
text-align: center;
height: 10px;
border-radius: 4px;
background: var(--el-color-primary-light-9);
}
</style>