vue3中reactive()应用

目录

【功能描述】

select组件的用法

实现思路

代码实现

答案


在实际项目中学语言技术,学得最透彻。也先说遇到这几天遇到得问题。

【功能描述】

题库系统的一个小功能,就是通过网页表单录入一个题目,有内容、答案、解析、知识点等字段。其中知识点字段用下拉选择框,选择项从后台获取。效果如下,左图是页面加载后,右图是点击知识点下拉框的选项。

     

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>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值