Переглянути джерело

问卷管理接口联调完成(报告暂无),账号信息部分修改等

htc 15 годин тому
батько
коміт
9218d006ce

+ 62 - 0
src/api/agent/index.js

@@ -7,6 +7,61 @@ export function getContractList(query) {
   })
 }
 
+// ---start 问卷管理
+export function getQuestionnaireList(query) {
+  return request({
+    url: '/core/questionnaire/page',
+    method: 'get',
+    params: query
+  })
+}
+export function addQuestionnaire(data) {
+  return request({
+    url: '/core/questionnaire',
+    method: 'post',
+    data
+  })
+}
+export function updateQuestionnaire(data) {
+  return request({
+    url: '/core/questionnaire',
+    method: 'put',
+    data
+  })
+}
+export function getQuestionnaireInfo(query) {
+  return request({
+    url: '/core/questionnaire/'+query,
+    method: 'get'
+  })
+}
+export function deleteQuestionnaire(id) {
+  return request({
+    url: `/core/questionnaire/${id}`,
+    method: 'delete'
+  })
+}
+export function getQuestionnaireQA(id) {
+  return request({
+    url: `/core/questionnaire/${id}`,
+    method: 'get'
+  })
+}
+export function getTeamQuestionnaireList(query) {
+  return request({
+    url: '/core/team/questionnaire/page',
+    method: 'get',
+    params: query
+  })
+}
+export function getQuestionnaireSchedule(teamQuestionnaireId) {
+  return request({
+    url: `/core/teammember/que/listByTeam/${teamQuestionnaireId}`,
+    method: 'get'
+  })
+}
+// ---end 问卷管理
+
 // ---start 项目管理
 export function getCoachProgramList(query) {
   return request({
@@ -70,6 +125,13 @@ export function deleteProgramTeam(query) {
     method: 'delete'
   })
 }
+export function sendQuestionnaire(data) {
+  return request({
+    url: '/core/team/questionnaire',
+    method: 'post',
+    data
+  })
+}
 // ---end 团队管理
 
 // ---start 教练管理

+ 6 - 2
src/assets/scss/common.scss

@@ -412,9 +412,13 @@ img {
     position: relative;
     z-index: 1;
     width: 100%;
-    height: 100%;
-    overflow: hidden;
+    // height: 100%;
+    height: calc(100vh - 260px);
+    // overflow: hidden;
+    overflow-y: auto;
     transition: width .3s;
+    box-sizing: border-box;
+    overflow-x: inherit;
   }
 
   &__menu {

+ 17 - 1
src/store_v3/modules/agent.js

@@ -1,7 +1,7 @@
 import {defineStore} from "pinia";
 import {ref} from "vue";
 
-import { getCoachList } from '@/api/agent/index.js'
+import { getCoachList,getQuestionnaireList } from '@/api/agent/index.js'
 
 export const useAgentStore = defineStore('agent', () => {
     const coachList = ref([]);
@@ -17,9 +17,25 @@ export const useAgentStore = defineStore('agent', () => {
         });
     };
 
+    const questionnaireList = ref([]);
+    const questionnaireMap = ref(new Map());
+    const getQuestionnaireData = () => {
+        getQuestionnaireList({page:1,limit:-1}).then((res) => {
+            questionnaireList.value = res.data.list;
+            const map = new Map();
+            questionnaireList.value.forEach((supplier) => {
+                map.set(supplier.id, supplier);
+            });
+            questionnaireMap.value = map;
+        });
+    };
+
     return {
         coachList,
         coachMap,
         getCoachData,
+        questionnaireList,
+        questionnaireMap,
+        getQuestionnaireData,
     };
 })

+ 3 - 3
src/views/main-sidebar.vue

@@ -29,8 +29,9 @@
       <div class="left" @click.self="handleUser">
         <img src="@/assets/images/agent/avatar.png">
         <div class="name">
-          <span>{{ userName }}</span>
-          <span class="type">{{ userType }}</span>
+          <span>{{ $store.state.user.realName }}</span>
+          <span class="type" v-if="$store.state.user.superAdmin===1">{{ 管理员 }}</span>
+          <span class="type" v-else>{{ $store.state.user.qualificationType }}</span>
         </div>
       </div>
     </div>
@@ -44,7 +45,6 @@ export default {
     return {
       activeMenuId:'',
       erjiMenu:[],
-      userName:'威廉教练',
       userType:'专业教练',
     }
   },

+ 2 - 0
src/views/main.vue

@@ -161,6 +161,8 @@ export default {
           }
           this.$store.state.user.id = res.data.id;
           this.$store.state.user.name = res.data.username;
+          this.$store.state.user.realName = res.data.realName;
+          this.$store.state.user.qualificationType = res.data.qualificationType;
           this.$store.state.user.superAdmin = res.data.superAdmin;
           this.$store.state.user.roleCodes = res.data.roleCodes;
           this.$store.state.user.enterpriseId=res.data.enterpriseId;

+ 51 - 1
src/views/modules/agent/knowledge.vue

@@ -7,7 +7,8 @@
                     <el-input v-model="queryParams.name" placeholder="请输入知识库名称查询"></el-input>
                     <img src="@/assets/images/agent/query_mini.png">
                 </div>
-                <div class="r_add" @click="handleAdd">+ 创建知识库</div>
+                <!-- <div class="r_add" @click="handleAdd">+ 创建知识库</div> -->
+                <div class="r_add" @click="show=true">+ 创建知识库</div>
             </div>
         </div>
         <template v-if="list.length">
@@ -42,6 +43,18 @@
                 <p>暂无知识库</p>
             </div>
         </template>
+        <el-dialog width="40%" :visible.sync="show" title="创建空知识库" @close="cancel">
+            <div class="zsk_tip">空知识库中还没有文档,你可以在今后任何时候上传文档至该知识库</div>
+            <el-form ref="zskRef" :model="form" :rules="rules" label-width="100px" style="width: 90%;margin: 0 auto;" label-position="top">
+                <el-form-item label="知识库名称" prop="baseName">
+                    <el-input v-model="form.baseName" placeholder="请输入备注"></el-input>
+                </el-form-item>
+            </el-form>
+            <div class="demo-drawer__footer" style="display: flex;justify-content: end;">
+                <el-button :loading="buttonLoading" type="primary" @click="submitForm">创建</el-button>
+                <el-button @click="cancel" style="margin-right: 5%;">取 消</el-button>
+            </div>
+        </el-dialog>
     </div>
 </template>
 
@@ -52,6 +65,7 @@
     const queryParams = ref({
         name: ''
     })
+    const show = ref(false)
     const list = ref([
         {
             name: '知识库1',
@@ -60,6 +74,33 @@
             time: '2023-04-01 09:21'
         }
     ])
+    const form = ref({
+        id:'',
+        baseName: ''
+    })
+    const rules = ref({
+        baseName: [
+            { required: true, message: '请输入知识库名称', trigger: 'blur' }
+        ]
+    })
+    const buttonLoading = ref(false)
+    const zskRef = ref(null)
+
+    const submitForm = () => {
+        proxy.$refs.zskRef.validate((valid) => {
+            if (valid) {
+                buttonLoading.value = true
+                
+            } else {
+                return false;
+            }
+        });
+    }
+    const cancel = () => {
+        show.value = false
+        proxy.$refs.zskRef.resetFields()
+    }
+
 
     const handleAdd = () => {
         proxy.$router.push('agentKnowledgeAdd')
@@ -209,5 +250,14 @@
                 margin-top: 16px;
             }
         }
