Procházet zdrojové kódy

添加评价管理列表 视图/图表展示

lm před 1 měsícem
rodič
revize
8a5cc30ba6

+ 43 - 18
src/components/questionnaireCard/QuestionnaireCard.vue

@@ -1,14 +1,16 @@
 <template>
      <ul class="questionnaire_list">
-        <li v-for="quesitonnaire in questionnaireList" :key="quesitonnaire.id" class="card_item">
+        <li v-for="quesitonnaire in dynamicQuestionnaireList" 
+            :key="quesitonnaire.id" class="card_item">
             <!-- 顶部区域:图标 + 标题 -->
             <div class="card_header">
                 <!-- 模拟图片中的文档图标 -->
                 <img src="" alt="" class="card-icon" />
                 <span class="card_title two_line_ellipsis">{{ quesitonnaire.assessmentTitle }}</span>
                 <el-switch
+                    v-model="quesitonnaire.cardOnOff"
                     v-if="cardType == 'reviewManage'"
-                    :model-valule="quesitonnaire.cardOnOff"
+                    :active-value="true"
                     @change="(val)=>HandleOnOffChange(val,quesitonnaire)"
                     style="--el-switch-on-color: #2e64fa; --el-switch-off-color: #bbbbbb"
                 />
@@ -21,14 +23,14 @@
                     <span  v-if="cardType == 'questionnareManage'" class="score_text">{{ quesitonnaire.assessmentScore }}分</span>
                     <span class="creator_text">管理员</span>
                     <!-- 评价类型 -->
-                    <span v-if="cardType == 'reviewManage'">线上匿名___</span>
+                    <span v-if="cardType == 'reviewManage'">{{ typeNumToName(quesitonnaire.reviewType).title }}</span>
                     <!-- 状态文本:根据 cardStatus 动态变色 -->
                     <span :class="['status_text', statusNumToName(quesitonnaire.cardStatus)?.themeClass ]">
                         {{ statusNumToName(quesitonnaire.cardStatus)?.title  }}
                     </span>
                 </div>
                 <div class="time_row">
-                    <span>{{ quesitonnaire.createdAt }}</span>
+                    <span>{{ quesitonnaire.time }}</span>
                 </div>
             </div>
 
@@ -47,28 +49,34 @@
 
 
 <script setup lang="ts">
-
+import { ref ,watch} from 'vue'
 interface QuestionnaireItm {
     id:Number,
     assessmentTitle:String,  //标题
-    assessmentScore:Number|String,  //总分
+    assessmentScore?:Number|String,  //总分
     teacherName:String,  //创建人
-    createdAt:String,   //创建时间
+    time:String,  //卡片展示的时间
     cardStatus:Number,   //1 已使用   0 未使用
-    cardOnOff?:Boolean
+    cardOnOff?:Boolean,
+    reviewType?:Number|String,  //卡片类型
 
 }
 
 interface StatusItm {
   statusFlag:Number | String,
   title:String,
-  themeClass:'used' | 'unused' | 'ongoing' | 'end' | String
+  themeClass?:'used' | 'unused' | 'ongoing' | 'end' | String
+}
+interface TypeItm {
+  type:Number | String,
+  title:String,
 }
 
  // questionnareManage (问卷管理) | reviewManage (评价管理) |  
 type CardType = 'questionnareManage' | 'reviewManage'
 
 
