feat[survey]: NPS 内容调整
- NPS value使用 hooks 方式. - 解决 preview 的 NPS 组件无法调整的问题 - PreviewNPS 组件实现方式调整 - 增加 nps组件相应的类型文件
This commit is contained in:
@@ -30,7 +30,6 @@
|
|||||||
<p>{{ element.config.prompt_right }}</p>
|
<p>{{ element.config.prompt_right }}</p>
|
||||||
</div>
|
</div>
|
||||||
<RateCharacter
|
<RateCharacter
|
||||||
v-model="answerValue"
|
|
||||||
:config="element.config"
|
:config="element.config"
|
||||||
:index="optionIndex"
|
:index="optionIndex"
|
||||||
@change="handleRateChange"
|
@change="handleRateChange"
|
||||||
@@ -47,7 +46,7 @@ import { ref } from 'vue';
|
|||||||
import RateCharacter from './RateCharacter.vue';
|
import RateCharacter from './RateCharacter.vue';
|
||||||
|
|
||||||
const isPreview = defineModel('isPreview', { default: false, type: Boolean });
|
const isPreview = defineModel('isPreview', { default: false, type: Boolean });
|
||||||
const props = defineProps({
|
/*const props = */ defineProps({
|
||||||
index: {
|
index: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
@@ -60,11 +59,10 @@ const props = defineProps({
|
|||||||
questionType: { type: [String, Number], default: 4 }
|
questionType: { type: [String, Number], default: 4 }
|
||||||
});
|
});
|
||||||
|
|
||||||
// answer 的答案以 矩阵形式存储, 例如 [4,7],上层更新答案的时候也容易
|
/**
|
||||||
const rates = defineModel('rates', { default: [], type: Array });
|
* element === question
|
||||||
const rate = ref(0);
|
* @type {ModelRef<Object, string, Object, Object>}
|
||||||
const answerValue = ref();
|
*/
|
||||||
|
|
||||||
const element = defineModel('element', {
|
const element = defineModel('element', {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
default: () => {
|
||||||
@@ -72,29 +70,13 @@ const element = defineModel('element', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 不知道的 BUG ,开始的时候不能重置颜色。 故如此
|
|
||||||
setTimeout(() => {
|
|
||||||
rate.value = localStorage.getItem(props.sn);
|
|
||||||
// console.log(`rate value:`, rate.value);
|
|
||||||
// if (rates.value[0] !== undefined) {
|
|
||||||
// console.log(`rates value:`, rates.value);
|
|
||||||
// rate.value = rates.value[0]
|
|
||||||
// }
|
|
||||||
// else return
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param index {number} 索引
|
* @param index {number} 索引
|
||||||
* @param rate {number} 具体数值
|
* @param rate {number} 具体数值
|
||||||
*/
|
*/
|
||||||
function handleRateChange(index, rate) {
|
function handleRateChange(/* index, rate */) {
|
||||||
// 如果没有查询到对应索引的数值, 那么就直接push一个,直到有数值为止
|
// 占位
|
||||||
while (rates.value.length < index) {
|
|
||||||
rates.value.push(NaN);
|
|
||||||
}
|
|
||||||
rates.value[index] = rate;
|
|
||||||
localStorage.setItem(props.sn, rate.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const chooseId = ref('');
|
const chooseId = ref('');
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
import { value as model } from '@/views/Design/components/Questions/hooks/useNPSHooks';
|
||||||
|
|
||||||
const rateItem = ref([
|
const rateItem = ref([
|
||||||
{
|
{
|
||||||
@@ -52,10 +53,6 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const model = defineModel('model', {
|
|
||||||
type: Number
|
|
||||||
});
|
|
||||||
|
|
||||||
const index = defineModel('index', {
|
const index = defineModel('index', {
|
||||||
type: Number
|
type: Number
|
||||||
});
|
});
|
||||||
@@ -74,12 +71,14 @@ const renderScore = (min, max, interval) => {
|
|||||||
}
|
}
|
||||||
rateItem.value = result;
|
rateItem.value = result;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置颜色
|
// 重置颜色
|
||||||
function getItem(value) {
|
function getItem(value) {
|
||||||
model.value = value.label;
|
// console.log(value.label);
|
||||||
rateItem.value.forEach((item, index) => {
|
rateItem.value.forEach((item, index) => {
|
||||||
rateItem.value[index].active = item.label <= value.label;
|
rateItem.value[index].active = item.label <= value.label;
|
||||||
});
|
});
|
||||||
|
model.value = value.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
export const value = ref<number>(-1);
|
||||||
@@ -1208,6 +1208,174 @@ async function answer(callback, callbackBeforePage) {
|
|||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关联引用
|
||||||
|
function onRelation(
|
||||||
|
// 避免出现参数 undefined 情况
|
||||||
|
{ options, value, list } = {},
|
||||||
|
{ question_type: _questionType, question_index: _questionIndex, related, answer } = {}
|
||||||
|
) {
|
||||||
|
// 关联
|
||||||
|
related.forEach((relationItem) => {
|
||||||
|
let relationOptions = [];
|
||||||
|
if (_questionType === 9 || _questionType === 10) {
|
||||||
|
// 矩阵选择
|
||||||
|
list.forEach((item) => {
|
||||||
|
if (item.type === relationItem.cite_type) {
|
||||||
|
relationOptions = [...relationOptions, ...item.options];
|
||||||
|
}
|
||||||
|
if (relationItem.relation_type === 1) {
|
||||||
|
relationOptions = relationOptions.filter((option) =>
|
||||||
|
_questionType === 9 ? option.value : option.value?.length
|
||||||
|
);
|
||||||
|
} else if (relationItem.relation_type === 2) {
|
||||||
|
relationOptions = relationOptions.filter((option) =>
|
||||||
|
_questionType === 9 ? !option.value : !option.value?.length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (_questionType === 11) {
|
||||||
|
// 矩阵打分
|
||||||
|
list.forEach((item) => {
|
||||||
|
if (item.type === relationItem.cite_type) {
|
||||||
|
relationOptions = [...relationOptions, ...item.options];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (_questionType === 25 || _questionType === 26) {
|
||||||
|
// 热区题
|
||||||
|
relationOptions = options.filter((option) => {
|
||||||
|
if (relationItem.relation_type === 1) {
|
||||||
|
return option.status === 1;
|
||||||
|
} else if (relationItem.relation_type === 2) {
|
||||||
|
return option.status === 2;
|
||||||
|
} else if (relationItem.relation_type === 3) {
|
||||||
|
return !option.status;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else if (_questionType === 105) {
|
||||||
|
// MXD
|
||||||
|
options.forEach((currentOptions) => {
|
||||||
|
currentOptions.forEach((option) => {
|
||||||
|
const index = relationOptions.findIndex(
|
||||||
|
(relationOption) => relationOption.option_key === option.option_key
|
||||||
|
);
|
||||||
|
if (index === -1) {
|
||||||
|
// 全部项
|
||||||
|
if (relationItem.relation_type === 0) {
|
||||||
|
return relationOptions.push(option);
|
||||||
|
}
|
||||||
|
// 高相关
|
||||||
|
if (relationItem.relation_type === 3) {
|
||||||
|
return option.value === 'b' && relationOptions.push(option);
|
||||||
|
}
|
||||||
|
// 不相关
|
||||||
|
if (relationItem.relation_type === 4) {
|
||||||
|
return option.value === 'w' && relationOptions.push(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (relationItem.relation_type === 0) {
|
||||||
|
// 全部选项
|
||||||
|
relationOptions = options;
|
||||||
|
} else {
|
||||||
|
// 过滤选中/未选中选项
|
||||||
|
relationOptions = options.filter((option) => {
|
||||||
|
if (_questionType === 1) {
|
||||||
|
// 单选
|
||||||
|
if (relationItem.relation_type === 1) return value === option.option_key;
|
||||||
|
return value !== option.option_key;
|
||||||
|
} else if (_questionType === 2) {
|
||||||
|
// 多选
|
||||||
|
if (relationItem.relation_type === 1) return value.includes(option.option_key);
|
||||||
|
return !value.includes(option.option_key);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 找到关联题
|
||||||
|
const question = questionsData.value.questions.find(
|
||||||
|
(question) => question.question_index === relationItem.relation_question_index
|
||||||
|
);
|
||||||
|
// 深拷贝关联选项
|
||||||
|
const copyRelationOptions = JSON.parse(JSON.stringify(relationOptions));
|
||||||
|
// 更新关联选项key
|
||||||
|
copyRelationOptions.forEach((option) => {
|
||||||
|
if (option.option_key[0] !== 'Q') {
|
||||||
|
let letter = 'A';
|
||||||
|
// 矩阵题行、列
|
||||||
|
if (_questionType >= 9 && _questionType <= 11) {
|
||||||
|
letter = relationItem.cite_type === 1 ? 'R' : 'C';
|
||||||
|
}
|
||||||
|
option.option_key = `Q${_questionIndex}${letter}${option.option_key}`;
|
||||||
|
}
|
||||||
|
// 其他项特殊处理
|
||||||
|
if (option.is_other && option.value) {
|
||||||
|
option.is_other = 0;
|
||||||
|
option.option = option.value;
|
||||||
|
}
|
||||||
|
delete option.value;
|
||||||
|
delete option.status;
|
||||||
|
});
|
||||||
|
// 更新关联题列表
|
||||||
|
const relatedList = question.list.find(
|
||||||
|
(relatedListItem) => relatedListItem.relation_question_index === _questionIndex
|
||||||
|
);
|
||||||
|
relatedList.options = answer ? copyRelationOptions : [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function jumpImmediately() {
|
||||||
|
const code = questionsData.value.action?.code;
|
||||||
|
if (page.value !== pages.value.length + 1 && ![20004, 20011, 20016].includes(code)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const survey = questionsData.value.survey;
|
||||||
|
let countTime = 0;
|
||||||
|
let url = '';
|
||||||
|
|
||||||
|
if (code === 20004 && survey.screening_end_url_select && survey.screening_end_url) {
|
||||||
|
countTime = survey.screening_standing_time;
|
||||||
|
url = survey.screening_end_url;
|
||||||
|
}
|
||||||
|
if (code === 20011 && survey.success_end_url_select && survey.success_end_url) {
|
||||||
|
countTime = survey.success_standing_time;
|
||||||
|
url = survey.success_end_url;
|
||||||
|
}
|
||||||
|
if (code === 20016 && survey.quota_end_url_select && survey.quota_end_url) {
|
||||||
|
countTime = survey.quota_standing_time;
|
||||||
|
url = survey.quota_end_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转链接
|
||||||
|
if (countTime <= 0 && url) {
|
||||||
|
questionsData.value.action.code = -1 * code; // 防止 AnswerMob AnswerPc 组件里显示最后一页
|
||||||
|
|
||||||
|
url = url.replaceAll('#sn#', questionsData.value.answer.sn);
|
||||||
|
url = url.replaceAll('#user#', questionsData.value.answer.respondent);
|
||||||
|
url = url.replaceAll('#survey_sn#', questionsData.value.answer.survey_sn);
|
||||||
|
if (proxy.$route.query.source === 'YILI_APP_WANGYI') {
|
||||||
|
Object.keys(proxy.$route.query).forEach((key) => {
|
||||||
|
if (!['sn', 'source', 'is_template', 'channelUCode'].includes(key)) {
|
||||||
|
url += `${url.indexOf('?') === -1 ? '?' : '&'}${key}=${proxy.$route.query[key]}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 判断是否小程序路径
|
||||||
|
if (url[0] === '/') {
|
||||||
|
// 判断是否在小程序环境
|
||||||
|
wx.miniProgram.getEnv(() => {
|
||||||
|
wx.miniProgram.redirectTo({ url });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (url.indexOf('http://') === -1 && url.indexOf('https://') === -1) {
|
||||||
|
url = `http://${url}`;
|
||||||
|
}
|
||||||
|
open(url, '_self');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
::v-deep .van-cell::after {
|
::v-deep .van-cell::after {
|
||||||
|
|||||||
@@ -1,43 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-p-s v-model:element="question" v-model:rates="rates" :active="false" :isPreview="true" />
|
<n-p-s v-model:element="question" v-model:value="value" :active="false" :isPreview="true" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NPS from '@/views/Design/components/Questions/NPS.vue';
|
import NPS from '@/views/Design/components/Questions/NPS.vue';
|
||||||
import { ref, watch } from 'vue';
|
import { watch } from 'vue';
|
||||||
|
import { value } from '@/views/Design/components/Questions/hooks/useNPSHooks';
|
||||||
|
|
||||||
const question = defineModel<question>('question', { default: { config: { is_required: false } } });
|
|
||||||
const answer = defineModel<{ [key: string]: number }>('answer', { default: undefined });
|
|
||||||
|
|
||||||
// rates 数值取决与 answer , 没有数据重新建立一个对应长度的数组
|
|
||||||
const rates = ref(answer.value ? getRates() : new Array(question.value.list[0].options!.length));
|
|
||||||
// // 预览新增 emit ['changeAnswer', 'previous', 'next']
|
// // 预览新增 emit ['changeAnswer', 'previous', 'next']
|
||||||
const emit = defineEmits(['changeAnswer', 'previous', 'next', 'update:element']);
|
const emit = defineEmits(['changeAnswer', 'previous', 'next', 'update:element']);
|
||||||
|
|
||||||
// 获取 rates
|
const question = defineModel<question>('question', { default: { config: { is_required: false } } });
|
||||||
function getRates() {
|
|
||||||
const keys = Object.keys(answer.value);
|
/**
|
||||||
return keys.map((item) => {
|
* answer 的答案类型
|
||||||
return answer.value[item];
|
* {
|
||||||
});
|
* "1": 10
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
const answer = defineModel<NPSAnswerType>('answer', { default: undefined });
|
||||||
|
|
||||||
|
// 解析答案
|
||||||
|
// function parseAnswer() {
|
||||||
|
// return answer.value[`1`];
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成NPS答案
|
||||||
|
*/
|
||||||
|
function genAnswer(value: number): NPSAnswerType {
|
||||||
|
const res: any = {};
|
||||||
|
res[`1`] = value;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(answer.value && getRates());
|
watch(value, (newValue) => {
|
||||||
watch(
|
// console.log(genAnswer(newValue as number));
|
||||||
rates,
|
answer.value = genAnswer(newValue as number);
|
||||||
() => {
|
// 提交答案
|
||||||
const res = {};
|
emit('changeAnswer', answer.value);
|
||||||
rates.value.map((item, index) => {
|
});
|
||||||
// index 是 key, item 是 value
|
|
||||||
res[index + 1] = item;
|
|
||||||
});
|
|
||||||
answer.value = res;
|
|
||||||
emit('changeAnswer', res);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
1
src/views/Survey/views/Preview/components/questions/types/previewNPS.d.ts
vendored
Normal file
1
src/views/Survey/views/Preview/components/questions/types/previewNPS.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
type NPSAnswerType = { [key: string]: number };
|
||||||
Reference in New Issue
Block a user