+
+        .zsk_tip{
+            font-family: PingFangSC, PingFang SC;
+            font-weight: 400;
+            font-size: 5rpx;
+            color: #6B7280;
+            line-height: 8rpx;
+            margin: -30px 0 20px;
+        }
     }
 </style>

+ 1 - 1
src/views/modules/agent/program/addTeam.vue

@@ -468,7 +468,7 @@
     }
 
     const handleDealDate = (e,index) => {
-        programForm.value.team.users[index].birthdate = proxy.parseTime(new Date(e), '{yy}-{mm}-{dd}');;
+        programForm.value.team.users[index].birthdate = proxy.parseTime(new Date(e), '{yy}-{mm}-{dd}');
     }
 
     onMounted(() => {

+ 43 - 19
src/views/modules/agent/questionnaire.vue

@@ -6,44 +6,48 @@
                 <p class="tip">创建和管理PERILL评估问卷</p>
             </div>
             <div class="t_r">
-                <el-button type="primary" icon="el-icon-plus">发送问卷</el-button>
+                <!-- <el-button type="primary" icon="el-icon-plus">发送问卷</el-button> -->
             </div>
         </div>
         <div class="query adfacjb">
-            <el-input placeholder="搜索问卷" prefix-icon="el-icon-search" v-model="queryParams.name" style="width: calc(100% - 448px);"></el-input>
-            <el-select v-model="queryParams.status" placeholder="全部状态" style="width: 200px;"></el-select>
-            <el-select v-model="queryParams.sort" placeholder="排序方式" style="width: 200px;"></el-select>
+            <el-select v-model="queryParams.questionnaireId" placeholder="问卷" style="width: calc(100% - 324px);" @change="getList" clearable>
+                <el-option v-for="item in useAgentStore().questionnaireList" :key="item.id" :label="item.title" :value="item.id"/>
+            </el-select>
+            <el-select v-model="queryParams.status" placeholder="全部状态" style="width: 300px;" @change="getList" clearable>
+                <el-option label="进行中" :value="0"></el-option>
+                <el-option label="已完成" :value="1"></el-option>
+            </el-select>
         </div>
         <div class="list">
             <div class="l_item" v-for="(item, index) in dataList" :key="index">
                 <div class="li_top adfacjb">
-                    <div class="lt_l">项目名称:{{ item.programName }}</div>
+                    <div class="lt_l">项目名称:{{ item.enterpriseName }}</div>
                     <div class="lt_r adfac">
-                        <div class="lr_pre adfac" @click="handleSchedule(item)">
+                        <div class="lr_pre adfac" v-hasPermi="['core:questionnaire:info']" @click="handleSchedule(item)">
                             <img src="@/assets/images/agent/send_mini.png">
                             <span>查看进度</span>
                         </div>
-                        <div class="lr_pre adfac" @click="handleReport(item)">
+                        <!-- <div class="lr_pre adfac" @click="handleReport(item)">
                             <img src="@/assets/images/agent/report_mini.png">
                             <span>查看报告</span>
-                        </div>
+                        </div> -->
                         <div class="lr_status" :class="{'jxz':item.status===0,'ywc':item.status===1}">{{ item.status===0?'进行中':(item.status===1?'已完成':'未知') }}</div>
                     </div>
                 </div>
                 <div class="li_team">团队名称:{{ item.teamName }}</div>
-                <div class="li_wj">问卷名称:{{ item.questionnaireName }}</div>
+                <div class="li_wj">问卷名称:{{ item.title }}</div>
                 <div class="li_time_person adf">
-                    <div class="ltp">发布时间:{{ item.fbTime }}</div>
-                    <div class="ltp">截止时间:{{ item.jzTime }}</div>
-                    <div class="ltp">团队成员:<span>{{ item.teamWcPerson||0 }}/{{item.teamPersons||0}}</span>完成</div>
-                    <div class="ltp">发送人:{{ item.sendPerson }}</div>
+                    <div class="ltp">发布时间:{{ item.startTime }}</div>
+                    <div class="ltp">截止时间:{{ item.endTime }}</div>
+                    <div class="ltp">团队成员:<span>{{ item.finishNum||0 }}/{{item.userNum||0}}</span>完成</div>
+                    <div class="ltp">发送人:{{ item.creatorName }}</div>
                 </div>
                 <div class="li_jd">
-                    <div class="lj" :style="{'width':(item.teamWcPerson/item.teamPersons*100)+'%'}"></div>
+                    <div class="lj" :style="{'width':(item.finishNum/item.userNum*100)+'%'}"></div>
                 </div>
                 <div class="li_text adfacjb">
                     <span>完成情况</span>
-                    <span>{{ ((item.teamWcPerson/item.teamPersons)*100).toFixed(0) }}%</span>
+                    <span>{{ ((item.finishNum/item.userNum)*100).toFixed(0) }}%</span>
                 </div>
             </div>
         </div>
@@ -51,13 +55,15 @@
 </template>
 
 <script setup name="">
-    import { ref, getCurrentInstance } from 'vue'
+    import { ref, getCurrentInstance, onMounted } from 'vue'
+    import {useAgentStore} from "@/store_v3/modules/agent";
+    import { getTeamQuestionnaireList } from "@/api/agent/index.js";
     const { proxy } = getCurrentInstance();
+    useAgentStore().getQuestionnaireData();
     
     const queryParams = ref({
-        name: '',
+        questionnaireId: '',
         status: '',
-        sort: ''
     })
     const dataList = ref([
         {
@@ -88,10 +94,28 @@
         proxy.$router.push({
             path: '/agentQuestionnaireSchedule',
             query:{
-                questionnaireName: item.questionnaireName
+                id: item.id,
+                questionnaireName: item.questionnaireName,
+                questionnaireName: item.title,
+                enterpriseName: item.enterpriseName,
+                startTime: item.startTime,
+                endTime: item.endTime,
+                userNum: item.userNum,
+                finishNum: item.finishNum
             }
         })
     }
+
+    const getList = () => {
+        getTeamQuestionnaireList(queryParams.value).then(res=>{
+            if(res.code!==0) return proxy.$message.error(res.msg)
+            dataList.value = res.data.list;
+        })
+    }
+
+    onMounted(()=>{
+        getList()
+    })
 </script>
 
 <style scoped lang="scss">

+ 45 - 9
src/views/modules/agent/questionnaire/detail.vue

@@ -16,14 +16,16 @@
                     </div>
                 </div>
                 <div class="ct_r">
-                    <div class="cr_title">{{ '企业员工培训需求调查问卷' }}</div>
-                    <div class="cr_type">问卷类型:{{ '高级' }}</div>
+                    <div class="cr_title">{{ title }}</div>
+                    <div class="cr_type">问卷类型:{{ type }}</div>
                     <div class="cr_list">
                         <div class="crl_item" v-for="(item, index) in dataList" :key="index">
-                            <div class="ci_title"><span>*</span>{{ (index+1)+'.'+item.question }}</div>
-                            <div class="ci_answer adfac" v-for="(answer, idx) in item.answerList" :key="idx">
-                                <img src="@/assets/images/agent/notselect_mini.png">
-                                <span>{{ answer.name }}</span>
+                            <div class="ci_title adfac"><span>*</span>{{ (index+1)+'.'+item.question }}</div>
+                            <div class="ci_answer adfac" v-for="(answer, idx) in item.questionOption" :key="idx">
+                                <template v-if="item.questionType==='0'">
+                                    <img src="@/assets/images/agent/notselect_mini.png">
+                                    <span>{{ answer.questionOption }}</span>
+                                </template>
                                 <!-- <el-radio v-model="item.answer" :label="answer.value" style="display: block;margin-top: 26px;">{{ answer.name }}</el-radio> -->
                                 <!-- <div class="ca_czs adfac" style="display: none;">
                                     <img src="@/assets/images/agent/edit_mini2.png">
@@ -38,7 +40,7 @@
             <div class="c_bottom adfacjc">
                 <div class="cb_l"></div>
                 <div class="cb_r">
-                    <el-button type="primary">发送问卷</el-button>
+                    <el-button type="primary" @click="sendWj">发送问卷</el-button>
                     <el-button type="default" @click="handleBack">返回</el-button>
                 </div>
             </div>
@@ -47,10 +49,16 @@
 </template>
 
 <script setup name="">
-    import { ref, getCurrentInstance } from 'vue'
+    import { ref, getCurrentInstance,onMounted } from 'vue'
     const { proxy } = getCurrentInstance();
+    import { getQuestionnaireQA } from '@/api/agent/index.js'
 
-    const questionList = ref([1,1,1,1,1,1,1,1,1])
+    const typecfg = {
+        1: '初级',
+        2: '中级',
+        3: '高级'
+    }
+    const questionList = ref([])
     const dataList = ref([
         {
             question:'您所在的部门是哪个?',
@@ -86,10 +94,34 @@
             ]
         }
     ])
+    const questionnaireId = ref('')
+    const title = ref('')
+    const type = ref('')
+
+    const sendWj = () =>{
+        proxy.$router.push({path:'/agentQuestionnairePublish',query:{id:questionnaireId.value,title:title.value,type:typecfg[type.value]}});
+    }
     
     const handleBack = () => {
         proxy.$router.go(-1)
     }
+
+    const getQuestionnaireQADetail = () => {
+        getQuestionnaireQA(questionnaireId.value).then(res=>{
+            if(res.code !== 0) return proxy.$message.error(res.msg)
+            questionList.value = res.data.detailList.map(d=>{
+                return {title: d.question, id:d.questionnaireId}
+            })
+            dataList.value = res.data.detailList;
+        })
+    }
+
+    onMounted(()=>{
+        questionnaireId.value = proxy.$route.query.id;
+        title.value = proxy.$route.query.title;
+        type.value = proxy.$route.query.type;
+        getQuestionnaireQADetail()
+    })
 </script>
 
 <style scoped lang="scss">
@@ -164,6 +196,10 @@
                             color: #6B7280;
                             line-height: 48px;
                             cursor: pointer;
+                            display: block;
+                            overflow: hidden;
+                            white-space: nowrap;
+                            text-overflow: ellipsis;
                             &:first-child{
                                 margin-top: 0;
                             }

+ 75 - 25
src/views/modules/agent/questionnaire/publish.vue

@@ -9,8 +9,8 @@
         </div>
         <div class="content">
             <div class="c_top adfacjb">
-                <span>企业员工培训需求调查问卷</span>
-                <el-button type="primary">预览问卷</el-button>
+                <span>{{ title }}</span>
+                <el-button type="primary" @click="reviewWj">预览问卷</el-button>
             </div>
             <div class="c_set">
                 <div class="cs_title" style="margin-top: 0;">选择项目成员</div>
@@ -29,18 +29,18 @@
                 <div class="cs_title">问卷回答设置</div>
                 <div class="cs_zd adfac">
                     <span>只允许作答</span>
-                    <el-input-number v-model="params.times" :min="1" controls-position="right" style="margin: 0 9px;"></el-input-number>
+                    <el-input-number v-model="params.answerSetting" :min="1" controls-position="right" style="margin: 0 9px;"></el-input-number>
                     <span>次</span>
                 </div>
                 <div class="cs_title">问卷作答时间设置</div>
                 <div class="cs_time">
                     <div class="ct_pre adfac">
                         <span>开始时间</span>
-                        <el-date-picker v-model="params.startTime" type="datetime" placeholder="选择开始时间" style="width:311px;"></el-date-picker>
+                        <el-date-picker v-model="params.startTime" type="datetime" placeholder="选择开始时间" style="width:311px;" @change="e=>handleChangeDatetime(e,'startTime')"></el-date-picker>
                     </div>
                     <div class="ct_pre adfac">
                         <span>截止时间</span>
-                        <el-date-picker v-model="params.endTime" type="datetime" placeholder="选择截止时间" style="width:311px;"></el-date-picker>
+                        <el-date-picker v-model="params.endTime" type="datetime" placeholder="选择截止时间" style="width:311px;" @change="e=>handleChangeDatetime(e,'endTime')"></el-date-picker>
                     </div>
                 </div>
                 <div class="cs_btn adfac">
@@ -56,7 +56,7 @@
                     <template v-if="!checkShow">
                         <div class="ecl_gs">
                             <div class="eg_title">按项目选</div>
-                            <div class="egt_item adfacjb" v-for="(item,index) in companyList" :key="index" @click="handleSelectCompany(item.name,item.id)">
+                            <div class="egt_item adfacjb" v-for="(item,index) in companyList" :key="index">
                                 <div class="ei_l adfac">
                                     <span>{{ item.name }}</span>
                                     <span>{{ item.num }}人</span>
@@ -97,33 +97,33 @@
 </template>
 
 <script setup name="">
-    import { ref, getCurrentInstance } from 'vue'
+    import { ref, getCurrentInstance,onMounted } from 'vue'
     const { proxy } = getCurrentInstance();
+    import { getCoachProgramList,getCoachList,sendQuestionnaire } from '@/api/agent/index.js'
     
+    const questionnaireId = ref('')
+    const title = ref('')
+    const type = ref('')
     const params = ref({
-        persons:[{name:'张三'},{name:'李四'}],
-        times:1,
+        questionnaireId:'',
+        teamId:'',
+        userIds:[],
+        persons:[],
+        answerSetting:1,
         startTime:'',
         endTime:''
     })
+    const typecfg = {
+        1: '初级',
+        2: '中级',
+        3: '高级'
+    }
     const userShow = ref(false)
     const checkShow = ref(false)
     const companyName = ref('')
     const userName = ref('')
-    const companyList = ref([
-        {id:1,name:'合肥市传秀科技有限公司',num:5},
-        {id:2,name:'合肥市恰恰科技有限公司',num:20},
-        {id:3,name:'上海明国实业有限公司',num:17},
-        {id:4,name:'上特斯拉上海有限公司',num:37}
-    ])
-
-    const userList = ref([
-        {id:1,name:'张三',checked:false},
-        {id:2,name:'李四',checked:false},
-        {id:3,name:'王五',checked:false},
-        {id:4,name:'赵六',checked:false},
-        {id:5,name:'田七',checked:false}
-    ])
+    const companyList = ref([])
+    const userList = ref([])
     const selectedUsers = ref([])
     const checkAll = ref(false)
     const isIndeterminate = ref(false)
@@ -131,6 +131,17 @@
 
     const handleAddUser = () => {
         userShow.value = true
+        getCoachProgramList({page:1,limit:-1}).then(res=>{
+            if(res.code !==0) return proxy.$message({type:'warning',message:res.msg});
+            companyList.value = res.data.map(l=>{
+                return {
+                    id:l.id,
+                    name:l.enterpriseName,
+                    num:l.teams.reduce((a,b)=>a+b.teamNum,0),
+                    teamId:l.teams[0].teamId
+                }
+            });
+        })
     }
 
     const handleBack = () => {
@@ -138,15 +149,29 @@
     }
 
     const handleRemoveUser = (item,index) => {
-        params.value.persons.splice(index,1)
+        params.value.persons.splice(index,1);
+        params.value.userIds = params.value.persons.map(l=>l.id);
     }
 
     const handleConfirm = () => {
-        if(params.value.persons.length === 0) return proxy.$message({type:'warning',message:'请选择项目成员'});
+        if(params.value.userIds.length === 0) return proxy.$message({type:'warning',message:'请选择项目成员'});
         if(!params.value.startTime || !params.value.endTime) return proxy.$message({type:'warning',message:'请设置问卷作答时间'});
+        sendQuestionnaire(params.value).then(res=>{
+            if(res.code!==0) return proxy.$message({type:'warning',message:res.msg});
+            proxy.$message({type:'success',message:'问卷已发送,请等待成员作答!'});
+            setTimeout(()=>{proxy.$router.go(-1)},1500)
+        })
     }
 
     const handleChooseUser = (item) => {
+        params.value.teamId = item.teamId;
+        getCoachList({teamId:item.teamId,page:1,limit:-1}).then(res=>{
+            if(res.code!==0) return proxy.$message({type:'warning',message:res.msg});
+            userList.value = res.data.list.map(l=>{
+                return {id:l.id,name:l.realName,checked:false}
+            })
+        })
+
         checkShow.value = true
         companyName.value = item.name
     }
@@ -155,7 +180,20 @@
         companyName.value = ''
     }
 
+    const handleChangeDatetime = (e,key) => {
+        params.value[key] = proxy.parseTime(new Date(e), '{yy}-{mm}-{dd} {hh}:{ii}:{ss}');
+    }
+
+
     const submitForm = () => {
+        if(selectedUsers.value.length===0) return proxy.$message.warning('请至少选择一位成员')
+        params.value.persons = JSON.parse(JSON.stringify(selectedUsers.value));
+        params.value.userIds = params.value.persons.map(l=>l.id);
+        checkAll.value = false;
+        isIndeterminate.value = false;
+        selectedUsers.value = [];
+        userList.value = [];
+        checkShow.value = false;
         userShow.value = false;
     }
     const cancel = () => {
@@ -187,6 +225,18 @@
         if(trues===userList.value.length) checkAll.value = true;
         else checkAll.value = false;
     }
+    
+    const reviewWj = () => {
+        proxy.$router.push({path:'/agentQuestionnaireDetail',query:{id:questionnaireId.value,title:title.value,type:typecfg[type.value]}});
+    }
+
+    
+    onMounted(()=>{
+        questionnaireId.value = proxy.$route.query.id;
+        params.value.questionnaireId = proxy.$route.query.id;
+        title.value = proxy.$route.query.title;
+        type.value = proxy.$route.query.type;
+    })
 </script>
 
 <style scoped lang="scss">

+ 82 - 24
src/views/modules/agent/questionnaire/schedule.vue

@@ -2,15 +2,15 @@
     <div class="page">
         <div class="top adfac">
             <img src="@/assets/images/agent/arrow_left.png" @click="handleBack">
-            <span>{{ questionnaireName }}</span>
+            <span>{{ questionnaireInfo.questionnaireName }}</span>
         </div>
         <div class="content">
             <div class="c_top adfacjb">
                 <div class="ct_l adfac">
                     <div class="cl_title">问卷作答进度</div>
-                    <div class="cl_pre">发布时间:<span>{{ '2025-05-02 14:00:00' }}</span></div>
-                    <div class="cl_pre">截止时间:<span>{{ '2025-05-02 14:00:00' }}</span></div>
-                    <div class="cl_pre">项目名称:<span>{{ 'TechCorp 领导团队评估' }}</span></div>
+                    <div class="cl_pre">发布时间:<span>{{ questionnaireInfo.startTime }}</span></div>
+                    <div class="cl_pre">截止时间:<span>{{ questionnaireInfo.endTime }}</span></div>
+                    <div class="cl_pre">项目名称:<span>{{ questionnaireInfo.enterpriseName }}</span></div>
                 </div>
                 <div class="ct_r adfac">
                     <template v-if="multipleSelection.length">
@@ -21,30 +21,36 @@
                 </div>
             </div>
             <div class="c_nums adfac">
-                <div class="cn_pre">团队人数:<span>{{ 30 }}</span></div>
-                <div class="cn_pre">已作答:<span>{{ 27 }}</span> 人</div>
-                <div class="cn_pre">未作答:<span style="color: #E22D48;">{{ 3 }}</span> 人</div>
+                <div class="cn_pre">团队人数:<span>{{ questionnaireInfo.userNum }}</span></div>
+                <div class="cn_pre">已作答:<span>{{ questionnaireInfo.finishNum }}</span> 人</div>
+                <div class="cn_pre">未作答:<span style="color: #E22D48;">{{ questionnaireInfo.userNum-questionnaireInfo.finishNum }}</span> 人</div>
             </div>
-            <el-table :data="userList" border cell-class-name="vertical-top-cell" v-loading="loading" empty-text="暂无人员" max-height="672px" style="margin-top: 16px;" @selection-change="handleSelectionChange">
+            <el-table ref="multipleTable" :data="userList" border cell-class-name="vertical-top-cell" v-loading="loading" empty-text="暂无人员" max-height="672px" style="margin-top: 16px;" @selection-change="handleSelectionChange">
                 <el-table-column type="selection" width="55"></el-table-column>
-                <el-table-column label="姓名" prop="aaa"></el-table-column>
-                <el-table-column label="性别" prop="bbb">
-                    <template #default="{ row }"></template>
+                <el-table-column label="姓名" prop="realName"></el-table-column>
+                <el-table-column label="性别" prop="gender">
+                    <template #default="{ row }">{{ genderCfg[row.gender]||'' }}</template>
                 </el-table-column>
-                <el-table-column label="手机号码" prop="aaa"></el-table-column>
-                <el-table-column label="所属部门" prop="aaa"></el-table-column>
-                <el-table-column label="职位" prop="aaa"></el-table-column>
-                <el-table-column label="用户标签" prop="aaa"></el-table-column>
-                <el-table-column label="发送人" prop="aaa"></el-table-column>
-                <el-table-column label="所属上级" prop="aaa"></el-table-column>
+                <el-table-column label="手机号码" prop="mobile"></el-table-column>
+                <el-table-column label="所属部门" prop="dept"></el-table-column>
+                <el-table-column label="职位" prop="post"></el-table-column>
+                <el-table-column label="级别" prop="level"></el-table-column>
+                <!-- <el-table-column label="用户标签" prop="aaa"></el-table-column> -->
+                <!-- <el-table-column label="发送人" prop="aaa"></el-table-column> -->
+                <!-- <el-table-column label="所属上级" prop="aaa"></el-table-column> -->
                 <el-table-column label="状态" prop="status">
-                    <template #default="{ row }"></template>
+                    <template #default="{ row }">
+                        <div class="q_status adfac" :class="{'wzd':row.status==='0','yzd':row.status==='1'}">
+                            <div class="qs_l"></div>
+                            <div class="qs_r">{{ row.status==='0'?'未作答':row.status==='1'?'已作答':'' }}</div>
+                        </div>
+                    </template>
                 </el-table-column>
-                <el-table-column label="提交时间" prop="aaa"></el-table-column>
+                <el-table-column label="提交时间" prop="submitTime"></el-table-column>
                 <el-table-column label="操作" width="150">
                     <template #default="scope">
-                        <el-button link type="text" size="mini" @click="handleReport(scope.row)" v-if="scope.row.status===1">查看报告</el-button>
-                        <el-button link type="text" size="mini" @click="handleRemind(scope.row)" v-if="scope.row.status===0">提醒作答</el-button>
+                        <el-button link type="text" size="mini" @click="handleReport(scope.row)" v-if="scope.row.status==='1'">查看报告</el-button>
+                        <el-button link type="text" size="mini" @click="handleRemind(scope.row)" v-if="scope.row.status==='0'" v-hasPermi="['core:questionnaire:remind']">提醒作答</el-button>
                     </template>
                 </el-table-column>
             </el-table>
@@ -53,10 +59,12 @@
 </template>
 
 <script setup name="">
-    import { ref, getCurrentInstance } from 'vue'
+    import { ref, getCurrentInstance, onMounted, nextTick } from 'vue'
     const { proxy } = getCurrentInstance();
+    import { getQuestionnaireSchedule } from '@/api/agent/index.js'
     
-    const questionnaireName = proxy.$route.query.questionnaireName;
+    const genderCfg = { 0: '男', 1: '女', 2: '保密' }
+    const questionnaireInfo = ref({})
     const multipleSelection = ref([])
     const userList = ref([{status:1},{status:0}])
     const loading = ref(false)
@@ -67,7 +75,8 @@
         multipleSelection.value = val;
     }
     const handleCancel = () => {
-        multipleSelection.value = []
+        multipleSelection.value = [];
+        proxy.$refs.multipleTable.clearSelection();
     }
     const handleReminds = () => {
         if(!multipleSelection.value.length) return proxy.$message({ type: 'warning', message: '请选择需要提醒的人员' })
@@ -94,6 +103,23 @@
             
         })
     }
