Răsfoiți Sursa

添加试题的拖拽

lm 1 lună în urmă
părinte
comite
e60da4f3eb

BIN
src/assets/icon/move_up_down.png


+ 45 - 0
src/baseComponents/useHandleMoveUpAndDown.ts

@@ -0,0 +1,45 @@
+/**
+ * 处理列表的上下移动
+ */
+import { Ref, ref } from 'vue'
+
+export function useHandleMoveUpAndDown(moveableList: Ref, rowHeight: number = 42) {
+    // 处理试题的上下移动
+    const startYPointer = ref(0)  //记录移动开始点
+
+    const HandleDragStart = (e: DragEvent) => {
+        console.log('开始拖动了', e)
+        startYPointer.value = e.clientY
+    }
+    const HandleDragEnd = (e: DragEvent, row:any, currentIndex: number) => {
+        console.log('结束拖动了', e)
+        let endYPointer = e.clientY
+
+        let moveNum = 0
+        if(endYPointer > startYPointer.value){
+            // 下移层数
+            moveNum =   Math.ceil((endYPointer - startYPointer.value) / rowHeight)
+            let maxLength = moveableList.value.length
+            if(moveNum > 0){
+                // 删除当前位置数据
+                moveableList.value.splice(currentIndex,1)
+                // 在目标位置添加数据
+                moveableList.value.splice(Math.min(currentIndex + moveNum - 1,maxLength),0,row)
+            }
+        }else{
+            // 上移层数
+            moveNum = Math.ceil(( startYPointer.value - endYPointer) / rowHeight)
+            if(moveNum > 0){
+                // 删除当前位置数据
+                moveableList.value.splice(currentIndex,1)
+                // 在目标位置添加数据
+                moveableList.value.splice(Math.max(currentIndex - moveNum,0),0,row)
+            }
+        }
+    }
+
+    return {
+        HandleDragStart,
+        HandleDragEnd
+    }
+}

+ 80 - 26
src/views/surveyManagement/createQuestionnaire/CreateQuestionnaire.vue

@@ -2,7 +2,7 @@
     <div  ref="courseDom" class="main_container">
         <TopBackNav>
             <template #topRight>
-                <span class="paper_score">阅卷总分:<span class="score">60分</span></span>
+                <span class="paper_score">阅卷总分:<span class="score">{{questionTotalScore}}分</span></span>
                 <el-button class="button_refresh">预览问卷</el-button>
                 <el-button class="button_background" @click="HandleQuestionnaireSave">保存问卷</el-button>
             </template>
@@ -44,7 +44,6 @@
             <!-- 中间面板:编辑区域 -->
             <div class="center_panel">
                 <div class="page_item questionnaire_base">
-                    <!-- questionnaireInfo -->
                     <QuestionnaireBaseForm 
                         v-model:formRef="questionnaireBaseFormRef" 
                         :defaultFormData="{}"
@@ -116,14 +115,27 @@
             <!-- 右侧面板:目录 -->
             <div class="right_panel page_item">
                 <h3 class="section_title right_title">问卷目录 (拖动排序)</h3>
-                <div class="empty_state">暂无问卷题目</div>
+                <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.title }}
+                    </li>
+                </ul>
+
             </div>
         </div>
     </div>
 </template>
 
 <script lang="ts" setup>
