Browse Source

问卷调研模块修改

htc 15 hours ago
parent
commit
b4d8d9f1e0

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

@@ -111,6 +111,33 @@ export function getPersonQueList(query) {
     params: query
   })
 }
+
+export function deleteQuestionnaireOne(id) {
+  return request({
+    url: `/core/team/questionnaire/deleteOne/${id}`,
+    method: 'delete'
+  })
+}
+export function getQuestionnairePublishInfo(id) {
+  return request({
+    url: `/core/team/questionnaire/${id}`,
+    method: 'get'
+  })
+}
+export function publishQuestionnaireConfirm(id) {
+  return request({
+    url: `/core/team/questionnaire/publish/${id}`,
+    method: 'post'
+  })
+}
+export function updateQuestionnairePublish(data) {
+  return request({
+    url: '/core/team/questionnaire',
+    method: 'put',
+    data
+  })
+}
+
 // ---end 问卷管理
 
 // ---start 项目管理

+ 510 - 4
src/views/modules/agent/questionnaire.vue

@@ -14,6 +14,7 @@
                 <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="-1"></el-option>
                 <el-option label="进行中" :value="0"></el-option>
                 <el-option label="已完成" :value="1"></el-option>
             </el-select>
@@ -22,7 +23,22 @@
             <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_r adfac">
+                    <div class="lt_r adfac" v-if="item.status===-1">
+                        <div class="lr_pre adfac" @click="handleUpdate(item)">
+                            <img src="@/assets/images/agent/send_mini.png">
+                            <span>修改问卷</span>
+                        </div>
+                        <div class="lr_pre adfac" @click="handlePublish(item)">
+                            <img src="@/assets/images/agent/send_mini.png">
+                            <span>发布问卷</span>
+                        </div>
+                        <div class="lr_pre adfac" @click="handleDelete(item)">
+                            <img src="@/assets/images/agent/report_mini.png">
+                            <span>删除问卷</span>
+                        </div>
+                        <div class="lr_status dfb">待发布</div>
+                    </div>
+                    <div class="lt_r adfac" v-else>
                         <div class="lr_pre adfac" v-hasPermi="['core:questionnaire:info']" @click="handleSchedule(item)">
                             <img src="@/assets/images/agent/send_mini.png">
                             <span>查看进度</span>
@@ -40,7 +56,7 @@
                 <div class="li_time_person adf">
                     <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">作答成员:<span>{{ item.finishNum||0 }}/{{item.userNum||0}}</span>完成</div>
                     <div class="ltp">发送人:{{ item.creatorName }}</div>
                 </div>
                 <div class="li_jd">
@@ -48,7 +64,7 @@
                 </div>
                 <div class="li_text adfacjb">
                     <span>完成情况</span>
-                    <span>{{ ((item.finishNum/item.userNum)*100).toFixed(0) }}%</span>
+                    <span>{{ item.userNum?((item.finishNum/item.userNum)*100).toFixed(0):0 }}%</span>
                 </div>
             </div>
             <el-row style="display: flex;justify-content: center;">
@@ -64,13 +80,108 @@
                 </el-pagination>
             </el-row>
         </div>