+
+    const getList = () => {
+        loading.value = true;
+        getQuestionnaireSchedule(questionnaireInfo.value.id).then(res => {
+            if(res.code!==0) return proxy.$message({ type: 'error', message: res.msg })
+            userList.value = res.data;
+        }).finally(()=>{
+            loading.value = false
+        })
+    }
+
+    onMounted(()=>{
+        questionnaireInfo.value = proxy.$route.query;
+        nextTick(()=>{
+            getList()
+        })
+    })
 </script>
 
 <style scoped lang="scss">
@@ -200,5 +226,37 @@
                 }
             }
         }
+
+        .q_status{
+            .qs_l{
+                width: 10px;
+                height: 10px;
+                border-radius: 50%;
+            }
+            .qs_r{
+                font-family: PingFangSC, PingFang SC;
+                font-weight: 400;
+                font-size: 14px;
+                line-height: 16px;
+                margin-left: 12px;
+            }
+
+            &.wzd{
+                .qs_l{
+                    background: #E22D48;
+                }
+                .qs_r{
+                    color: #E22D48;
+                }
+            }
+            &.yzd{
+                .qs_l{
+                    background: #009191;
+                }
+                .qs_r{
+                    color: #009191;
+                }
+            }
+        }
     }
 </style>

+ 142 - 30
src/views/modules/agent/questionnaireList.vue