-import {onMounted, ref} from 'vue'
+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'
@@ -131,8 +143,11 @@ 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 moveUpDownIcon from '@/assets/icon/move_up_down.png'
+
 
 // 题目选项信息 参数
 interface Option {
@@ -144,7 +159,7 @@ interface Option {
 interface QuestionItm {
     id:Number
     title: String   //题目名称
-    titleScore?: String  //题目分值
+    titleScore?: String | Number  //题目分值
     questionType: String   //题目类型
     extraConfig: String[]    // 是否必答 required, 是否横排 isHorizontal
     options: Option[]  //单选/多选 的 选项配置
@@ -166,12 +181,30 @@ const questionnaireInfo = ref<QuestionnaireInfo>({
     questionnaireDesc:''
 })
 // 已经添加的题型
-const addedQuestionList = ref<QuestionItm[]>([])
+const addedQuestionList = ref<QuestionItm[]>([
+    {
+        id:0,
+        title: '测试',   //题目名称
+        titleScore: 20,  //题目分值
+        questionType: '2',   //题目类型
+        extraConfig:[],    // 是否必答 required, 是否横排 isHorizontal
+        options: [] ,
+    }
+])
 // 当前选中的题型
 const selectedQuestionType = ref('')
-// 选中题型的类型 // 得分题  true  非得分题 false 
+// 选中题型 是否得分 // 得分题  true  非得分题 false 
 const selectedQuestionGetScore = ref(false)
-
+// 试卷总分
+const questionTotalScore = computed(()=>{
+    return addedQuestionList.value?.reduce((a,b)=>{
+        let aScore =  Number(a.titleScore || 0)
+        let bScore =  Number(b.titleScore || 0)
+        return {
+            titleScore:aScore + bScore
+        }
+    },{titleScore:0}).titleScore
+})
 
 
 // 选中题型时的处理
@@ -190,18 +223,25 @@ const CheckedQuestionType = (type:any) => {
     }
 }
 
-// 同步表单数据
+// 同步问卷头表单数据
 const SyncQuestionnaireFormData = (params)=>{
     questionnaireInfo.value = {...params}
 }
 
+// 处理试题排序
+const { HandleDragStart, HandleDragEnd } = useHandleMoveUpAndDown(addedQuestionList,42)
 
+
+const addedIndex = ref(1)
 // 添加试题信息
 const HandleAddQuesiton = (params)=>{
     const {formRef,formData} = params
     console.log('获取到试题信息了',formData)
 
-    let id = (Number(addedQuestionList.value[addedQuestionList.value.length - 1]?.id || 0) + 1)
+    // 不能用最后一个id来添加
+    let id = addedIndex.value + 1
+    addedIndex.value += 1
+    
     let options = formData.options.map(item=>{
         return {
             label:item.content,
@@ -214,6 +254,8 @@ const HandleAddQuesiton = (params)=>{
     selectedQuestionType.value = ''
 }
 
+
+
 // 保存试卷
 const HandleQuestionnaireSave = async ()=>{
     // 试卷总表单
@@ -239,13 +281,7 @@ const HandleQuestionnaireSave = async ()=>{
                     id:Number(questionnaireList[questionnaireList.length - 1]?.id || 0) + 1,
                     createTime:unioformDateTransform(new Date()),
                     createBy:'管理员',
-                    totalScore:addedQuestionList.value.reduce((a,b)=>{
-                        let aScore =  Number(a.titleScore || 0)
-                        let bScore =  Number(b.titleScore || 0)
-                        return {
-                            titleScore:aScore + bScore
-                        }
-                    }).titleScore,
+                    totalScore:questionTotalScore.value,
                     useStatus:1
                 })
                 localStorage.setItem('questionnaireList',JSON.stringify(questionnaireList))
@@ -285,17 +321,17 @@ const HandleQuestionnaireSave = async ()=>{
 }
 
 .questionnaire_config {
-    flex-grow: 1;
     display: flex;
     justify-content: space-between;
+    align-items: flex-start;
     gap: 20px;
 
-    position: relative;
+    max-height: calc(100vh - 116px - 60px);
+    height: calc(100vh - 116px - 60px);
+    overflow-y:auto ;
     
     /* --- 左侧面板 --- */
     .left_panel {
-        position: fixed;
-        left: 20px;
         width: 200px;
         display: flex;
         flex-direction: column;
@@ -336,13 +372,14 @@ const HandleQuestionnaireSave = async ()=>{
 
     /* --- 中间面板 --- */
     .center_panel {
+        max-height:100%;
+        height:100%;
         width: calc(100vw - 440px - 80px);
-        margin-left: calc(200px + (100% - 440px -  calc(100vw - 440px - 80px))/2);
         display: flex;
         flex-direction: column;
         border-radius: 10px;
-
         background-color: white;
+        overflow-y:auto ;
 
         .questionnaire_base{
             border-radius:10px 10px 0 0;
@@ -413,8 +450,9 @@ const HandleQuestionnaireSave = async ()=>{
 
     /* --- 右侧面板 --- */
     .right_panel {
-        position: fixed;
-        right: calc(20px + 6px);
+        max-height:100%;
+        overflow-y:auto ;
+
         align-self: flex-start;
         width: 240px;
         min-height: calc(100vh - 156px - 40px);
@@ -428,6 +466,22 @@ const HandleQuestionnaireSave = async ()=>{
             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 {