| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- <template>
- <div ref="courseDom" class="main_container">
- <TopBackNav>
- <template #topRight>
- <span class="paper_score">阅卷总分:<span class="score">{{questionTotalScore}}分</span></span>
- <el-button class="button_refresh" @click="PreviewQuestionnaire">预览问卷</el-button>
- <el-button class="button_background" @click="SaveQuestionnaire">保存问卷</el-button>
- </template>
- </TopBackNav>
- <div class="page_jg_20" style="flex-shrink: 0;"></div>
- <div class="questionnaire_config" >
- <div class="left_panel">
- <div class="panel_section page_item">
- <h3 class="section_title">统计题</h3>
- <!-- 题型循环 -->
- <div class="type_list">
- <template v-for="typeItm in statisticQuestionType" :key="typeItm.value">
- <el-button class="button_refresh"
- @click="CheckedQuestionType(typeItm.value)" >
- <img :src="typeItm.icon" alt="">
- {{ typeItm.label }}
- </el-button>
- </template>
- </div>
- </div>
- <div class="panel_section page_item">
- <h3 class="section_title">评分题</h3>
- <!-- 题型循环 -->
- <div class="type_list">
- <template v-for="typeItm in scoredQuestionType" :key="typeItm.value">
- <el-button class="button_refresh"
- @click="CheckedQuestionType(typeItm.value)">
- <img :src="typeItm.icon" alt="">
- {{ typeItm.label }}
- </el-button>
- </template>
- </div>
- </div>
- </div>
- <!-- 中间面板:编辑区域 -->
- <div class="center_panel">
- <div class="page_item questionnaire_base">
- <QuestionnaireBaseForm
- v-model:formRef="questionnaireBaseFormRef"
- :defaultFormData="{}"
- @formChage="SyncQuestionnaireFormData"
- ></QuestionnaireBaseForm>
- </div>
- <!-- 已添加题目展示区 -->
- <ul class="question_list_area">
- <li v-for="(question,index) in addedQuestionList" :key="question.id">
- <div class="question_title"
- :class="{required:question.extraConfig.includes('required')}">
- {{index + 1}}.{{ question.questionTitleName }}
- <span class="question_type">[{{ qustionTypeToName(question.questionType) }}]</span>
- </div>
- <QuestionTypeShow
- :isHorizontal="question.extraConfig.includes('isHorizontal')"
- :questionType="Number(question.questionType)"
- :questionSubOptions="question.options"
- :evaluationType="Number(question.evaluationType)"
- :questionScore="Number(question.questionScore)"
- ></QuestionTypeShow>
- </li>
- </ul>
- <!-- 题目添加区域 -->
- <div class=" add_question_area">
- <div v-if="!selectedQuestionType" class="add_question">
- <div class="add_action">+ 点击左侧添加题型</div>
- </div>
- <div v-else class="question_itm_edit">
- <QuestionConfig
- :displayMode="selectedQuestionGetScore ? 'scored' : 'statistic'"
- :questionType="selectedQuestionType"
- @confirm="AddQuesiton"
- ></QuestionConfig>
- </div>
- </div>
- </div>
- <!-- 右侧面板:目录 -->
- <div class="right_panel page_item">
- <h3 class="section_title right_title">问卷目录 (拖动排序)</h3>
- <div v-if="!(addedQuestionList?.length >0)" class="empty_state">暂无问卷题目</div>
-
-
- <ul v-else class="question_list_brief">
- <li v-for="(question,index) in addedQuestionList" :key="question.id">
- <img :src="moveUpDownIcon" alt=""
- :draggable="true"
- @dragstart="(e)=>HandleDragStart(e)"
- @dragend="(e)=>HandleDragEnd(e,question,index)"
- >
- {{ index + 1 }}.{{ question.questionTitleName }}
- </li>
- </ul>
- </div>
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import {computed, onMounted, ref} from 'vue'
- import { useRouter } from 'vue-router';
- import TopBackNav from '@/components/TopBackNav.vue';
- import {statisticQuestionType,scoredQuestionType,qustionTypeToName} from '../../../components/questionConfig/questionTypeList'
- import QuestionConfig from '@/components/questionConfig/QuestionConfig.vue';
- import QuestionnaireBaseForm from '@/components/questionConfig/QuestionnaireBaseForm.vue';
- import { ElMessage } from 'element-plus';
- import { unioformDateTransform } from '@/utils/dateTransform';
- import { useHandleMoveUpAndDown } from '@/baseComponents/useHandleMoveUpAndDown';
- import QuestionTypeShow from '@/components/QuestionTypeShow.vue';
- import moveUpDownIcon from '@/assets/icon/move_up_down.png'
- import {addQuestionnaireApi} from '@/api/surveyManagement'
- // 题目选项信息 参数
- interface Option {
- label: String
- score?: String
- value?: Number
- }
- // 题目信息 参数
- interface QuestionItm {
- id:Number
- questionTitleName: String //题目名称
- questionScore?: String | Number //题目分值
- questionType: String | String //题目类型
- extraConfig: String[] // 是否必答 required, 是否横排 isHorizontal
- options: Option[], //单选/多选 的 选项配置
- evaluationType: Number|String // 评分题 的 分值展示形式
- }
- interface QuestionnaireInfo {
- assessmentTitle:String,
- assessmentIllustrate:String
- }
- const router = useRouter()
- // 试卷头表单
- const questionnaireBaseFormRef = ref()
- // 已存储的试卷整体信息
- const questionnaireMsg = ref()
- // 试卷头基本信息
- const questionnaireInfo = ref<QuestionnaireInfo>({
- assessmentTitle:'',
- assessmentIllustrate:''
- })
- // 已经添加的题型
- const addedQuestionList = ref<QuestionItm[]>([
- {
- id:0,
- questionTitleName: '测试', //题目名称
- questionScore: 20, //题目分值
- questionType: '2', //题目类型
- extraConfig:[], // 是否必答 required, 是否横排 isHorizontal
- options: [] ,
- evaluationType:''
- }
- ])
- // 当前选中的题型
- const selectedQuestionType = ref('')
- // 选中题型 是否得分 // 得分题 true 非得分题 false
- const selectedQuestionGetScore = ref(false)
- // 试卷总分
- const questionTotalScore = computed(()=>{
- return addedQuestionList.value?.reduce((a,b)=>{
- let aScore = Number(a.questionScore || 0)
- let bScore = Number(b.questionScore || 0)
- return {
- questionScore:aScore + bScore
- }
- },{questionScore:0}).questionScore
- })
- // 选中题型时的处理
- const CheckedQuestionType = (type:any) => {
- selectedQuestionType.value = type
- switch(type){
- case '0':
- case '1':
- case '2':
- selectedQuestionGetScore.value = false
- break
- case '3':
- case '4':
- selectedQuestionGetScore.value = true
- break
- }
- }
- // 同步问卷头表单数据
- const SyncQuestionnaireFormData = (params)=>{
- questionnaireInfo.value = {...params}
- }
- // 处理试题排序
- const { HandleDragStart, HandleDragEnd } = useHandleMoveUpAndDown(addedQuestionList,42)
- const addedIndex = ref(1)
- // 添加试题信息
- const AddQuesiton = (params)=>{
- const {formRef,formData} = params
- console.log('获取到试题信息了',formData)
- // 不能用最后一个id来添加
- let id = addedIndex.value + 1
- addedIndex.value += 1
-
- let options = formData.options.map(item=>{
- return {
- label:item.content,
- value:item.id,
- score:item.score
- }
- })
- addedQuestionList.value.push({id ,...formData,options})
- formRef.resetFields()
- selectedQuestionType.value = ''
- }
- // 保存试卷
- const SaveQuestionnaire = async ()=>{
- // 试卷总表单
- let formRef = questionnaireBaseFormRef.value
- if (!formRef) return
- await formRef.validate(async(valid) => {
- if (valid) {
- if(addedQuestionList.value.length > 0){
- // addQuestionnaireApi()
- /**
- * 模拟数据
- */
- // 模拟保存数据 和页面跳转
- let questionnaireList = JSON.parse(localStorage.getItem('questionnaireList') || '[]')
- questionnaireMsg.value = {
- ...(questionnaireInfo.value),
- questionData:addedQuestionList.value,
- id:Number(questionnaireList[questionnaireList.length - 1]?.id || 0) + 1,
- createTime:unioformDateTransform(new Date()),
- createBy:'管理员',
- assessmentScore:questionTotalScore.value,
- useStatus:1
- }
- questionnaireList.push(questionnaireMsg.value)
- localStorage.setItem('questionnaireList',JSON.stringify(questionnaireList || []))
- }else{
- ElMessage.warning('请添加问卷题目')
- }
- }
- }).catch(err=>{
- console.log('报错信息',err)
- })
- }
- // 预览试卷
- const PreviewQuestionnaire =()=>{
- sessionStorage.setItem('questionnaire',JSON.stringify(questionnaireMsg.value || '{}'))
- router.push(`/main/previewQuestionnaire`)
- }
- </script>
- <style lang="scss" scoped>
- :deep(.top_header){
- .top_right{
- display: flex;
- align-items: center;
- .el-button{
- margin-left:0px !important;
- &.button_refresh{
- margin-right: 10px;
- }
- }
- .paper_score{
- font-weight: 600;
- margin-right: 20px;
- .score{
- color:#2E64FA;
- }
- }
- }
- }
- .questionnaire_config {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- gap: 20px;
- max-height: calc(100vh - 116px - 60px);
- height: calc(100vh - 116px - 60px);
- overflow-y:auto ;
-
- /* --- 左侧面板 --- */
- .left_panel {
- width: 200px;
- display: flex;
- flex-direction: column;
- gap: 20px;
- .panel_section {
- padding: 16px 20px;
- .type_list {
- display: flex;
- flex-direction: column;
- gap: 16px;
-
- .el-button {
- display: flex;
- justify-content: flex-start;
- align-items: center;
- padding:12px;
- margin: 0;
- font-size: 14px;
- color:#666666;
- img{
- vertical-align: middle;
- width: 14px;
- height: 14px;
- border-radius: 50%;
- margin-right: 8px;
- position: relative;
- }
- &:hover{
- color:#2E64FA;
- border:solid #2E64FA 1px;
- }
-
- }
- }
- }
- }
- /* --- 中间面板 --- */
- .center_panel {
- max-height:100%;
- height:100%;
- width: calc(100vw - 440px - 80px);
- display: flex;
- flex-direction: column;
- border-radius: 10px;
- background-color: white;
- overflow-y:auto ;
- .questionnaire_base{
- border-radius:10px 10px 0 0;
- padding-bottom: 0;
- }
-
- // 已添加试题区
- .question_list_area{
- display: flex;
- flex-direction: column;
- gap:20px;
- padding:20px;
- font-size: 14px;
- color:#000;
- li{
- .question_title{
- margin-bottom: 20px;
- .question_type{
- color:#2E64FA;
- margin-left: 8px;
- }
- &.required{
- position: relative;
- padding-left: 5px;
- ::before{
- content:'*';
- position: absolute;
- left:0;
- color:#F56C6C;
- }
- }
- }
- }
- }
- // 添加试题区域
- .add_question_area{
- .add_question {
- // flex-grow: 0;
- margin:20px;
- border: 1px dashed #C0C4CC;
- border-radius: 4px;
- display: flex;
- justify-content: center;
- align-items: center;
- height: 240px;
- .add_action {
- color: #c0c4cc;
- font-size: 16px;
- cursor: pointer;
- }
- }
- .question_itm_edit {
- // flex-grow: 0;
- background-color: #F8FAFE;
- }
- }
- }
- /* --- 右侧面板 --- */
- .right_panel {
- max-height:100%;
- overflow-y:auto ;
- align-self: flex-start;
- width: 240px;
- min-height: calc(100vh - 156px - 40px);
- display: flex;
- flex-direction: column;
- .empty_state {
- flex: 1;
- display: flex;
- justify-content: center;
- align-items: center;
- font-size: 16px;
- color: #C0C4CC;
- }
- .question_list_brief{
- display: flex;
- flex-direction: column;
- font-size: 14px;
- li{
- display: flex;
- align-items: center;
- gap:8px;
- padding:10px 0;
- img{
- width:20px;
- height:20px;
- }
- }
- }
- }
- .section_title {
- font-weight: 600;
- font-size: 16px;
- margin-bottom: 10px;
- }
- }
- </style>
|