@@ -6,13 +6,16 @@
                 <p class="tip">创建和管理PERILL评估问卷</p>
             </div>
             <div class="t_r">
-                <el-button type="primary" icon="el-icon-plus">新增问卷</el-button>
+                <el-button type="primary" icon="el-icon-plus" @click="show=true" v-hasPermi="['core:questionnaire:create']">新增问卷</el-button>
             </div>
         </div>
         <div class="query adfacjb">
-            <el-input placeholder="搜索问卷" prefix-icon="el-icon-search" v-model="queryParams.name" style="width: calc(100% - 448px);"></el-input>
-            <el-select v-model="queryParams.type" placeholder="全部类型" style="width: 200px;"></el-select>
-            <el-select v-model="queryParams.sort" placeholder="排序方式" style="width: 200px;"></el-select>
+            <el-input placeholder="搜索问卷" prefix-icon="el-icon-search" v-model="queryParams.title" style="width: calc(100% - 424px);" @keyup.enter.native="getList"></el-input>
+            <el-select v-model="queryParams.type" placeholder="全部类型" style="width: 400px;" @change="getList">
+                <el-option label="初级" :value="1"></el-option>
+                <el-option label="中级" :value="2"></el-option>
+                <el-option label="高级" :value="3"></el-option>
+            </el-select>
         </div>
         <div class="list">
             <el-table :data="dataList" border cell-class-name="vertical-top-cell" v-loading="loading" empty-text="暂无问卷" max-height="578px">