+        <el-dialog width="40%" :visible.sync="show" title="修改问卷" @close="cancel">
+            <div class="content">
+                <div class="c_set">
+                    <div class="cs_title" style="margin-top: 0;">选择项目成员</div>
+                    <div class="cs_users adf">
+                        <div class="cu_l adfacjb">
+                            <img src="@/assets/images/agent/users_mini.png">
+                            <div class="cul_add" @click="handleAddUser">+ 添加</div>
+                        </div>
+                        <div class="cu_r adfac">
+                            <div class="cr_item adfacjb" v-for="(item,index) in params.persons" :key="index">
+                                <span>{{ item.name }}</span>
+                                <img src="@/assets/images/agent/remove_mini.png" @click="handleRemoveUser(item,index)">
+                            </div>
+                        </div>
+                    </div>
+                    <div class="cs_title">问卷回答设置</div>
+                    <div class="cs_zd adfac">
+                        <span>只允许作答</span>
+                        <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;" @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;" @change="e=>handleChangeDatetime(e,'endTime')"></el-date-picker>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <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>
+        <el-dialog width="790px" :visible.sync="userShow" title="选择成员" @close="userShow=false">
+            <div class="ed_coach adf">
+                <div class="ec_l">
+                    <el-input placeholder="请输入成员名搜索" suffix-icon="el-icon-search" v-model="userName" style="width: 100%;"></el-input>
+                    <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">
+                                <div class="ei_l adffc" style="cursor: pointer;">
+                                    <div class="adfac">
+                                        <span @click="handleChooseUser(item)">{{ item.programName }}</span>
+                                        <span></span>
+                                    </div>
+                                    <div><span style="margin-left: 0;">{{ item.enterpriseName }}-{{ item.teamName }}</span></div>
+                                </div>
+                                <div class="ei_r" @click="handleChooseUser(item)">
+                                    <span>成员</span>
+                                </div>
+                            </div>
+                        </div>
+                    </template>
+                    <template v-else>
+                        <div class="ecl_company">
+                            <i class="el-icon-arrow-left" @click="handleUserBack"></i>
+                            {{ companyName }}
+                        </div>
+                        <el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange" style="margin-top: 33px;">全选</el-checkbox>
+                        <div class="ecl_cbs">
+                            <el-checkbox v-model="user.checked" v-for="(user,index) in userList" :key="index" @change="handleChangeUser" style="margin-top: 24px;display: block;">{{ user.name }}</el-checkbox>
+                        </div>
+                    </template>
+                </div>
+                <div class="ec_r">
+                    <div class="ecr_text">已选择({{ selectedUsers.length }}/<span>{{ userList.length }}</span>)</div>
+                    <div class="ecr_names">
+                        <div class="en_pre adfacjc" v-for="(item,index) in selectedUsers" :key="index">
+                            <span>{{ item.name }}</span>
+                            <img src="@/assets/images/agent/delete_mini.png" @click="handleDeleteUser(item,index)">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="demo-drawer__footer" style="display: flex;justify-content: end;margin-top: 20px;">
+                <el-button type="primary" @click="submitForm2">确 认</el-button>
+                <el-button @click="cancel2" style="margin-right: 5%;">取 消</el-button>
+            </div>
+        </el-dialog>
     </div>
 </template>
 
 <script setup name="">
     import { ref, getCurrentInstance, onMounted } from 'vue'
     import {useAgentStore} from "@/store_v3/modules/agent";
-    import { getTeamQuestionnaireList } from "@/api/agent/index.js";
+    import { 
+        getTeamQuestionnaireList,
+        deleteQuestionnaireOne,
+        getQuestionnairePublishInfo,
+        getProgramList,
+        getCoachList,
+        publishQuestionnaireConfirm,
+        updateQuestionnairePublish
+    } from "@/api/agent/index.js";
     const { proxy } = getCurrentInstance();
     useAgentStore().getQuestionnaireData();
     
@@ -83,6 +194,170 @@
     const total = ref(0)
     const dataList = ref([])
 
