12-详细代码
question文件夹的question-add-or-update.vue新增修改组件:
<template>
<div class="questionEdit">
<el-dialog :visible.sync="dialogVisible" fullscreen>
<div slot="title" class="title">{{modal==='add'?'新增试题':'编辑试题'}}</div>
<el-form
class="form"
label-position="left"
:rules="rules"
:model="questionForm"
ref="questionFormRef"
label-width="120px"
>
<el-form-item label="学科" prop="subject">
<el-select v-model="questionForm.subject" placeholder="请选择学科">
<el-option
v-for="(item, index) in subjectList"
:key="index"
:value="item.id"
:label="item.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="阶段" prop="step">
<el-select v-model="questionForm.step" placeholder="请选择阶段">
<el-option v-for="(value, name) in stepObj" :key="name" :label="value" :value="+name"></el-option>
</el-select>
</el-form-item>
<el-form-item label="企业" prop="enterprise">
<el-select v-model="questionForm.enterprise" placeholder="请选择企业">
<el-option
v-for="(item, index) in enterpriseList"
:key="index"
:value="item.id"
:label="item.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="城市" prop="city">
<el-cascader
size="large"
:options="options"
v-model="questionForm.city"
:props="{value:'label'}"
></el-cascader>
</el-form-item>
<el-form-item label="题型" prop="type">
<el-radio-group v-model="questionForm.type">
<el-radio v-for="(value, name) in typeObj" :key="name" :label="+name">{{value}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="难度" prop="difficulty">
<el-radio-group v-model="questionForm.difficulty">
<el-radio v-for="(value, name) in difficultyObj" :key="name" :label="+name">{{value}}</el-radio>
</el-radio-group>
</el-form-item>
<hr class="hrMargin" />
<el-form-item label="试题标题" prop="title" class="setMargin">
<quill-editor
:options="{placeholder:'请输入标题....'}"
v-model="questionForm.title"
@change="onEditorChange('title')"
></quill-editor>
</el-form-item>
<el-form-item
:label="typeObj[questionForm.type]"
:prop="validateQuestionTypeObj[questionForm.type]"
>
<!-- 单选多选简答子组件 -->
<question-type :questionForm="questionForm" @childChange="onChildChange"></question-type>
</el-form-item>
<hr class="hrMargin" />
<el-form-item label="解析视频">
<!-- 把 questionForm中的video的值传递给子组件-->
<upload-file type="video" v-model="questionForm.video"></upload-file>
</el-form-item>
<hr class="hrMargin" />
<el-form-item label="答案解析" prop="answer_analyze" class="setMargin">
<quill-editor
:options="{placeholder:'请输入答案解析....'}"
v-model="questionForm.answer_analyze"
@change="onEditorChange('answer_analyze')"
></quill-editor>
</el-form-item>
<hr class="hrMargin" />
<el-form-item label="试题备注" prop="remark">
<el-input v-model="questionForm.remark"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="submit">确 定</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import { regionData } from "element-china-area-data";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
import { quillEditor } from "vue-quill-editor";
//导入子组件题型组件
import questionType from "./question-type";
// 导入子组件上传组件
import UploadFile from "./upload-file";
export default {
components: {
quillEditor,
questionType,
UploadFile
},
name: "QuestionEdit",
// 接收从父组件传来的值
// props: ['subjectList','enterpriseList'],
props: {
subjectList: Array,
enterpriseList: Array,
stepObj: Object,
typeObj: Object,
difficultyObj: Object
},
data() {
return {
//校验题型的时候prop要看情况看是单选还是多选还是简答
validateQuestionTypeObj: {
1: "single_select_answer",
2: "multiple_select_answer",
3: "short_answer"
},
dialogVisible: false, // 控制dialog的显示及隐藏
modal: "", // add 新增 edit 修改
options: regionData,
//模型
questionForm: {
title: "", // 标题
subject: "", // 学科id标识
step: "", //阶段1、初级 2、中级 3、高级
enterprise: "", // 企业id标识
city: [], // [省、市、区县]
type: 1, // 题型 1单选 、2多选 、3简答
difficulty: 1, // 题目难度 1简单 、2一般 、3困难
single_select_answer: "", // 单选题答案
multiple_select_answer: [], //多选题答案
short_answer: "", // 简答题答案
video: "", // 解析视频地址
answer_analyze: "", // 答案解析
remark: "", // 答案备注
select_options: [
{
label: "A",
text: "splice",
image: ""
},
{
label: "B",
text: "slice",
image: ""
},
{
label: "C",
text: "pop",
image: ""
},
{
label: "D",
text: "shift",
image: ""
}
] // 选项,介绍,图片介绍
},
rules: {
subject: [{ required: true, message: "请选择学科", trigger: "change" }],
step: [{ required: true, message: "请选择阶段", trigger: "change" }],
enterprise: [
{ required: true, message: "请选择企业", trigger: "change" }
],
city: [{ required: true, message: "请选择城市", trigger: "change" }],
type: [{ required: true, message: "请选择题型", trigger: "change" }],
difficulty: [
{ required: true, message: "请选择难度", trigger: "change" }
],
title: [{ required: true, message: "请输入标题", trigger: "change" }],
answer_analyze: [
{ required: true, message: "请输入答案解析", trigger: "change" }
],
remark: [{ required: true, message: "请输入备注", trigger: "change" }],
single_select_answer: [
{ required: true, message: "请输入单选答案", trigger: "blur" }
],
multiple_select_answer: [
{ required: true, message: "请输入多选答案", trigger: "blur" }
],
short_answer: [
{ required: true, message: "请输入简答题答案", trigger: "change" }
]
}
};
},
methods: {
// 对富文本编辑器中字段进行校验(试题标题跟答案解析)
onEditorChange(value) {
// 一改变就做校验
//validateField对对部分表单字段进行校验的方法
this.$refs.questionFormRef.validateField(value);
},
// 对QuestionType子组件中的 单选或多选或简答及时校验
onChildChange() {
//一改变就校验
this.$refs.questionFormRef.validateField([
"single_select_answer",
"multiple_select_answer",
"short_answer"
]);
},
//新增&修改
submit() {
this.$refs.questionFormRef.validate(async valid => {
if (!valid) return;
let res = "";
if (this.modal == "add") {
res = await this.$axios.post("/question/add", this.questionForm);
} else {
// 发送编辑请求的时候又要是字符串
this.questionForm.city = this.questionForm.city.join(",");
res = await this.$axios.post("/question/edit", this.questionForm);
}
if (res.data.code == 200) {
//提示
this.$message({
type: "success",
message: this.modal === "add" ? "新增成功" : "编辑成功"
});
//把当前对话框关掉
this.dialogVisible = false;
// 调用父组件search方法
this.$parent.search();
} else {
this.$message.error(res.data.message);
}
});
}
},
//监听器
watch: {
dialogVisible(newValue) {
//当dialogVisible是true的时候
if (newValue) {
this.$nextTick(() => {
this.$refs.questionFormRef.clearValidate(); //清空校验
});
}
}
}
};
</script>
question-type.vue题型子组件:
<template>
<div class="questionType">
<!-- 选中单选的时候 -->
<div v-if="questionForm.type==1">
<div v-for="(item, index) in questionForm.select_options" :key="index" class="item">
<!-- 单选radio -->
<el-radio @change="changeValue" v-model="questionForm.single_select_answer" :label="item.label">{{item.label}}</el-radio>
<!-- input -->
<el-input v-model="item.text" style="margin-right:15px"></el-input>
<!-- image -->
<upload-file v-model="item.image"></upload-file>
</div>
</div>
<!-- 选中多选的时候 -->
<div v-if="questionForm.type==2">
<div v-for="(item, index) in questionForm.select_options" :key="index" class="item">
<!-- 多选checkbox -->
<el-checkbox @change="changeValue" v-model="questionForm.multiple_select_answer" :label="item.label"></el-checkbox>
<!-- input -->
<el-input v-model="item.text" style="margin-left:15px;margin-right:15px"></el-input>
<!-- image -->
<upload-file v-model="item.image"></upload-file>
</div>
</div>
<!-- 选中简答的时候 -->
<div v-if="questionForm.type==3">
<el-input @change="changeValue" type="textarea" :rows="5" placeholder="请输入内容" v-model="questionForm.short_answer"></el-input>
</div>
</div>
</template>
<script>
// 导入子组件上传组件
import UploadFile from "./upload-file";
export default {
name: "QuestionType",
// 注册上传组件
components: {
UploadFile
},
props: ["questionForm"],
methods: {
changeValue(){
// 题型子组件里面的radio,checkbox以及input一改变就传这个事件去告诉父组件现在已经有值了可以把校验去掉了
this.$emit('childChange')
}
},
};
</script>
upload-file.vue上传子组件:
<template>
<div class="uploadFile">
<el-upload
class="avatar-uploader"
:action="BASE_URL+'/question/upload'"
name="file"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<div v-if="type==='video'">
<video v-if="value" :src="BASE_URL+'/'+value" controls class="avatar"></video>
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</div>
<div v-else>
<img v-if="value" :src="BASE_URL+'/'+value" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</div>
</el-upload>
</div>
</template>
<script>
export default {
//接受父组件传来的值
props: {
type: {
type: String, //这里表示type类型是字符串
default: "image" //这个代表接受父组件传来的type的值,如果没有传递参数默认就是image
},
value: String
},
data() {
return {
BASE_URL: process.env.VUE_APP_BASEURL
};
},
methods: {
handleAvatarSuccess(res) {
// 1、res.data.url ===> 父组件(question-add-or-update.vue) questionForm.video
// 2、让我们该子组件中的内容,根据实际情况进行渲染(有可能渲染图片、有可能渲染视频)
this.$emit("input", res.data.url);
},
beforeAvatarUpload(file) {
if (this.type == "video") {
const isVideo = file.type === "video/mp4";
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isVideo) {
this.$message.error("上传的文件只能是 mp4 格式!");
}
if (!isLt2M) {
this.$message.error("上传文件大小不能超过 2MB!");
}
return isVideo && isLt2M;
} else {
const isImage =
file.type === "image/jpeg" ||
file.type === "image/png" ||
file.type === "image/gif";
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isImage) {
this.$message.error("上传图片只能是 JPG/PNG/GIF 格式!");
}
if (!isLt2M) {
this.$message.error("上传图片大小不能超过 2MB!");
}
return isImage && isLt2M;
}
}
}
};
</script>
13-效果演示