@@ -21,15 +24,19 @@
                         {{ scope.$index + 1 }}
                     </template>
                 </el-table-column>
-                <el-table-column label="问卷标题" prop="aaa"></el-table-column>
-                <el-table-column label="创建人" prop="aaa"></el-table-column>
-                <el-table-column label="创建时间" prop="aaa"></el-table-column>
-                <el-table-column label="问卷类型" prop="aaa"></el-table-column>
+                <el-table-column label="问卷标题" prop="title"></el-table-column>
+                <!-- <el-table-column label="创建人" prop="title"></el-table-column> -->
+                <el-table-column label="问卷类型" prop="type">
+                    <template #default="scope">
+                        {{ typecfg[scope.row.type]||'' }}
+                    </template>
+                </el-table-column>
+                <el-table-column label="创建时间" prop="createDate"></el-table-column>
                 <el-table-column label="操作" width="200">
                     <template #default="scope">
-                        <el-button link type="text" size="mini" @click="handleRelease(scope.row)">发布问卷</el-button>
-                        <el-button link type="text" size="mini" @click="handleDetail(scope.row)">详情</el-button>
-                        <el-button link type="text" size="mini" @click="handleDelete(scope.row)">删除</el-button>
+                        <el-button link type="text" size="mini" @click="handleRelease(scope.row)" v-hasPermi="['core:questionnaire:publish']">发布问卷</el-button>
+                        <el-button link type="text" size="mini" @click="handleDetail(scope.row)" v-hasPermi="['core:questionnairedetail:info']">详情</el-button>
+                        <el-button link type="text" size="mini" @click="handleDelete(scope.row)" v-hasPermi="['core:questionnaire:delete']">删除</el-button>
                     </template>
                 </el-table-column>
             </el-table>