+
 const props = defineProps({
     questionnaireList:{   //卡片数据
         type:Array<QuestionnaireItm>,
@@ -79,7 +87,7 @@ const props = defineProps({
         type:Array<StatusItm>
     },
     cardTypeMsg:{
-        type:Array,
+        type:Array<TypeItm>,
     },
     showSwitch:{   //展示卡片开关
         type:Boolean,
@@ -91,13 +99,13 @@ const props = defineProps({
     }
 })
 
-const emit = defineEmits('switchOnOff')
+const dynamicQuestionnaireList = ref<QuestionnaireItm[]>([])
+const emit = defineEmits(['switchOnOff'])
 // 处理卡片的开关状态
 const HandleOnOffChange = (val,row)=>{
-    console.log('当前值',val)
     emit('switchOnOff',{id:row.id,onOffVal:val })
 }
-const statusNumToName=(status:String|Number|null)=>{
+const statusNumToName=(status:String|Number|null|undefined)=>{
   let result =  props?.cardStatusMsg?.find(item=>item.statusFlag === status)
   if(result){
     return result
@@ -108,6 +116,22 @@ const statusNumToName=(status:String|Number|null)=>{
     }
   }
 }
+
+const typeNumToName=(type:String|Number|null|undefined)=>{
+  let result =  props?.cardTypeMsg?.find(item=>item.type === type)
+  if(result){
+    return result
+  }else{
+    return {
+      title:'',
+    }
+  }
+}
+
+
+watch(()=>props.questionnaireList,(newVal)=>{
+    dynamicQuestionnaireList.value = newVal
+},{deep:true,immediate:true})
 </script>
 
 
@@ -200,7 +224,8 @@ const statusNumToName=(status:String|Number|null)=>{
                         }
                     }
                     /* 未使用 (灰色) */
-                    &.unused {
+                    &.unused,
+                    &.end {
                         color: #999;
                         &::before {
                             background-color: #ccc;
@@ -208,12 +233,12 @@ const statusNumToName=(status:String|Number|null)=>{
                     }
 
                     &.ongoing {
-
+                        color: #2BC644;
+                        &::before {
+                            background-color: #2BC644;
+                        }
                     }
 
-                    &.end{
-
-                    }
                 }
             }
             .time_row {

+ 17 - 1
src/views/surveyManagement/useStatus.ts → src/components/statusDict.ts

@@ -9,4 +9,20 @@ export const useStatusConvert = [
         title:'未使用',
         themeClass: 'need_summary_icon'
     },
-]
+]
+
+
+export const cardTypeMsg = [
+    {
+        type:0,
+        title:'线上匿名'
+    },
+    {
+        type:1,
+        title:'线下答卷'
+    },
+    {
+        type:2,
+        title:'线上导入'
+    },
+]

+ 367 - 2
src/views/reviewManagement/ReviewManagement.vue

@@ -1,19 +1,384 @@
 <template>
     <div  ref="courseDom" class="main_container">
         <div  class="page_item end_item">
-           
+            <div class="page_search">
+                <div class="search_content">
+                    <div class="conent_left">
+                        <TabSearch 
+                            :tabList="viewModeTabList" 
+                            tabType="box" 
+                            :defaultTabKey="defaultViewMode" 
+                            @tabChange="HandleTabChage"
+                        />
+                        <FormSearchGroup :search-list="searchList" 
+                            @searchChange="HandleSearchChange"></FormSearchGroup>
+                    </div>
+                    <div class="conent_right">
+                        <el-button class="button_background" @click="GoToCreateQuestionnaire">创建评价</el-button>
+                    </div>
+                </div>
+            </div>
+
+            <div class="page_jg_20"></div>
+
+            <SearchBlock 
+                :searchList="statusSearchList" 
+                @searchChange="HandleSearchChange">
+            </SearchBlock>
+
+            <div class="page_jg_20"></div>
+
+            <!-- 视图展示数据 -->
+            <QuestionnaireCard 
+                v-if="defaultViewMode == 'view'"
+                :cardStatusMsg="cardStatusMsg" 
+                :cardTypeMsg="cardTypeMsg"
+                :questionnaireList="questionnaireList" 
+                cardType="reviewManage"
+                @switchOnOff="HandleCardSwitchOnOff"
+            >
+
+                <template #default="{row}">
+
+                    <el-button class="button_background">查看详情</el-button>
+                    <el-button 
+                        :class="[Number(row.useStatus) === 1 ? 'is-disabled' : 'button_border']"
+                        @click="Number(row.useStatus) === 1 ?  '' : GoToCreateQuestionnaire(row)"    
+                    >编辑</el-button>
+
+                    <el-button 
+                        :class="[Number(row.useStatus) === 1 ? 'is-disabled' : 'delete_button_border']"
+                        @click="Number(row.useStatus) === 1 ?  '' : OpenDeleteDialog(row)"
+                    >删除</el-button>
+                </template>
+            </QuestionnaireCard>
+
+            <!-- 列表展示数据 -->
+            <Table v-else
+                :currentPage="currentPage" 
+                :pageSize="pageSize" 
+                :totalNum="totalNum"
+                :tableData="questionnaireList"
+                :tableColumns="questionnaireTableColumns"
+            >
+                <template #num="{currentIndex}">
+                    {{ currentIndex + pageSize * (currentPage - 1)}}
+                </template>
+                <template #reviewType="{row}">
+                    {{ cardTypeMsg.find(item=>{return item.type === row.reviewType})?.title }}
+                </template>
+
+                <template #useStatus="{row}">
+                    <TableRowStatus :useStatus="row.useStatus" :statusMsg="useStatusConvert"></TableRowStatus>
+                </template>
+
+                <template #isOpen="{row}">
+                    <span>
+                        <el-switch
+                            v-model="row.cardOnOff"
+                            :active-value="true"
+                            @change="(val)=>HandleCardSwitchOnOff(val,row)"
+                            style="--el-switch-on-color: #2e64fa; --el-switch-off-color: #bbbbbb"
+                        />
+                    </span>
+                </template>
+
+                <template #operation="{row}">
+                    <div class="table_row_option">
+                        <span  
+                            :class="[Number(row.useStatus) === 1 ? 'editor_disable' : 'el_button_editor']"
+                            @click="Number(row.useStatus) === 1 ?  '' : GoToCreateQuestionnaire(row)"    
+                        >编辑</span>
+                        <span 
+                            :class="[Number(row.useStatus) === 1 ? 'editor_disable' : 'el_button_delete']"
+                            @click="Number(row.useStatus) === 1 ?  '' : OpenDeleteDialog(row)"
+                        >删除</span>
+                    </div>
+                </template>
+            </Table>
         </div>
+
+        <Dialog 
+            :visible="dialogVisible" 
+            :submitLoading="dialogSubmitLoading"
+            @close="HandleDialogClose"
+            @confirm="HandleDialogConfirm"
+        >
+            <span class="dialog_alert">确定删除 [ <span class="title">{{  currentOperationRow?.assessmentTitle}}</span> ] 吗</span>
+        </Dialog>
+
+
     </div>
 </template>
 
 <script lang="ts" setup>
 import {onMounted, ref} from 'vue'
+import { useRouter } from 'vue-router';
+import { ElMessage } from 'element-plus';
+
+import Table from '@/baseComponents/Table.vue';
+import TabSearch from '@/baseComponents/TabSearch.vue';
+import TableRowStatus from '@/baseComponents/TableRowStatus.vue';
+import Dialog from '@/baseComponents/Dialog.vue';
+
+import QuestionnaireCard from '@/components/questionnaireCard/QuestionnaireCard.vue';
+
+
+import {searchList, viewModeTabList, statusSearchList} from './searchListConfig'
+import { questionnaireTableColumns } from './table';
+import {useStatusConvert, cardTypeMsg} from '@/components/statusDict'
+
+import FormSearchGroup from '@/baseComponents/FormSearchGroup.vue';
+import SearchBlock from '@/baseComponents/SearchBlock.vue';
+
+
+interface QuestionnaireItm {
+    id:Number | String,
+    assessmentTitle:String,  //标题
+
+    teacherName:String,  //创建人
+    // createdAt:String,   //创建时间
+    startTime:String,   //创建时间
+    endTime:String,   //创建时间
+    useStatus:Number,   //1 已使用   0 未使用
+    cardStatus:Number,
+    cardOnOff:Boolean,
+    reviewType:Number|String,
+    
+    time:String,
+    isOpen:Boolean,
+
+}
+const router = useRouter()
+
+// 1. 视图模式切换
+const defaultViewMode = ref('list')
+const  HandleTabChage = (val)=>{    
+    defaultViewMode.value = val
+}
+
+// 查询参数
+const searchParams = ref()
+const HandleSearchChange = (params,key)=>{
+    console.log('获取到的查询参数',params)
+    searchParams.value = {
+        ...searchParams.value,
+        ...params
+    }
+}
+
+
+
+//. 问卷列表
+const currentPage = ref(1)
+const totalNum = ref(0)
+const pageSize = ref(20)
+const questionnaireList = ref<QuestionnaireItm[]>([
+    {
+        "id": '655355453521268736',
+        // "createdAt": "2026-06-29 10:28",
+        startTime:'2026-06-29 16:00',
+        endTime:'2026-06-29 16:00',
+         time:'2026-06-29 16:00 至 2026-06-29 16:00',
+
+        // "teacherId": "510448965049651259",
+        "teacherName": "李敏",
+        "assessmentTitle": "f制",
+        // "assessmentIllustrate": "fdsf",
+        // "assessmentScore": "30.00",
+        // "assessmentSortCode": 111,
+        "useStatus": 0,
+        // "questionData": null,
+        isOpen:true,
+        'cardOnOff':true,
+        cardStatus:0,
+        reviewType:0
+    },
+    {
+        "id": '6553554535212687236',
+        // "createdAt": "2026-06-29 10:28",
+
+        startTime:'2026-06-29 16:00',
+        endTime:'2026-06-29 16:00',
+        time:'2026-06-29 16:00 至 2026-06-29 16:00',
+        // "teacherId": "510448965049651259",
+        "teacherName": "李敏",
+        "assessmentTitle": "fd-复制",
+        // "assessmentIllustrate": "fdsf",
+        // "assessmentScore": "30.00",
+        // "assessmentSortCode": 111,
+        "useStatus": 1,
+        // "questionData": null,
+        isOpen:true,
+        'cardOnOff':false,
+        cardStatus:0,
+        reviewType:1
+    },
+]);
+
+// 问卷状态展示
+const cardStatusMsg = ref([
+    {
+        statusFlag:0,
+        title:'进行中',
+        themeClass:'ongoing'
+    },
+    {
+        statusFlag:1,
+        title:'已结束',
+        themeClass:'end'
+    },
+])
 
 
+// 问卷开关切换
+const HandleCardSwitchOnOff = (params)=>{
+    const {id,onOffVal} = params
+    console.log('当前值',onOffVal)
+    // 实际需要调用接口
+    questionnaireList.value = questionnaireList.value.map(item=>{
+        if(item.id == id){
+            return {
+                ...item,
+                cardOnOff:onOffVal
+            }
+        }else{
+            return item
+        }
+    })
+}
 
-onMounted(async ()=>{
+const GetQuestionnaireList = async ()=>{
+    let params = {
+        pageNum:currentPage.value,
+        pageSize: pageSize.value
+    }
+    let res = await getQuestionnaireListApi(params)
+
+    if(res.code == 200){
+        questionnaireList.value = res.data.records.map(item=>{
+            return {
+                ...item,
+                cardStatus:item.useStatus
+            }
+        })
+    }
+}
+
+
+// 2. 跳转创建问卷 /修改文件 
+const GoToCreateQuestionnaire = (row?:any)=>{
+}
+
+
+
+
+
+
+// 删除问卷
+const dialogVisible= ref(false)
+const dialogSubmitLoading = ref(false)
+const currentOperationRow = ref()
+// 打开删除弹窗
+const OpenDeleteDialog = (row)=>{
+    dialogVisible.value = true
+    currentOperationRow.value = row
+}
+// 关闭删除弹窗
+const HandleDialogClose = ()=>{
+    dialogVisible.value = false
+    currentOperationRow.value = null
+    dialogSubmitLoading.value = false
+}
+// 确认删除操作
+const HandleDialogConfirm = async ()=>{
+    dialogSubmitLoading.value = true
+    let res = undefined
+    try{
+        // res =  await deleteQuestionnaireApi(currentOperationRow.value.id)
+    }catch{}
+    if(res?.code == 200){
+        ElMessage.success(res?.msg)
+        HandleDialogClose()
+
+
+    }else{
+        dialogSubmitLoading.value = false
+    }
+}
+
+
+const GetSchoolYearList = ()=>{
+    let schoolYearOpt = [
+        {
+            label:'全部',
+            value:'all'
+        },
+        {
+            label:'2024-2025学年',
+            value:'2024-2025'
+        }
+    ]
+    searchList.value = searchList.value.map(item=>{
+        if(item.key == 'schoolYear'){
+            return {
+                ...item,
+                options:schoolYearOpt,
+                defaultValue:'all'
+            }
+        }else{
+            return item  
+        }
+    })
+
+}
+
+
+onMounted(async()=>{
+    GetSchoolYearList()
+    // await GetQuestionnaireList()
 })
+
+
+
 </script>
 
 <style lang="scss" scoped>
+
+.conent_left{
+    display: flex;
+    gap:20px;
+    align-items: center;
+
+    .simple_filter{
+        gap:20px
+    }
+}
+
+:deep(.box_tab){
+   .selected{
+        &.prefix{
+            background:  #2E64FA;
+            color: #fff;
+            border:none 
+        }
+   } 
+}
+.dialog_alert{
+    color:#666;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    .title{
+        color: #333;
+    }
+}
+
+
+
+
+
+
+
+
 </style>

+ 63 - 0
src/views/reviewManagement/searchListConfig.ts

@@ -0,0 +1,63 @@
+import {ref} from 'vue'
+
+
+import viewLightIcon from '@/assets/icon/view_white.png'
+import viewIcon from '@/assets/icon/view.png'
+import listIcon from '@/assets/icon/list.png'
+import listLightIcon from '@/assets/icon/list_white.png'
+
+
+export const searchList  = ref([
+    {
+        key:'schoolYear',
+        type:'select',
+        placeholder:'请选择',
+        defaultValue:'',
+        options:[]
+    },
+    {
+        key:'queryString',
+        type:'input',
+        placeholder:'评价名称',
+        defaultValue:'',
+    },
+])
+
+export const statusSearchList = ref([
+    {
+        label:'状态',
+        key:'status',
+        type:"single",
+        options:[
+            {
+                label:'全部',
+                value:'all'
+            },
+            {
+                label:'进行中',
+                value:'0'
+            },
+            {
+                label:'已结束',
+                value:'1'
+            },
+        ],
+        defaultValue:'all',
+    }
+])
+
+
+export const viewModeTabList = ref([
+    {
+        label:'视图',
+        key:'view',
+        prefix:viewIcon,
+        prefixLight:viewLightIcon,
+    },
+    {
+        label:'列表',
+        key:'list',
+        prefix:listIcon,
+        prefixLight:listLightIcon,
+    }
+])

+ 96 - 0
src/views/reviewManagement/table.ts

@@ -0,0 +1,96 @@
+import { ref } from "vue";
+export const questionnaireTableColumns = ref([
+    // 1. ID (隐藏列)
+    {
+        name: 'id',
+        label: 'id',
+        width: '50',
+        fixed: '',
+        align: 'center',
+        custom: false,
+        hidden: true,
+    },
+    {
+        name: 'num',
+        label: '序号',
+        width: '60', 
+        fixed: '',
+        align: 'center',
+        custom: true,
+        hidden: false,
+    },
+    {
+        name: 'assessmentTitle',
+        label: '问卷名称',
+        width: '250',
+        fixed: '',
+        align: 'left',
+        custom: false,
+        hidden: false,
+    },
+    {
+        name: 'reviewType',
+        label: '评价方式',
+        width: '100',
+        fixed: '',
+        align: 'center',
+        custom: true,
+        hidden: false,
+    },
+    {
+        name: 'teacherName',
+        label: '创建人',
+        width: '100',
+        fixed: '',
+        align: 'center',
+        custom: false,
+        hidden: false,
+    },
+    {
+        name: 'useStatus',
+        label: '状态',
+        width: '120',
+        fixed: '',
+        align: 'center',
+        custom: true,
+        hidden: false,
+    },
+
+    {
+        name: 'isOpen',
+        label: '对学生开放',
+        width: '120',
+        fixed: '',
+        align: 'center',
+        custom: true,
+        hidden: false,
+    },
+
+    {
+        name: 'startTime',
+        label: '开始时间',
+        width: '180',
+        fixed: '',
+        align: 'center',
+        custom: false,
+        hidden: false,
+    },
+    {
+        name: 'endTime',
+        label: '结束时间',
+        width: '180',
+        fixed: '',
+        align: 'center',
+        custom: false,
+        hidden: false,
+    },
+    {
+        name: 'operation',
+        label: '操作',
+        width: '200',
+        fixed: 'right', 
+        align: 'center',
+        custom: true,
+        hidden: false,
+    }
+])

+ 7 - 2
src/views/surveyManagement/SurveyManagement.vue

@@ -101,7 +101,10 @@ import { getQuestionnaireListApi , copyQuestionnaireApi,deleteQuestionnaireApi}
 
 import { viewModeTabList } from './tabSearch';  //视图项配置
 import { questionnaireTableColumns } from './table';
-import {useStatusConvert} from './useStatus'
+import {useStatusConvert} from '@/components/statusDict'
+
+
+
 import { ElMessage } from 'element-plus';
 import Dialog from '@/baseComponents/Dialog.vue';
 interface QuestionnaireItm {
@@ -112,6 +115,7 @@ interface QuestionnaireItm {
     createdAt:String,   //创建时间
     useStatus:Number,   //1 已使用   0 未使用
     cardStatus:Number,
+    time:String  //卡片 展示的时间
 }
 const router = useRouter()
 
@@ -154,7 +158,8 @@ const GetQuestionnaireList = async ()=>{
         questionnaireList.value = res.data.records.map(itm=>{
             return {
                 ...itm,
-                cardStatus:itm.useStatus
+                cardStatus:itm.useStatus,
+                time:itm.createdAt
             }
         })
     }