|
|
@@ -6,25 +6,42 @@
|
|
|
<view class="top-progress adfac">
|
|
|
<view class="top-progress-text">测评进度</view>
|
|
|
<view class="top-progress-box">
|
|
|
- <view class="top-progress-box-current" :style="{'width':(2/36*100)+'%'}"></view>
|
|
|
+ <view class="top-progress-box-current" :style="{'width':(answerNum/list.length*100)+'%'}"></view>
|
|
|
</view>
|
|
|
- <view class="top-progress-num"><span>{{2}}</span>/{{36}}</view>
|
|
|
+ <view class="top-progress-num"><span>{{answerNum}}</span>/{{list.length}}</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="memo adfac" v-if="type==='questionnaire'" @click="noticeShow=true">
|
|
|
<text>问卷答题说明</text>
|
|
|
<image :src="imgBase+'icon_memo.png'"></image>
|
|
|
</view>
|
|
|
- <view class="list">
|
|
|
- <question-item v-for="(item,index) in list" :key="index" :item="item"></question-item>
|
|
|
- </view>
|
|
|
+ <scroll-view class="list" scroll-y="true" :scroll-top="scrollTop" :style="{ height: h - 203 - mt + 'px' }">
|
|
|
+ <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>
|
|
|
+ <view>
|
|
|
+ <div class="l_item" v-for="(item, index) in list" :key="item.id" :id="'question-' + index">
|
|
|
+ <question-item :item="item" :index="index" @change="handleAnswerChange"></question-item>
|
|
|
+ </div>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+ </scroll-view>
|
|
|
+ <!-- <view class="list">
|
|
|
+ <div class="l_item" v-for="(item, index) in list" :key="item.id" :id="'question-' + index">
|
|
|
+ <question-item :item="item" :index="index" @change="handleAnswerChange"></<question-item>
|
|
|
+ </div>
|
|
|
+ </view> -->
|
|
|
<view class="bottom adfacjb">
|
|
|
- <view class="bottom-left adffcac" @click="teamShow=true" v-if="!type">
|
|
|
+ <view class="bottom-left adffcac" @click="showTeamInfo" v-if="type==1">
|
|
|
<image :src="imgBase+'questionnaire_info.png'"></image>
|
|
|
<text>团队信息</text>
|
|
|
</view>
|
|
|
<view class="bottom-left adfac" v-else-if="type==='questionnaire'">
|
|
|
- <view class="bottom-left-pre adffcac" @click="teamShow=true">
|
|
|
+ <view class="bottom-left-pre adffcac" @click="showTeamInfo">
|
|
|
<image :src="imgBase+'questionnaire_info.png'"></image>
|
|
|
<text>团队信息</text>
|
|
|
</view>
|
|
|
@@ -57,68 +74,257 @@
|
|
|
noticeContent:`问卷答题说明<br/><br/>问卷答题说明<br/><br/>问卷答题说明<br/><br/>问卷答题说明`,
|
|
|
teamShow:false,
|
|
|
userShow:false,
|
|
|
- teamUserList:[],
|
|
|
- list:[
|
|
|
- {
|
|
|
- question:'我们的团队成员可以清晰阐述团队的共享目的 We can collectively and clearly articulate our shared purpose',
|
|
|
- userAnswer:[
|
|
|
- {
|
|
|
- assessmentMethod:1,
|
|
|
- answer:'',
|
|
|
- questionOption:[
|
|
|
- { questionOption:'1-强烈不同意(Strongly disagree)' },
|
|
|
- { questionOption:'2-不同意(Disagree)' },
|
|
|
- { questionOption:'3-中立(Neutral)' },
|
|
|
- { questionOption:'4-同意(Agree)' },
|
|
|
- { questionOption:'5-完全同意(Totally agree)' }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- assessmentMethod:2,
|
|
|
- answer:'',
|
|
|
- questionOption:[
|
|
|
- { questionOption:'1-强烈不同意(Strongly disagree)' },
|
|
|
- { questionOption:'2-不同意(Disagree)' },
|
|
|
- { questionOption:'3-中立(Neutral)' },
|
|
|
- { questionOption:'4-同意(Agree)' },
|
|
|
- { questionOption:'5-完全同意(Totally agree)' }
|
|
|
- ]
|
|
|
- },
|
|
|
- ]
|
|
|
- }
|
|
|
- ],
|
|
|
- teamInfo:{
|
|
|
- aaa:'',
|
|
|
- bbb:'',
|
|
|
- ccc:'',
|
|
|
- ddd:'',
|
|
|
- eee:'',
|
|
|
- fff:'',
|
|
|
- ggg:'',
|
|
|
- hhh:'',
|
|
|
- iii:'',
|
|
|
- }
|
|
|
+ teamUserList:[],
|
|
|
+ teamQuestionnaireId: '',
|
|
|
+ list: [],
|
|
|
+ questionnaire: null,
|
|
|
+ isLoading: true,
|
|
|
+ isSubmitting: false,
|
|
|
+ scrollTop: 0, // scroll-view 的滚动位置
|
|
|
+ oldScrollTop: 0, // 辅助值,确保即使滚动到相同位置也能触发
|
|
|
+ userAnswerTemp: [],
|
|
|
+ teamId:'',
|
|
|
+ teamInfo:{},
|
|
|
+ teamScaleData:[],
|
|
|
+ teamLevelData:[],
|
|
|
}
|
|
|
},
|
|
|
onLoad(options) {
|
|
|
- this.type = options.type;
|
|
|
- this.questionnaireId = options.questionnaireId;
|
|
|
+ this.type = options.type;
|
|
|
+ this.teamQuestionnaireId = options.teamQuestionnaireId;
|
|
|
+ this.teamId = options.teamId;
|
|
|
+ this.getList();
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ answerNum() {
|
|
|
+ return this.list.filter(l => l?.answer.filter(a => a?.value).length == this.userAnswerTemp.length).length || 0;
|
|
|
+ }
|
|
|
},
|
|
|
- methods:{
|
|
|
- confirmSubmit(){
|
|
|
- let url = '/pagesPublish/questionnaireResult'
|
|
|
- if(this.type === 'questionnaire') url = '/pagesPublish/submitResult'
|
|
|
- uni.navigateTo({ url })
|
|
|
+ methods:{
|
|
|
+ handleAnswerChange(e) {
|
|
|
+ const item = this.list[e.index];
|
|
|
+ if (item) {
|
|
|
+ let answer = JSON.parse(JSON.stringify(item.answer));
|
|
|
+ answer.forEach(a=>{
|
|
|
+ a.value = a.assessmentMethod==e.assessmentMethod?e.value:a.value
|
|
|
+ })
|
|
|
+ this.$set(item, 'answer', answer);
|
|
|
+ if (item.warn) {
|
|
|
+ this.$set(item, 'warn', false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.setQuestionnaireCache();
|
|
|
+ },
|
|
|
+ getList() {
|
|
|
+ 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.userAnswerTemp = res.data.data[0].userAnswer.map(u=>{
|
|
|
+ return {
|
|
|
+ assessmentMethod:u.assessmentMethod,
|
|
|
+ value:''
|
|
|
+ }
|
|
|
+ })
|
|
|
+ let uaTemp = JSON.parse(JSON.stringify(this.userAnswerTemp))
|
|
|
+ this.list = res.data.data.map(item => {
|
|
|
+ let _a = answerMap.get(item.id);
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ answer:_a?uaTemp.map(u=>{
|
|
|
+ return {
|
|
|
+ assessmentMethod:u.assessmentMethod,
|
|
|
+ value:_a.find(a=>a.assessmentMethod==u.assessmentMethod)?.value||''
|
|
|
+ }
|
|
|
+ }):this.userAnswerTemp,
|
|
|
+ warn: false
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.list.forEach(l=>{
|
|
|
+ let _a = answerMap.get(l.id);
|
|
|
+ l.userAnswer.forEach(u=>{
|
|
|
+ u.answer = _a?(_a.find(a=>a.assessmentMethod==u.assessmentMethod)?.value||''):''
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.isLoading = false;
|
|
|
+ this.$showToast('网络异常,请稍后重试');
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.isLoading = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ scrollToQuestion(index) {
|
|
|
+ const query = uni.createSelectorQuery().in(this);
|
|
|
+ const targetId = `#question-${index}`;
|
|
|
+ query.select(targetId).boundingClientRect();
|
|
|
+ query.select('.list').scrollOffset(); // 获取 scroll-view 的滚动信息
|
|
|
+ query.select('.list').boundingClientRect(); // 获取 scroll-view 自身的位置信息
|
|
|
+ query.exec((res) => {
|
|
|
+ // 增加安全校验
|
|
|
+ if (!res || !res[0] || !res[1] || !res[2]) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // res[0]: 目标元素 (#question-N) 的位置信息
|
|
|
+ const itemRect = res[0];
|
|
|
+ // res[1]: 滚动容器 (.list) 的滚动信息
|
|
|
+ const scrollViewScroll = res[1];
|
|
|
+ // res[2]: 滚动容器 (.list) 自身的位置信息
|
|
|
+ const scrollViewRect = res[2];
|
|
|
+
|
|
|
+ // 计算目标滚动位置 = 当前滚动距离 + (目标元素顶部 - 滚动容器顶部) - 预留间距
|
|
|
+ const targetPosition = scrollViewScroll.scrollTop + itemRect.top - scrollViewRect.top - 10; // 减10px作为顶部留白
|
|
|
+
|
|
|
+ // 通过先设置为旧值,再在 nextTick 中设置为新值的方式,确保滚动能够触发
|
|
|
+ this.scrollTop = this.oldScrollTop;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.scrollTop = targetPosition;
|
|
|
+ this.oldScrollTop = targetPosition;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ confirmSubmit() {
|
|
|
+ if (this.isSubmitting) return;
|
|
|
+
|
|
|
+ let firstUnansweredIndex = -1;
|
|
|
+ this.list.forEach((l, i) => {
|
|
|
+ const isAnswered = l.answer.filter(a => a?.value).length==this.userAnswerTemp.length?true:false;
|
|
|
+ this.$set(l, 'warn', !isAnswered);
|
|
|
+ if (!isAnswered && firstUnansweredIndex === -1) {
|
|
|
+ firstUnansweredIndex = i;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (firstUnansweredIndex > -1) {
|
|
|
+ uni.showModal({
|
|
|
+ title: '提示',
|
|
|
+ content: `第 ${firstUnansweredIndex + 1} 项未选择答案,请选择。`,
|
|
|
+ showCancel: false,
|
|
|
+ success: (res) => {
|
|
|
+ if (res.confirm) {
|
|
|
+ // 调用新的滚动方法
|
|
|
+ this.scrollToQuestion(firstUnansweredIndex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return; // 终止提交
|
|
|
+ }
|
|
|
+
|
|
|
+ const submitList = this.list.map((l) => {
|
|
|
+ let {answer,userAnswer,warn,...other} = JSON.parse(JSON.stringify(l));
|
|
|
+ userAnswer.forEach(u=>{
|
|
|
+ u.questionOption.forEach(q=>{
|
|
|
+ q.userAnswer=answer.find(a=>a.assessmentMethod==u.assessmentMethod)?.value==q.questionOption;
|
|
|
+ })
|
|
|
+ })
|
|
|
+ return {
|
|
|
+ ...other,
|
|
|
+ isAnswer: '1',
|
|
|
+ userAnswer
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ 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');
|
|
|
+ let url = '/pagesPublish/questionnaireResult'
|
|
|
+ if(this.type == 2) url = '/pagesPublish/submitResult'
|
|
|
+ uni.navigateTo({ url })
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ this.$showToast('网络异常,请稍后重试');
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.isSubmitting = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ setQuestionnaireCache() {
|
|
|
+ const answeredList = this.list.filter((l) => l.answer.filter(a => a?.value).length>0).map((l) => ({ id: l.id, answer: l.answer }));
|
|
|
+ if (answeredList.length === 0) {
|
|
|
+ uni.removeStorageSync('questionnaire');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const qinfo = {
|
|
|
+ key: this.teamQuestionnaireId,
|
|
|
+ list: answeredList
|
|
|
+ };
|
|
|
+ uni.setStorageSync('questionnaire', JSON.stringify(qinfo));
|
|
|
+ },
|
|
|
+ async getTeamScaleData(){
|
|
|
+ return new Promise((resolve,reject)=>{
|
|
|
+ this.$api.get('/getListByType/team_scale').then(({data:res})=>{
|
|
|
+ if(res.code!==0) return this.$showToast(res.msg)
|
|
|
+ this.teamScaleData = res.data.map(d=>({name:d.dictLabel,id:d.dictValue}))
|
|
|
+ resolve()
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ async getTeamHierarchyData(){
|
|
|
+ return new Promise((resolve,reject)=>{
|
|
|
+ this.$api.get('/getListByType/team_hierarchy').then(({data:res})=>{
|
|
|
+ if(res.code!==0) return this.$showToast(res.msg)
|
|
|
+ this.teamLevelData = res.data.map(d=>({name:d.dictLabel,id:d.dictValue}))
|
|
|
+ resolve()
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ async showTeamInfo(){
|
|
|
+ await this.getTeamScaleData()
|
|
|
+ await this.getTeamHierarchyData()
|
|
|
+ this.$api.get(`/core/user/team/${this.teamId}`).then(({data:res})=>{
|
|
|
+ if(res.code!==0) return this.$showToast(res.msg)
|
|
|
+ this.teamInfo = res.data;
|
|
|
+ this.teamInfo.functionsName = res.data.functions.map(f=>f.functionName).join('、');
|
|
|
+ this.teamInfo.organizationsName = res.data.organizations.map(f=>f.orgName).join('、');
|
|
|
+ this.teamInfo.address = res.data.provinceName+res.data.cityName;
|
|
|
+ this.teamInfo.scaleName = this.teamScaleData.find(d=>d.id==res.data.scale).name;
|
|
|
+ this.teamInfo.hierarchyName = this.teamLevelData.find(d=>d.id==res.data.hierarchy).name;
|
|
|
+ this.teamShow = true;
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
+ .loading-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
.default_page{
|
|
|
padding: 0 0 192rpx;
|
|
|
background: #FFFFFF;
|
|
|
- box-sizing: border-box;
|
|
|
+ box-sizing: border-box;
|
|
|
+ height: 100vh;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
|
|
|
.top{
|
|
|
padding: 40rpx 24rpx 50rpx;
|
|
|
@@ -186,10 +392,12 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .list{
|
|
|
- flex: 1;
|
|
|
+ .list{
|
|
|
+ width: 100%;
|
|
|
padding: 0 24rpx;
|
|
|
- overflow-y: auto;
|
|
|
+ box-sizing: border-box;
|
|
|
+ // flex: 1;
|
|
|
+ // overflow-y: auto;
|
|
|
}
|
|
|
|
|
|
.bottom{
|