@@ -46,31 +53,135 @@
                 </el-pagination>
             </el-row>
         </div>
+        <el-dialog width="50%" :visible.sync="show" title="新增问卷" @close="cancel">
+            <el-form ref="wjRef" :model="form" :rules="rules" label-width="100px" style="width: 90%;margin: 0 auto;">
+                <el-form-item label="问卷标题" prop="title">
+                    <el-input v-model="form.title" placeholder="请输入问卷标题"></el-input>
+                </el-form-item>
+                <el-form-item label="问卷类型" prop="type">
+                    <el-select v-model="form.type" placeholder="请选择问卷类型" style="width: 100%;">
+                        <el-option label="初级" :value="1"></el-option>
+                        <el-option label="中级" :value="2"></el-option>
+                        <el-option label="高级级" :value="3"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="备注" prop="remark">
+                    <el-input v-model="form.remark" placeholder="请输入备注"></el-input>
+                </el-form-item>
+                <el-form-item label="上传文件" prop="file">
+                    <el-upload
+                        :action="uploadUrl"
+                        :headers="uploadHeaders"
+                        :on-success="uploadFileSuccess"
+                        :before-upload="beforeAvatarUpload"
+                        :on-remove="removeFile"
+                        :limit="1"
+                        :file-list="fileList">
+                        <el-button size="small" type="primary">上传附件</el-button>
+                    </el-upload>
+                </el-form-item>
+            </el-form>
+            <div class="demo-drawer__footer" style="display: flex;justify-content: end;">
+                <el-button :loading="buttonLoading" type="primary" @click="submitForm">保 存</el-button>
+                <el-button @click="cancel" style="margin-right: 5%;">取 消</el-button>
+            </div>
+        </el-dialog>
     </div>
 </template>
 
 <script setup name="">
