|
@@ -6,102 +6,165 @@
|
|
|
<p class="tip">共 <span>{{list.length}}</span> 题,已作答 <span style="font-weight: bold;">{{answerNum}}</span> 题,请耐心选择!</p>
|
|
|
</div>
|
|
|
<div class="list" :style="{'height':(h-180-mt)+'px'}">
|
|
|
- <div class="l_item" v-for="(item,index) in list" :key="index">
|
|
|
- <div class="li_box" :class="{'active':item.warn}">
|
|
|
- <div class="lb_title adf">
|
|
|
- <span>*</span>
|
|
|
- {{index+1}}. {{item.question}}
|
|
|
- </div>
|
|
|
- <div class="lb_answers">
|
|
|
- <u-radio-group v-model="item.answer" placement="column">
|
|
|
- <div class="la_item" v-for="(pre,idx) in item.userAnswer" :key="idx">
|
|
|
- <u-radio :label="pre.questionOption" :name="pre.questionOption" activeColor="#833478" size="36rpx" iconSize="32rpx" labelSize="32rpx"></u-radio>
|
|
|
- </div>
|
|
|
- </u-radio-group>
|
|
|
- </div>
|
|
|
+ <div v-if="isLoading" class="loading-container adfacjc">
|
|
|
+ <div class="adfac">
|
|
|
+ <u-loading-icon size="42"></u-loading-icon>
|
|
|
+ <text style="margin-left: 10rpx;font-size: 34rpx;color: #666666;">问卷加载中...</text>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <template v-else>
|
|
|
+ <div class="l_item" v-for="(item,index) in list" :key="item.id">
|
|
|
+ <QuestionItem :item="item" :index="index" @change="handleAnswerChange"></QuestionItem>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
- <div class="bottom">
|
|
|
- <div class="zt_btn" @tap="submitWj">提交问卷</div>
|
|
|
+ <div class="bottom">
|
|
|
+ <view class="zt_btn" @tap="submitWj">{{isSubmitting ? '提交中...' : '提交问卷'}}</view>
|
|
|
</div>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+ import QuestionItem from '@/components/QuestionItem/index.vue'
|
|
|
export default {
|
|
|
+ components:{QuestionItem},
|
|
|
data(){
|
|
|
return {
|
|
|
title:'',
|
|
|
teamQuestionnaireId:'',
|
|
|
list:[],
|
|
|
- answerNum:0,
|
|
|
- questionnaire:null
|
|
|
+ questionnaire:null,
|
|
|
+ isLoading: true, // 新增加载状态
|
|
|
+ isSubmitting: false // 新增提交状态
|
|
|
}
|
|
|
},
|
|
|
onLoad(option) {
|
|
|
this.title = option.title;
|
|
|
this.teamQuestionnaireId = option.teamQuestionnaireId;
|
|
|
this.getList();
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ answerNum() {
|
|
|
+ return this.list.filter(l => !!l.answer).length || 0;
|
|
|
+ }
|
|
|
},
|
|
|
- watch:{
|
|
|
- list:{
|
|
|
- handler(newval){
|
|
|
- this.answerNum = this.list.filter(l=>l.answer!='')?.length||0;
|
|
|
- },
|
|
|
- deep:true
|
|
|
- }
|
|
|
- },
|
|
|
- methods:{
|
|
|
+ methods:{
|
|
|
+ handleAnswerChange(e) {
|
|
|
+ const item = this.list[e.index];
|
|
|
+ if (item) {
|
|
|
+ this.$set(item, 'answer', e.value);
|
|
|
+ if (item.warn) {
|
|
|
+ this.$set(item, 'warn', false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
getList(){
|
|
|
- if(uni.getStorageSync('questionnaire')) this.questionnaire = JSON.parse(uni.getStorageSync('questionnaire'))
|
|
|
- this.$api.get('/core/team/member/answer/listByUser/'+this.teamQuestionnaireId).then(res=>{
|
|
|
- if(res.data.code!==0) return this.$showToast(res.data.msg)
|
|
|
- this.list = res.data.data;
|
|
|
- this.list.forEach((l,i)=>{
|
|
|
- this.$set(this.list[i],'warn',false);
|
|
|
- this.$set(this.list[i],'answer','');
|
|
|
- if(this.questionnaire&&this.teamQuestionnaireId==this.questionnaire.key){
|
|
|
- let t = this.questionnaire.list.find(q=>q.id==l.id)
|
|
|
- this.$set(this.list[i],'answer',t?t.answer:'');
|
|
|
- }
|
|
|
- })
|
|
|
- })
|
|
|
+ let questionnaire = null;
|
|
|
+ try {
|
|
|
+ const cacheStr = uni.getStorageSync('questionnaire');
|
|
|
+ if (cacheStr) {
|
|
|
+ questionnaire = JSON.parse(cacheStr);
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error("解析问卷缓存失败:", e);
|
|
|
+ uni.removeStorageSync('questionnaire'); // 缓存有问题,直接清除
|
|
|
+ }
|
|
|
+
|
|
|
+ this.isLoading = true;
|
|
|
+ this.$api.get('/core/team/member/answer/listByUser/' + this.teamQuestionnaireId).then(res => {
|
|
|
+ if (res.data.code !== 0) return this.$showToast(res.data.msg);
|
|
|
+ const answerMap = new Map();
|
|
|
+ if (questionnaire && this.teamQuestionnaireId == questionnaire.key) {
|
|
|
+ questionnaire.list.forEach(q => answerMap.set(q.id, q.answer));
|
|
|
+ }
|
|
|
+
|
|
|
+ this.list = res.data.data.map(item => ({
|
|
|
+ ...item,
|
|
|
+ warn: false,
|
|
|
+ answer: answerMap.get(item.id) || '' // 直接从 Map 中获取答案
|
|
|
+ }));
|
|
|
+ }).catch(()=>{
|
|
|
+ this.isLoading = false;
|
|
|
+ this.$showToast('网络异常,请稍后重试');
|
|
|
+ }).finally(() => {
|
|
|
+ this.isLoading = false;
|
|
|
+ });
|
|
|
},
|
|
|
- submitWj(){
|
|
|
- this.list.forEach((l,i)=>{
|
|
|
- this.$set(this.list[i],'warn',!l.answer?true:false)
|
|
|
- })
|
|
|
- let idx = this.list.findIndex(l=>l.warn);
|
|
|
- if(idx>-1) return this.$showModal(`第${idx+1}项未选择答案,请选择。`)
|
|
|
- let newList = JSON.parse(JSON.stringify(this.list));
|
|
|
- newList.forEach(l=>{
|
|
|
- l.isAnswer = '1';
|
|
|
- let i = l.userAnswer.findIndex(a=>a.questionOption===l.answer)
|
|
|
- if(i>-1) l.userAnswer[i].userAnswer = true;
|
|
|
- delete l.answer
|
|
|
- delete l.warn
|
|
|
- })
|
|
|
- this.$api.post('/core/team/member/answer/submit',newList).then(res=>{
|
|
|
- if(res.data.code!==0) return this.$showToast(res.data.msg)
|
|
|
- uni.removeStorageSync('questionnaire')
|
|
|
- uni.navigateTo({
|
|
|
- url:'/pages/questionnaireResult'
|
|
|
- })
|
|
|
- })
|
|
|
+ submitWj() {
|
|
|
+ if (this.isSubmitting) return;
|
|
|
+
|
|
|
+ let firstUnansweredIndex = -1;
|
|
|
+ this.list.forEach((l, i) => {
|
|
|
+ const isAnswered = !!l.answer;
|
|
|
+ this.$set(l, 'warn', !isAnswered);
|
|
|
+ if (!isAnswered && firstUnansweredIndex === -1) {
|
|
|
+ firstUnansweredIndex = i;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (firstUnansweredIndex > -1) {
|
|
|
+ // 使用 uni.showModal 以提供更好的用户体验
|
|
|
+ return uni.showModal({
|
|
|
+ title: '提示',
|
|
|
+ content: `第 ${firstUnansweredIndex + 1} 项未选择答案,请选择。`,
|
|
|
+ showCancel: false
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用更清晰的逻辑构建提交数据
|
|
|
+ const submitList = this.list.map(question => {
|
|
|
+ // 映射 userAnswer 数组,并添加一个清晰的 `isSelected` 字段
|
|
|
+ const formattedUserAnswer = question.userAnswer.map(option => ({
|
|
|
+ ...option,
|
|
|
+ // 假设后端需要一个布尔值来表示是否选中
|
|
|
+ isSelected: option.questionOption === question.answer
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 剔除前端专用的字段(answer, warn),保持提交数据纯净
|
|
|
+ const { answer, warn, ...restOfQuestion } = question;
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...restOfQuestion,
|
|
|
+ isAnswer: '1', // 标记为已作答
|
|
|
+ userAnswer: formattedUserAnswer // 使用格式化后的数组
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ this.isSubmitting = true;
|
|
|
+ this.$api.post('/core/team/member/answer/submit', submitList).then(res => {
|
|
|
+ if (res.data.code !== 0) {
|
|
|
+ // 在失败时也应该重置提交状态
|
|
|
+ this.isSubmitting = false;
|
|
|
+ return this.$showToast(res.data.msg);
|
|
|
+ }
|
|
|
+ uni.removeStorageSync('questionnaire');
|
|
|
+ // 提交成功后建议使用 redirectTo 或 reLaunch,防止用户返回到填写页
|
|
|
+ uni.redirectTo({
|
|
|
+ url: '/pages/questionnaireResult'
|
|
|
+ });
|
|
|
+ }).catch(err => {
|
|
|
+ // this.isSubmitting = false; // .finally() 中已经处理
|
|
|
+ this.$showToast('网络异常,请稍后重试');
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.isSubmitting = false; // 确保无论成功或失败,提交状态都会被重置
|
|
|
+ });
|
|
|
},
|
|
|
setQuestionnaireCache(){
|
|
|
- if(uni.getStorageSync('questionnaire')) uni.removeStorageSync('questionnaire')
|
|
|
- let list = [];
|
|
|
- this.list.forEach(l=>{
|
|
|
- if(l.answer) list.push({id:l.id,answer:l.answer})
|
|
|
- })
|
|
|
- let qinfo = {
|
|
|
- key:this.teamQuestionnaireId,
|
|
|
- list
|
|
|
+ const answeredList = this.list
|
|
|
+ .filter(l => l.answer) // 过滤出已回答的
|
|
|
+ .map(l => ({ id: l.id, answer: l.answer })); // 转换成需要的格式
|
|
|
+
|
|
|
+ if (answeredList.length === 0) {
|
|
|
+ uni.removeStorageSync('questionnaire');
|
|
|
+ return;
|
|
|
}
|
|
|
- if(list.length===0) return
|
|
|
- uni.setStorageSync('questionnaire',JSON.stringify(qinfo));
|
|
|
+
|
|
|
+ const qinfo = {
|
|
|
+ key: this.teamQuestionnaireId,
|
|
|
+ list: answeredList
|
|
|
+ };
|
|
|
+ uni.setStorageSync('questionnaire', JSON.stringify(qinfo));
|
|
|
}
|
|
|
},
|
|
|
onUnload() {
|
|
@@ -109,11 +172,16 @@
|
|
|
},
|
|
|
onBackPress() {
|
|
|
this.setQuestionnaireCache();
|
|
|
+ return false;
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="less">
|
|
|
+ .loading-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
.page{
|
|
|
background: #F7F2F6;
|
|
|
box-sizing: border-box;
|
|
@@ -152,69 +220,6 @@
|
|
|
background: #FFFFFF;
|
|
|
padding: 6rpx;
|
|
|
box-sizing: border-box;
|
|
|
- .li_box{
|
|
|
- width: 100%;
|
|
|
- padding: 32rpx 34rpx;
|
|
|
- box-sizing: border-box;
|
|
|
- &.active{
|
|
|
- border: 2rpx dotted #FD4F66;
|
|
|
- }
|
|
|
- .lb_title{
|
|
|
- font-family: PingFang-SC, PingFang-SC;
|
|
|
- font-weight: bold;
|
|
|
- font-size: 32rpx;
|
|
|
- color: #252525;
|
|
|
- line-height: 48rpx;
|
|
|
- span{
|
|
|
- font-family: PingFangSC, PingFang SC;
|
|
|
- font-weight: 400;
|
|
|
- font-size: 32rpx;
|
|
|
- color: #FD4F66;
|
|
|
- line-height: 51rpx;
|
|
|
- }
|
|
|
- }
|
|
|
- .lb_answers{
|
|
|
- width: calc(100% - 40rpx);
|
|
|
- margin: 30rpx 20rpx 0;
|
|
|
- box-sizing: border-box;
|
|
|
- border: 1rpx solid #E5E7EB;
|
|
|
- .la_item{
|
|
|
- padding: 34rpx 24rpx;
|
|
|
- border-bottom: 1rpx solid #E5E7EB;
|
|
|
- &:last-child{
|
|
|
- border-bottom: none;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- .la_inp{
|
|
|
- width: 100%;
|
|
|
- height: 96rpx;
|
|
|
- border-radius: 24rpx;
|
|
|
- border: 1rpx solid #DFCDDC;
|
|
|
- padding: 24rpx 30rpx;
|
|
|
- box-sizing: border-box;
|
|
|
- margin-top: 30rpx;
|
|
|
- }
|
|
|
- .la_warn{
|
|
|
- padding: 7rpx 23rpx;
|
|
|
- margin-top: 20rpx;
|
|
|
- background: #FFECEC;
|
|
|
- .lw{
|
|
|
- width: 36rpx;
|
|
|
- height: 36rpx;
|
|
|
- border-radius: 50%;
|
|
|
- background: #FD4F66;
|
|
|
- }
|
|
|
- span{
|
|
|
- font-family: PingFangSC, PingFang SC;
|
|
|
- font-weight: 400;
|
|
|
- font-size: 24rpx;
|
|
|
- color: #FD4F66;
|
|
|
- line-height: 51rpx;
|
|
|
- margin-left: 17rpx;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
|