+    const show = ref(false)
+    const params = ref({
+        questionnaireId:'',
+        programId:'',
+        teamId:'',
+        userIds:[],
+        persons:[],
+        memberInfos:[],
+        answerSetting:1,
+        startTime:'',
+        endTime:''
+    })
+    const userShow = ref(false)
+    const checkShow = ref(false)
+    const companyName = ref('')
+    const userName = ref('')
+    const companyList = ref([])
+    const userList = ref([])
+    const selectedUsers = ref([])
+    const checkAll = ref(false)
+    const isIndeterminate = ref(false)
+    const buttonLoading = ref(false)
+    
+    const handleAddUser = () => {
+        userShow.value = true
+        getProgramList({page:1,limit:-1}).then(res=>{
+            if(res.code !==0) return proxy.$message({type:'warning',message:res.msg});
+            companyList.value = res.data.list.map(l=>{
+                return {
+                    id:l.id,
+                    programName:l.programName,
+                    enterpriseName:l.enterpriseName,
+                    teamName:l.teamName,
+                    teamId:l.teamId
+                }
+            });
+        })
+    }
+    const handleChangeDatetime = (e,key) => {
+        params.value[key] = proxy.parseTime(new Date(e), '{yy}-{mm}-{dd} {hh}:{ii}:{ss}');
+    } 
+    const handleRemoveUser = (item,index) => {
+        params.value.persons.splice(index,1);
+        params.value.userIds = params.value.persons.map(l=>l.id);
+    }
+    const handleChooseUser = (item) => {
+        params.value.programId = item.id;
+        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.programName
+    }
+    const handleUserBack = () => {
+        checkShow.value = false
+        companyName.value = ''
+    }
+    const handleCheckAllChange = (val) => {
+        selectedUsers.value = val ? JSON.parse(JSON.stringify(userList.value)) : [];
+        isIndeterminate.value = false;
+        userList.value.forEach(item => item.checked = val)
+    }
+    const handleChangeUser = () => {
+        let trues = userList.value.filter(item => item.checked).length;
+        if(trues>0&&trues<userList.value.length) isIndeterminate.value = true;
+        else isIndeterminate.value = false;
+        if(trues===userList.value.length) checkAll.value = true;
+        else checkAll.value = false;
+
+        selectedUsers.value = userList.value.filter(item => item.checked);
+    }
+    const handleDeleteUser = (item,index) => {
+        let i = userList.value.findIndex(c => c.id === item.id);
+        if(i>-1) userList.value[i].checked = false;
+        selectedUsers.value.splice(index,1);
+
+        let trues = userList.value.filter(item => item.checked).length;
+        if(trues>0&&trues<userList.value.length) isIndeterminate.value = true;
+        else isIndeterminate.value = false;
+        if(trues===userList.value.length) checkAll.value = true;
+        else checkAll.value = false;
+    }
+    const submitForm2 = () => {
+        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 cancel2 = () => {
+        userShow.value = false;
+    }
+    const cancel = () => {
+        show.value = false;
+    }
+    const submitForm = () => {
+        if(buttonLoading.value) return
+        buttonLoading.value = true;
+
+        if(params.value.userIds.length === 0){
+            proxy.$message({type:'warning',message:'请选择项目成员'});
+            buttonLoading.value = false;
+            return;
+        }
+        if(!params.value.startTime || !params.value.endTime){
+            proxy.$message({type:'warning',message:'请设置问卷作答时间'});
+            buttonLoading.value = false;
+            return;
+        }
+
+        params.value.memberInfos = params.value.persons.map(l=>{
+            return {
+                userId:l.id,
+                realName:l.name
+            }
+        })
+        let { persons ,userIds ,...paras } = params.value;
+        let data = {...paras};
+        updateQuestionnairePublish(data).then(res=>{
+            if(res.code!==0) return proxy.$message.error(res.msg)
+            proxy.$message.success('修改成功')
+        }).finally(()=>{buttonLoading.value = false;show.value = false;})
+    }
+
+    const handleUpdate = row => {
+        getQuestionnairePublishInfo(row.id).then(res=>{
+            if(res.code!==0) return proxy.$message({type:'warning',message:res.msg});
+            params.value = {...res.data,persons:[],userIds:[]}
+            params.value.persons = res.data.memberInfos.map(m=>({id:m.userId,name:m.realName}));
+            params.value.userIds = res.data.memberInfos.map(m=>m.userId);
+            show.value = true;
+        })
+    }
+    const handlePublish = async row => {
+        await proxy.$modal.confirm('是否确认发布该问卷?', '提示', {})
+        let res = await publishQuestionnaireConfirm(row.id).finally(()=>{})
+        if(res.code!==0) return proxy.$message.error(res.msg)
+        proxy.$message.success('发布成功')
+        getList()
+    }
+    const handleDelete = row => {
+        proxy.$confirm('是否确认删除该问卷?', '警告', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+        }).then(async () => {
+            let res = await deleteQuestionnaireOne(row.id)
+            if(res.code!==0) return proxy.$message.error(res.msg)
+            proxy.$message.success('删除成功')
+            return
+        }).then(()=>{
+            getList()
+        })
+    }
+
     const handleSchedule = (item) => {
         proxy.$router.push({
             path: '/agentQuestionnaireSchedule',
@@ -209,6 +484,10 @@
                             font-size: 14px;
                             line-height: 24px;
                             text-align: center;
+                            &.dfb{
+                                background: #e7e7e7;
+                                color: #414040;
+                            }
                             &.jxz{
                                 background: #FCECB6;
                                 color: #864F10;
@@ -281,4 +560,231 @@
             }
         }
     }
+
+    .content{
+        .c_set{
+            width: 100%;
+            background: #FFFFFF;
+            border-radius: 6px;
+            margin-top: 8px;
+            padding: 0 21px 28px 21px;
+            box-sizing: border-box;
+            .cs_title{
+                font-family: PingFang-SC, PingFang-SC;
+                font-weight: bold;
+                font-size: 14px;
+                color: #252525;
+                line-height: 20px;
+                margin-top: 30px;
+            }
+            .cs_users{
+                width: 100%;
+                height: 36px;
+                border-radius: 6px;
+                border: 1px solid #E5E7EB;
+                margin-top: 20px;
+                padding: 7px 12px;
+                box-sizing: border-box;
+                .cu_l{
+                    width: 104px;
+                    padding-right: 15px;
+                    box-sizing: border-box;
+                    &>img{
+                        width: 18px;
+                        height: 18px;
+                        margin-top: -5px;
+                    }
+                    .cul_add{
+                        width: 54px;
+                        height: 24px;
+                        border-radius: 4px;
+                        border: 1px solid #761E6A;
+                        font-family: PingFangSC, PingFang SC;
+                        font-weight: 400;
+                        font-size: 12px;
+                        color: #761E6A;
+                        line-height: 22px;
+                        text-align: center;
+                        margin-top: -2px;
+                        cursor: pointer;
+                    }
+                }
+                .cu_r{
+                    width: calc(100% - 104px);
+                    margin-left: -16px;
+                    .cr_item{
+                        padding: 4px 10px;
+                        background: #F9FAFB;
+                        border-radius: 6px;
+                        margin-left: 16px;
+                        span{
+                            font-family: PingFangSC, PingFang SC;
+                            font-weight: 400;
+                            font-size: 14px;
+                            color: #252525;
+                            line-height: 14px;
+                        }
+                        img{
+                            width: 16px;
+                            height: 16px;
+                            margin-left: 10px;
+                            cursor: pointer;
+                        }
+                    }
+                }
+            }
+            .cs_zd{
+                margin-top: 12px;
+                span{
+                    font-family: PingFangSC, PingFang SC;
+                    font-weight: 400;
+                    font-size: 14px;
+                    color: #6B7280;
+                    line-height: 20px;
+                }
+            }
+            .cs_time{
+                margin-top: 6px;
+                .ct_pre{
+                    margin-top: 30px;
+                    span{
+                        font-family: PingFangSC, PingFang SC;
+                        font-weight: 400;
+                        font-size: 14px;
+                        color: #6B7280;
+                        line-height: 20px;
+                        margin-right: 23px;
+                    }
+                }
+            }
+            .cs_btn{
+                margin-top: 70px;
+                padding-left: 79px;
+            }
+        }
+    }
+
+    .ed_coach{
+        margin-top: -20px;
+        .ec_l{
+            width: calc(50% - 1px);
+            height: 100%;
+            padding-right: 30px;
+            box-sizing: border-box;
+            border-right: 1px solid #ECEEF5;
+
+            .ecl_gs{
+                margin-top: 19px;
+                .eg_title{
+                    font-family: PingFangSC, PingFang SC;
+                    font-weight: 400;
+                    font-size: 14px;
+                    color: #6B7280;
+                    line-height: 20px;
+                }
+                .egt_item{
+                    margin-top: 24px;
+                    .ei_l{
+                        span{
+                            font-family: PingFang-SC, PingFang-SC;
+                            font-weight: bold;
+                            font-size: 14px;
+                            color: #252525;
+                            line-height: 20px;
+                            &:last-child{
+                                font-weight: 400;
+                                color: #6B7280;
+                                margin-left: 20px;
+                            }
+                        }
+                    }
+                    .ei_r{
+                        span{
+                            font-family: PingFangSC, PingFang SC;
+                            font-weight: 400;
+                            font-size: 14px;
+                            color: #761E6A;
+                            line-height: 20px;
+                            position: relative;
+                            padding-left: 8px;
+                            cursor: pointer;
+                            &::before{
+                                content: '';
+                                width: 1px;
+                                height: 16px;
+                                background: #DDE0E6;
+                                position: absolute;
+                                left: 0;
+                                top: 50%;
+                                margin-top: -8px;
+                            }
+                        }
+                    }
+                }
+            }
+
+            .ecl_company{
+                margin-top: 19px;
+                position: relative;
+                font-family: PingFang-SC, PingFang-SC;
+                font-weight: bold;
+                font-size: 14px;
+                color: #252525;
+                line-height: 20px;
+                text-align: center;
+                i{
+                    position: absolute;
+                    left: 0;
+                    font-size: 14px;
+                    color: #252525;
+                    cursor: pointer;
+                }
+            }
+            .ecl_cbs{
+                width: 100%;
+                // height: calc(100% - 143px);
+                max-height: 376px;
+                overflow-y: auto;
+            }
+        }
+        .ec_r{
+            width: 50%;
+            padding-left: 30px;
+            box-sizing: border-box;
+            .ecr_text{
+                font-family: PingFang-SC, PingFang-SC;
+                font-weight: bold;
+                font-size: 16px;
+                color: #252525;
+                line-height: 22px;
+                span{
+                    color: #B9B9B9;
+                }
+            }
+            .ecr_names{
+                margin-left: -16px;
+                overflow: hidden;
+                .en_pre{
+                    float: left;
+                    padding: 10px;
+                    background: #F9FAFB;
+                    border-radius: 6px;
+                    margin: 19px 0 0 16px;
+                    span{
+                        font-family: PingFangSC, PingFang SC;
+                        font-weight: 400;
+                        font-size: 14px;
+                        color: #252525;
+                        line-height: 14px;
+                    }
+                    img{
+                        width: 16px;
+                        height: 16px;
+                        margin-left: 10px;
+                        cursor: pointer;
+                    }
+                }
+            }
+        }
+    }
 </style>

+ 21 - 4
src/views/modules/agent/questionnaire/publish.vue

@@ -61,7 +61,6 @@
                                 <div class="ei_l adffc" style="cursor: pointer;">
                                     <div class="adfac">
                                         <span @click="handleChooseUser(item)">{{ item.programName }}</span>
-                                        <!-- <span style="margin-left: 20px;">{{ item.num }}人</span> -->
                                         <span></span>
                                     </div>
                                     <div><span style="margin-left: 0;">{{ item.enterpriseName }}-{{ item.teamName }}</span></div>
@@ -115,6 +114,7 @@
         teamId:'',
         userIds:[],
         persons:[],
+        memberInfos:[],
         answerSetting:1,
         startTime:'',
         endTime:''
@@ -166,9 +166,26 @@
         if(!canSubmit.value) return
         canSubmit.value = false;
 
-        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(params.value.userIds.length === 0){
+            proxy.$message({type:'warning',message:'请选择项目成员'});
+            canSubmit.value = true;
+            return;
+        }
+        if(!params.value.startTime || !params.value.endTime){
+            proxy.$message({type:'warning',message:'请设置问卷作答时间'});
+            canSubmit.value = true;
+            return;
+        }
+
+        params.value.memberInfos = params.value.persons.map(l=>{
+            return {
+                userId:l.id,
+                realName:l.name
+            }
+        })
+        let { persons ,userIds ,...paras } = params.value;
+        let data = {...paras};
+        sendQuestionnaire(data).then(res=>{
             if(res.code!==0){
                 canSubmit.value = true;
                 return proxy.$message({type:'warning',message:res.msg});