-    import { ref, getCurrentInstance } from 'vue'
+    import Cookies from "js-cookie"
+    import { ref, getCurrentInstance, onMounted } from 'vue'
     const { proxy } = getCurrentInstance();
+    const uploadUrl = `${window.SITE_CONFIG["apiURL"]}/sys/oss/uploadFile`
+    const uploadHeaders = {token:Cookies.get("token")};
+    import { getQuestionnaireList, addQuestionnaire, deleteQuestionnaire } from '@/api/agent/index.js'
     
     const queryParams = ref({
         page:1,
         limit:10,
-        name: '',
-        type: '',
-        sort: ''
+        title: '',
+        type: ''
     })
-    const dataList = ref([1,2,3])
+    const typecfg = {
+        1: '初级',
+        2: '中级',
+        3: '高级'
+    }
+    const dataList = ref([])
     const total = ref(0)
     const loading = ref(false)
+    const show = ref(false)
+    const fileList = ref([])
+    const wjRef = ref(null)
+    const buttonLoading = ref(false)
+    const form = ref({
+        id:'',
+        title: '',
+        type: '',
+        remark: '',
+        file: ''
+    })
+    const rules = ref({
+        title:[{required:true,message:'请输入问卷标题',trigger:'blur'}],
+        type:[{required:true,message:'请选择问卷类型',trigger:'change'}],
+        file:[{required:true,message:'请上传附件',trigger:'change'}]
+    })
 
+    const uploadFileSuccess = (e) => {
+        form.value.file = e.data
+            if(e.data){
+                fileList.value = [{name:e.data.split('/')[e.data.split('/').length-1],url:e.data}]
+            }
+    }
+    const beforeAvatarUpload = (e) => {
+        let type = e.name.split('.')[e.name.split('.').length-1];
+        let isExcel = e.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
+        if(type.toLowerCase() !== 'xlsx' && type.toLowerCase() !== 'xls' && !isExcel){
+            proxy?.$modal.msgError('请上传xlsx或xls格式的Excel文件!');
+            return false;
+        }
+    }
+    const removeFile = (file) => {
+        form.value.file = ''
+        fileList.value = []
+    }
+
+    const submitForm = () => {
+        proxy.$refs.wjRef.validate(async valid => {
+            if (valid) {
+                buttonLoading.value = true;
+                const res = await addQuestionnaire(form.value);
+                if (res.code === 0) {
+                    proxy?.$modal.msgSuccess('新增成功!');
+                    show.value = false;
+                    getList();
+                } else {
+                    buttonLoading.value = false;
+                }
+            } else return false;
+        })
+    }
+
+    const cancel = () => {
+        show.value = false;
+        form.value = {
+            id:'',
+            title: '',
+            type: '',
+            remark: '',
+            file: ''
+        }
+        fileList.value = [];
+        proxy?.$refs.wjRef.resetFields();
+    }
 
     const getList = async () => {
         let query = {...queryParams.value};
         loading.value = true;
-        // const res = await listOrder(query);
-        // userList.value = res.data.list;
-        // total.value = res.data.total;
+        const res = await getQuestionnaireList(query);
+        dataList.value = res.data.list;
+        total.value = res.data.total;
         loading.value = false;
     }
     const handleSizeChange = (e)=>{
@@ -83,21 +194,22 @@
     }
 
     const handleRelease = row => {
-        proxy.$router.push({path:'/agentQuestionnairePublish'});
+        proxy.$router.push({path:'/agentQuestionnairePublish',query:{id:row.id,title:row.title,type:typecfg[row.type]}});
     }
     const handleDetail = row => {
-        proxy.$router.push({path:'/agentQuestionnaireDetail'});
+        proxy.$router.push({path:'/agentQuestionnaireDetail',query:{id:row.id,title:row.title,type:typecfg[row.type]}});
     }
-    const handleDelete = row => {
-        proxy.$confirm('确定删除该问卷吗?', '提示', {
-            confirmButtonText: '确定',
-            confirmButtonColor:'#761E6A',
-            cancelButtonText: '取消',
-            type: 'warning'
-        }).then(() => {
-            
-        })
+    const handleDelete = async row => {
+        await proxy?.$modal.confirm('确认删除问卷【' + row.title + '】吗?').finally(() => loading.value = false);
+        let res = await deleteQuestionnaire(row.id);
+        if (res.code === 0) proxy?.$modal.msgSuccess("删除成功");
+        else return proxy?.$modal.msgError(res.msg);
+        await getList();
     }
+
+    onMounted(()=>{
+        getList();
+    })
 </script>
 
 <style scoped lang="scss">