Merge remote-tracking branch 'origin/feature/feature-20250331-h5' into feature/feature-20250331-h5

This commit is contained in:
Huangzhe
2025-03-13 09:38:22 +08:00
33 changed files with 1020 additions and 949 deletions

View File

@@ -1,5 +1,5 @@
# .env.development
VITE_APP_BASEURL=http://192.168.8.165:15011/
VITE_APP_BASEURL=https://yls-api-uat.dctest.digitalyili.com/
VITE_APP_ENV=development
VITE_APP_CURRENTMODE=dev
VITE_APP_BASEOSS=https://diaoyan-files.automark.cc

4
auto-imports.d.ts vendored
View File

@@ -5,4 +5,6 @@
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {}
declare global {
}

5
components.d.ts vendored
View File

@@ -8,6 +8,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
Contenteditable: typeof import('./src/components/contenteditable.vue')['default']
RichText: typeof import('./src/components/RichText.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
VanActionSheet: typeof import('vant/es')['ActionSheet']
@@ -16,18 +17,14 @@ declare module 'vue' {
VanCellGroup: typeof import('vant/es')['CellGroup']
VanCheckbox: typeof import('vant/es')['Checkbox']
VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
VanCol: typeof import('vant/es')['Col']
VanDivider: typeof import('vant/es')['Divider']
VanField: typeof import('vant/es')['Field']
VanGrid: typeof import('vant/es')['Grid']
VanGridItem: typeof import('vant/es')['GridItem']
VanIcon: typeof import('vant/es')['Icon']
VanNavBar: typeof import('vant/es')['NavBar']
VanPicker: typeof import('vant/es')['Picker']
VanPopup: typeof import('vant/es')['Popup']
VanRadio: typeof import('vant/es')['Radio']
VanRadioGroup: typeof import('vant/es')['RadioGroup']
VanRow: typeof import('vant/es')['Row']
VanSearch: typeof import('vant/es')['Search']
VanStepper: typeof import('vant/es')['Stepper']
VanSwitch: typeof import('vant/es')['Switch']

View File

@@ -1,9 +1,11 @@
<template>
<div v-if="isPreview"
v-html="nodes"
<div
v-if="isPreview"
style="min-width: 22px"
:class="isMobile ? 'mobile-text' : ''"
@click="onClick"></div>
@click="onClick"
v-html="nodes"
></div>
<div v-else v-html="nodes"></div>
</template>
@@ -32,8 +34,8 @@ export default defineComponent({
onClick(evt) {
const targetTagName = (evt.target || evt.srcElement).tagName || '';
if (targetTagName.toLowerCase() === 'img') {
evt.stopPropagation()
evt.cancelBubble = true
evt.stopPropagation();
evt.cancelBubble = true;
}
}
}

View File

@@ -49,7 +49,7 @@ const router = createRouter({
path: '/preview',
name: 'preview',
meta: {},
component: ()=> import ( '@/views/Survey/views/Preview/Index.vue')
component: () => import('@/views/Survey/views/Preview/Index.vue')
},
{
path: '/create',

View File

@@ -31,7 +31,7 @@ export const useQuestionStore = defineStore('questionStore', () => {
// 作用未知
const l = ref({});
// 主题颜色
// 主题颜色
const themeColor = ref({});
return {
@@ -44,7 +44,8 @@ export const useQuestionStore = defineStore('questionStore', () => {
loading,
prevLoading,
localPageTimer,
questions,showPage
questions,
showPage
};
});

View File

@@ -8,4 +8,3 @@ export const vFocus: Directive = {
el.focus();
}
};

View File

@@ -42,10 +42,10 @@ service.interceptors.request.use(
service.interceptors.response.use(
(response) => {
if (
response.status === 200 ||
response.status === 201 ||
response.status === 202 ||
response.status === 204
response.status === 200
|| response.status === 201
|| response.status === 202
|| response.status === 204
) {
if (response.config.method === 'put') {
// message.success('保存中...');

View File

@@ -335,6 +335,9 @@ const actionFun = {
// emit 事件
const saveQueItem = (logics, questions, survey) => {
// questions.map((item, index) => {
// item.title = index + 1;
// });
saveQuestion({
sn: route.query.sn,
data: {

View File

@@ -11,7 +11,12 @@
<div class="flex flex-start">操作选项</div>
</template>
<van-cell-group :border="false" class="ml10">
<van-cell title="此题必答" :border="false" label-align="left">
<van-cell
v-if="![6].includes(activeQuestion.question_type)"
title="此题必答"
:border="false"
label-align="left"
>
<template #right-icon>
<van-switch
v-model="activeQuestion.config.is_required"

View File

@@ -63,6 +63,7 @@
:active-value="1"
:inactive-value="0"
size="0.5rem"
@change="emit('saveOption')"
></van-switch>
</template>
</van-field>
@@ -71,7 +72,7 @@
<van-cell
v-if="![5, 6, 7].includes(actionQuestion.config.text_type)"
title="字数限制"
:title="`${[1, 2].includes(actionQuestion.config.text_type) ? '数值限制' : '字数限制'}`"
:border="false"
label-align="left"
>
@@ -130,6 +131,7 @@
v-model="actionQuestion.config.line_type"
icon-size="0.4rem"
direction="horizontal"
@change="emit('saveOption')"
>
<van-radio :name="0">单行</van-radio>
<van-radio :name="1">多行</van-radio>
@@ -165,7 +167,7 @@
label-align="left"
input-align="right"
class="action-field"
placeholder="不限"
placeholder=""
@blur="emit('saveOption')"
@update:model-value="
(value) => {
@@ -239,6 +241,7 @@ const selectText = (textType) => {
const confirm = ({ selectedValues }) => {
actionQuestion.value.config.text_type = Number(selectedValues[0]);
selectTextTypeModel.value = false;
emit('saveOption');
};
const actionQuestion = computed({

View File

@@ -17,7 +17,11 @@
></contenteditable>
</template>
<template #input>
<div contenteditable="true" class="input other_input"></div>
<textarea
class="other_input"
:placeholder="element.config.placeholder"
:rows="element.config.line_height"
></textarea>
</template>
</van-field>
</div>
@@ -45,7 +49,6 @@ const props = defineProps({
// 创建一个本地副本以保存更改
const emit = defineEmits(['update:element']);
const { element } = toRefs(props);
const emitValue = () => {
emit('update:element', element.value);
};
@@ -55,7 +58,7 @@ const emitValue = () => {
.cont {
.other_input {
width: 100%;
height: 40px;
min-height: 40px;
margin-bottom: 10px;
padding: 3px 5px;
border: 1px solid #ccc;

View File

@@ -13,13 +13,13 @@
@focusout="col.editor = false"
@click="handleRowNameChange(col.option!)"
/>
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"/>
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option" />
</td>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in rows" :key="rowIndex">
<th v-html="row.option"/>
<th v-html="row.option" />
<td v-for="(col, colIndex) in columns" :key="colIndex">
<input
type="checkbox"
@@ -34,18 +34,18 @@
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits } from 'vue';
import {vFocus} from '@/utils/directives/useVFocus';
import { defineProps } from 'vue';
import { vFocus } from '@/utils/directives/useVFocus';
const props = defineProps<{
rows: { option: string }[];
columns: { option: string, editor?: boolean }[];
columns: { option: string; editor?: boolean }[];
questionType: number;
matrixAnswer: { [key: string]: any };
rowRecord: (number | string)[];
rowRecord:(number | string)[];
}>();
const emits = defineEmits(['update:matrixAnswer', 'update:rowRecord']);
/* const emits = */ defineEmits(['update:matrixAnswer', 'update:rowRecord']);
const isOptionChecked = (rowIndex: number, colIndex: number): boolean => {
const key = `R${rowIndex + 1}_C${colIndex + 1}`;
@@ -57,31 +57,32 @@ const handleRowNameChange = (value: string) => {
// 你可以在这里添加其他逻辑
};
const handleColNameChange = (rowOption: string, colOption: string, e: Event) => {
const target = e.target as HTMLInputElement;
const col = props.columns.findIndex(option => option.option === colOption);
const row = props.rows.findIndex(option => option.option === rowOption);
if (props.questionType === 10) {
if (target.checked) {
props.rowRecord[col] = (props.rowRecord[col] || []).concat(row + 1);
} else {
props.rowRecord[col] = (props.rowRecord[col] || []).filter(item => item !== row + 1);
}
props.matrixAnswer = {};
props.rows.forEach((rowOption, rowIndex) => {
const colOptions = props.rowRecord[rowIndex];
if (colOptions) {
colOptions.forEach((col: any) => {
props.matrixAnswer[`R${rowIndex + 1}_C${col}`] = true;
});
}
});
}
emits('update:matrixAnswer', props.matrixAnswer);
emits('update:rowRecord', props.rowRecord);
};
// const handleColNameChange = (rowOption: string, colOption: string, e: Event) => {
// const target = e.target as HTMLInputElement;
// const col = props.columns.findIndex(option => option.option === colOption);
// const row = props.rows.findIndex(option => option.option === rowOption);
//
// if (props.questionType === 10) {
// if (target.checked) {
// props.rowRecord[col] = (props.rowRecord[col] || []).concat(row + 1);
// } else {
// props.rowRecord[col] = (props.rowRecord[col] || []).filter(item => item !== row + 1);
// }
// const newMatrixAnswer: { [key: string]: boolean } = {};
// const newRowRecord: (number | string)[] = [...props.rowRecord];
// props.rows.forEach((rowOption, rowIndex) => {
// const colOptions = newRowRecord[rowIndex];
// if (colOptions) {
// colOptions.forEach((col: any) => {
// newMatrixAnswer[`R${rowIndex + 1}_C${col}`] = true;
// });
// }
// });
// }
//
// emits('update:matrixAnswer', newMatrixAnswer);
// emits('update:rowRecord', newRowRecord);
// };
</script>
<style scoped lang="scss">

View File

@@ -35,45 +35,45 @@
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits } from 'vue';
import {vFocus} from '@/utils/directives/useVFocus';
import { defineProps } from 'vue';
import { vFocus } from '@/utils/directives/useVFocus';
let props = defineProps<{
const props = defineProps<{
rows: { option: string }[];
columns: { option: string, editor?: boolean }[];
columns: { option: string; editor?: boolean }[];
questionType: number;
matrixAnswer: { [key: string]: any };
rowRecord: (number | string)[];
rowRecord:(number | string)[];
}>();
const emits = defineEmits(['update:matrixAnswer', 'update:rowRecord']);
/* const emits = */ defineEmits(['update:matrixAnswer', 'update:rowRecord']);
const isOptionChecked = (rowIndex: number, colIndex: number): boolean => {
const key = `R${rowIndex + 1}_C${colIndex + 1}`;
return !!props.matrixAnswer[key];
};
const handleRowNameChange = (value: string) => {
console.log(`row change: ${value}`);
const handleRowNameChange = (/* value: string */) => {
// console.log(`row change: ${value}`);
// 你可以在这里添加其他逻辑
};
const handleColNameChange = (rowOption: string, colOption: string, e: Event) => {
const target = e.target as HTMLInputElement;
const col = props.columns.findIndex(option => option.option === colOption);
const row = props.rows.findIndex(option => option.option === rowOption);
if (props.questionType === 9) {
props.rowRecord[col] = row + 1;
props.matrixAnswer = {};
props.rowRecord.forEach((row, index) => {
props.matrixAnswer[`${index + 1}_${row}`] = 1;
});
}
emits('update:matrixAnswer', props.matrixAnswer);
emits('update:rowRecord', props.rowRecord);
};
// const handleColNameChange = (rowOption: string, colOption: string) => {
// // const target = e.target as HTMLInputElement;
// const col = props.columns.findIndex((option) => option.option === colOption);
// const row = props.rows.findIndex((option) => option.option === rowOption);
//
// if (props.questionType === 9) {
// props.rowRecord[col] = row + 1;
// props.matrixAnswer = {};
// props.rowRecord.forEach((row, index) => {
// props.matrixAnswer[`${index + 1}_${row}`] = 1;
// });
// }
//
// emits('update:matrixAnswer', props.matrixAnswer);
// emits('update:rowRecord', props.rowRecord);
// };
</script>
<style scoped lang="scss">

View File

@@ -33,24 +33,30 @@
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits } from 'vue';
import {vFocus } from "@/utils/directives/useVFocus"
import { defineProps, defineEmits } from 'vue';
import { vFocus } from '@/utils/directives/useVFocus';
const props = defineProps<{
rows: { option: string }[];
columns: { option: string, editor?: boolean }[];
columns: { option: string; editor?: boolean }[];
questionType: number;
matrixAnswer: { [key: string]: any };
}>();
const emits = defineEmits(['update:matrixAnswer']);
const handleRowNameChange = (value: string) => {
console.log(`row change: ${value}`);
const handleRowNameChange = (/* value: string */) => {
// console.log(`row change: ${value}`);
// 你可以在这里添加其他逻辑
};
const handleColNameChange = (rowOption: string, colOption: string, e: Event, rowIndex: number, colIndex: number) => {
const handleColNameChange = (
rowOption: string,
colOption: string,
e: Event,
rowIndex: number,
colIndex: number
) => {
const target = e.target as HTMLInputElement;
const key = `R${rowIndex + 1}_C${colIndex + 1}`;
const newMatrixAnswer = { ...props.matrixAnswer, [key]: target.value };

View File

@@ -10,12 +10,7 @@
{{ index + 1 }}
</template>
<template #label>
<div
:contenteditable="active"
class="van-field"
@blur="saveStem($event, element)"
v-html="element.stem"
></div>
<contenteditable v-model="element.stem" :active="active" @blur="saveStem"></contenteditable>
</template>
<template #input>
<div v-for="(optionItem, optionItemIndex) in element.options" :key="optionItemIndex">
@@ -38,7 +33,7 @@
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { ref } from 'vue';
import RateCharacter from './RateCharacter.vue';
const props = defineProps({
@@ -56,23 +51,11 @@ const props = defineProps({
sn: { type: String, default: '' },
questionType: { type: [String, Number], default: 4 }
});
const answerValue = ref()
// NPS 的答案
const NPSAnswer = ref({
'question_index': props.index,
'answer': {
'1': answerValue.value
}
});
const element = ref(props.element);
const { element } = toRefs(props);
const chooseId = ref('');
// 创建一个本地副本以保存更改
const localElement = ref({ ...props.element });
const saveStem = (e) => {
localElement.value.stem = e.target.innerHTML;
const emit = defineEmits(['update:element']);
const saveStem = () => {
emit('update:element', element.value);
};
const chooseOption = (item) => {

View File

@@ -11,15 +11,18 @@
{{ index + 1 }}
</template>
<template #label>
<contenteditable v-model="element.stem" :active="active"></contenteditable>
<!-- <div v-html="element.stem" v-else></div>-->
<contenteditable
v-model="element.stem"
:active="active"
@blur="emitValue"
></contenteditable>
</template>
</van-field>
</div>
</template>
<script setup>
import contenteditable from '@/components/contenteditable.vue';
import { ref } from 'vue';
import { toRefs } from 'vue';
const props = defineProps({
element: {
type: Object,
@@ -39,6 +42,11 @@ const props = defineProps({
}
});
const element = ref(props.element);
const { element } = toRefs(props);
const emit = defineEmits(['update:element']);
const emitValue = () => {
emit('update:element', element.value);
};
</script>
<style scoped lang="scss"></style>

View File

@@ -11,16 +11,18 @@
<!-- <img v-if="styleInfo.head_img_status && styleInfo.head_img_url" width="100%" class="head-img"-->
<!-- :src="styleInfo.head_img_url" />-->
<!-- LOGO -->
<van-cell-group>
</van-cell-group>
<van-cell-group> </van-cell-group>
<div
v-if="styleInfo.logo_status && styleInfo.logo_url"
class="example-logo"
:style="[
{
'justify-content':
styleInfo.logo_site === 1 ? 'flex-start' : styleInfo.logo_site === 2 ? 'center' : 'flex-end'
styleInfo.logo_site === 1
? 'flex-start'
: styleInfo.logo_site === 2
? 'center'
: 'flex-end'
},
{ 'padding-left': styleInfo.logo_site === 1 ? '20px' : '' },
{ 'padding-right': styleInfo.logo_site === 3 ? '20px' : '' },
@@ -52,8 +54,8 @@
/>
<!-- 问卷名和描述 -->
<q-first
isMobile
v-else-if="page === 0"
isMobile
:title="questionsData?.survey?.title"
:desc="questionsData?.survey?.introduction"
:questions="questionsData?.questions"
@@ -64,21 +66,21 @@
/>
<!-- -mobile -->
<question
v-for="question in questions"
v-else
:id="'questionIndex' + question.question_index"
:key="question.question_index"
class="question"
:tip="question.tip"
:stem="question.stem"
:title="question.title"
:error="question.error"
:warning="question.warning"
v-for="question in questions"
:key="question.question_index"
:questions="questionsData.questions"
:questionType="question.question_type"
:questionIndex="question.question_index"
:showTitle="styleInfo.is_question_number && true"
isMobile
:id="'questionIndex' + question.question_index"
:isAnswer="isAnswer"
>
<!-- 单选题 -->
@@ -476,11 +478,15 @@
"
:buttonTextColor="styleInfo.button_text_color"
:buttonColor="styleInfo.button_color"
@previous="previous"
@next="next"
:nextText="localPageTimer.is_show && localPageTimer.short_time ? `${localPageTimer.short_time}S` : ''"
:nextText="
localPageTimer.is_show && localPageTimer.short_time
? `${localPageTimer.short_time}S`
: ''
"
:nextDisabled="localPageTimer.short_time"
isMobile
@previous="previous"
@next="next"
/>
</div>
<LangTranslate
@@ -492,19 +498,9 @@
</div>
</template>
<script>
import questions from './js/questions';
import { defineComponent } from 'vue';
import LangTranslate from './components/LangTranslate.vue';
export default defineComponent({
components: { LangTranslate },
...questions
});
</script>
<script setup>
import { useTemplateRef, watch } from 'vue';
import { useTemplateRef } from 'vue';
import LangTranslate from './components/LangTranslate.vue';
import QLast from '@/views/Survey/views/Preview/components/questions/QLast.vue';
import Question from '@/views/Survey/views/Preview/components/questions/Question.vue';
import QFirst from '@/views/Survey/views/Preview/components/questions/QFirst.vue';
@@ -513,9 +509,13 @@ import { storeToRefs } from 'pinia';
import { useQuestionStore } from '@/stores/Questions/useQuestionStore';
import ProgressBar from './components/ProcessBar/Index.vue';
import { AnswerApi } from './js/api';
import { useRoute, useRouter } from 'vue-router';
import { useRoute } from 'vue-router';
import { getLanguage } from '@/views/Survey/views/Preview/js/language';
// export default defineComponent({
// components: { LangTranslate },
// ...questions
// });
// scrollbar
const scrollbar = useTemplateRef('scrollbar');
@@ -526,7 +526,7 @@ const {
page,
pages,
l,
themeColor, showPage,
showPage,
loading,
prevLoading,
localPageTimer,
@@ -573,7 +573,7 @@ async function getQuestions() {
// 获取 DOM 的内容
function getDomString(htmlString) {
if (typeof htmlString !== 'string') return
if (typeof htmlString !== 'string') return;
const match = htmlString.match(/<.*>(.*?)<\/.*>/);
if (match) return match[1];
}
@@ -629,9 +629,13 @@ async function answer(callback, callbackBeforePage) {
if (questionType === 10) {
// 矩阵多选题
if (question.config.is_change_row_cell) {
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(config.min_select || 1);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(
config.min_select || 1
);
} else {
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerLine(config.min_select || 1);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerLine(
config.min_select || 1
);
}
} else if (questionType === 12) {
// 图片显示题,如果开启了仅显示并且开启了单图显示时长,需要确保用户每一个图片都点开看过了才能下一页
@@ -645,7 +649,11 @@ async function answer(callback, callbackBeforePage) {
} else if (!question.error) {
question.error = translatedText.value.ThisIsARequiredQuestion;
}
} else if (answer && questionType === 1 && Object.keys(answer).findIndex((value) => !answer[value]) !== -1) {
} else if (
answer
&& questionType === 1
&& Object.keys(answer).findIndex((value) => !answer[value]) !== -1
) {
// 单选题
isError = true;
question.error = translatedText.value.PleaseInputAValue;
@@ -676,23 +684,28 @@ async function answer(callback, callbackBeforePage) {
// 选项分组最少选项数量
config.option_groups?.option_group.some((optionGroup) => {
const { min, title } = optionGroup;
const select = optionGroup.groups.filter((groups) => Object.keys(answer).includes(groups.option_key));
const select = optionGroup.groups.filter((groups) =>
Object.keys(answer).includes(groups.option_key)
);
if (title && select.length < min) {
// 判断隐藏选项
question.hideOptionsSet = new Set(question.hideOptions);
const options = question.list.flatMap((list) =>
list.options
.filter(({ option_key }) =>
.filter(({ option_key: optionKey }) =>
optionGroup.groups.some(
({ option_key: groupKey }) =>
groupKey === option_key && !question.hideOptionsSet.has(groupKey)
groupKey === optionKey && !question.hideOptionsSet.has(groupKey)
)
)
.map(({ option_key }) => option_key)
.map(({ option_key: optionKey }) => optionKey)
);
if (options.length >= min) {
isError = true;
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsInTitleGroup(min, title);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsInTitleGroup(
min,
title
);
}
}
return isError;
@@ -700,10 +713,18 @@ async function answer(callback, callbackBeforePage) {
}
} else if (answer && questionType === 10) {
// 矩阵多选题,列分组时,校验选项数量
const cellGroups = (config?.cell_option_groups?.option_group || []).filter((i) => i.groups?.length);
const cellGroups = (config?.cell_option_groups?.option_group || []).filter(
(i) => i.groups?.length
);
if (cellGroups.length) {
const rows = question.list.reduce((p, c) => [...p, ...(c.type === 1 ? c.options || [] : [])], []);
const cols = question.list.reduce((p, c) => [...p, ...(c.type === 2 ? c.options || [] : [])], []);
const rows = question.list.reduce(
(p, c) => [...p, ...(c.type === 1 ? c.options || [] : [])],
[]
);
const cols = question.list.reduce(
(p, c) => [...p, ...(c.type === 2 ? c.options || [] : [])],
[]
);
const freeCols = cols.filter(
(col) => !cellGroups.some((g) => g.groups.find((c) => c.option_key === col.option_key))
);
@@ -715,7 +736,9 @@ async function answer(callback, callbackBeforePage) {
});
});
});
arr.forEach((a, idx) => a.push(freeCols.map((col) => `${rows[idx].option_key}_${col.option_key}`)));
arr.forEach((a, idx) =>
a.push(freeCols.map((col) => `${rows[idx].option_key}_${col.option_key}`))
);
const answered = Object.keys(answer);
cellGroups.forEach((group, idx) => {
@@ -754,13 +777,17 @@ async function answer(callback, callbackBeforePage) {
}
return p;
},
question.list.filter((i) => i.type === 1).flatMap((i) => i.options.map((i) => 0))
question.list.filter((i) => i.type === 1).flatMap((i) => i.options.map(() => 0))
);
if (minSelect && minSelect > Math.max(...perLineSelectedCount)) {
if (question.config.is_change_row_cell) {
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(config.min_select || 1);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(
config.min_select || 1
);
} else {
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerLine(config.min_select || 1);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerLine(
config.min_select || 1
);
}
isError = true;
}
@@ -798,19 +825,21 @@ async function answer(callback, callbackBeforePage) {
question.error = '';
// 填空题
const { value } = answer;
let newValue = value.replace(/\n|\r|\r\n/g, '');
const newValue = value.replace(/\n|\r|\r\n/g, '');
switch (config.text_type) {
case 3: // 字母
isError = config.include_mark == 1
? !/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]]+$/.test(
isError
= config.include_mark === 1
? !/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]]+$/.test(
newValue
) || !newValue.length
: !/^[a-zA-Z]+$/.test(newValue) || !newValue.length;
question.error = isError ? translatedText.value.PleaseEnterEnglishLetters : '';
break;
case 4: // 中文
isError = config.include_mark == 1
? !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]])+$/.test(
isError
= config.include_mark === 1
? !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]])+$/.test(
newValue
) || !newValue.length
: !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/.test(
@@ -819,7 +848,8 @@ async function answer(callback, callbackBeforePage) {
question.error = isError ? translatedText.value.PleaseEnterChineseWords : '';
break;
case 5: // 邮箱
isError = !/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
isError
= !/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
);
question.error = isError ? translatedText.value.PleaseEnterACorrectEmail : '';
@@ -829,7 +859,8 @@ async function answer(callback, callbackBeforePage) {
question.error = isError ? translatedText.value.PleaseEnterACorrectPhone : '';
break;
case 7: // 身份证号
isError = !/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
isError
= !/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
value
);
question.error = isError ? translatedText.value.PleaseEnterACorrectID : '';
@@ -849,37 +880,44 @@ async function answer(callback, callbackBeforePage) {
switch (config.text_type) {
case 3: // 字母
if (
!/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]]+$/.test(
!/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]]+$/.test(
value
)
)
) {
question.error = translatedText.value.PleaseEnterEnglishLetters;
}
break;
case 4: // 中文
if (
!/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]])+$/.test(
!/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]])+$/.test(
value
)
)
) {
question.error = translatedText.value.PleaseEnterChineseWords;
}
break;
case 5: // 邮箱
if (
!/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
)
)
) {
question.error = translatedText.value.PleaseEnterACorrectEmail;
}
break;
case 6: // 手机号
if (!/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value))
if (!/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value)) {
question.error = translatedText.value.PleaseEnterACorrectPhone;
}
break;
case 7: // 身份证号
if (
!/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(value)
!/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
value
)
) {
question.error = translatedText.value.PleaseEnterACorrectID;
}
break;
default:
break;
@@ -937,89 +975,93 @@ async function answer(callback, callbackBeforePage) {
});
});
}
// 质量控制(选择题)
const allPages = pages.value.flatMap((currentPages) => currentPages);
const allQuestions = allPages.map((questionIndex) => {
return questionsData.value.questions.find((question) => question.question_index === questionIndex);
});
const repeat = questionsData.value.survey.repeat_list?.find((repeat) =>
repeat.question_indexes.find(({ first_index, last_index }) => {
const firstIndex = allPages.findIndex((questionIndex) => questionIndex === first_index);
const lastIndex = allPages.findIndex((questionIndex) => questionIndex === last_index);
const currentQuestions = allQuestions
.slice(firstIndex, lastIndex + 1)
.filter((currentQuestions) => currentQuestions.question_type === repeat.question_type);
const answerIndexes = currentQuestions.map((question) => question.answerIndex);
return hasNConsecutiveNumbers(
answerIndexes,
repeat.allow_repeat_num + 1,
(warnStart, warnEnd) => {
currentQuestions.forEach((question, index) => {
if (index >= warnStart && index < warnEnd) {
if (repeat.repeat_type) {
question.warning = translatedText.value.TheAnswerIsRepeatedMoreThanOneTimesPleaseRevise(
repeat.allow_repeat_num,
repeat.repeat_type
);
} else {
question.error = translatedText.value.TheAnswerIsRepeatedMoreThanOneTimesPleaseRevise(
repeat.allow_repeat_num,
repeat.repeat_type
);
}
} else {
question.warning = '';
question.error = '';
}
});
},
() => {
currentQuestions.forEach((question) => {
question.warning = '';
});
}
);
})
);
if (repeat) {
await new Promise((resolve) => {
Modal[repeat.repeat_type ? 'confirm' : 'info']({
class: 'custom-modal custom-modal-title-notice',
title: translatedText.value.PleaseAnswerCarefully,
content: repeat.alert_text,
cancelText: translatedText.value.ContinueAnswer,
okText: translatedText.value.ReviseAnswer,
onCancel: () => {
questions.value.forEach((question) => (question.answerIndex = ''));
resolve();
}
});
});
}
// 判断是作答还是预览
if (!props.isAnswer) {
loading.value = true;
try {
// 模拟接口
const data = answerMock(questionsData.value, page.value);
console.log('模拟作答数据', data);
// 更新答案
updateAnswer(data.answer_info_autofill);
// 更新分页数组
questionsData.value.answer.pages = data.pages;
// 选项隐藏
hideOptions(data.hide_options);
// 更新action
questionsData.value.action = data.action;
if ([20004, 20011, 20016].includes(data.action.code)) {
return (page.value = pages.value.length + 1);
}
callback(data.jump_to);
} catch (error) {
console.log(error);
}
return (loading.value = false);
}
// // 质量控制(选择题)
// const allPages = pages.value.flatMap((currentPages) => currentPages);
// const allQuestions = allPages.map((questionIndex) => {
// return questionsData.value.questions.find(
// (question) => question.question_index === questionIndex
// );
// });
// const repeat = questionsData.value.survey.repeat_list?.find((repeat) =>
// repeat.question_indexes.find(({ first_index: firstIndex, last_index: lastIndex }) => {
// const firstIndex = allPages.findIndex((questionIndex) => questionIndex === firstIndex);
// const lastIndex = allPages.findIndex((questionIndex) => questionIndex === lastIndex);
// const currentQuestions = allQuestions
// .slice(firstIndex, lastIndex + 1)
// .filter((currentQuestions) => currentQuestions.question_type === repeat.question_type);
// const answerIndexes = currentQuestions.map((question) => question.answerIndex);
// return hasNConsecutiveNumbers(
// answerIndexes,
// repeat.allow_repeat_num + 1,
// (warnStart, warnEnd) => {
// currentQuestions.forEach((question, index) => {
// if (index >= warnStart && index < warnEnd) {
// if (repeat.repeat_type) {
// question.warning
// = translatedText.value.TheAnswerIsRepeatedMoreThanOneTimesPleaseRevise(
// repeat.allow_repeat_num,
// repeat.repeat_type
// );
// } else {
// question.error
// = translatedText.value.TheAnswerIsRepeatedMoreThanOneTimesPleaseRevise(
// repeat.allow_repeat_num,
// repeat.repeat_type
// );
// }
// } else {
// question.warning = '';
// question.error = '';
// }
// });
// },
// () => {
// currentQuestions.forEach((question) => {
// question.warning = '';
// });
// }
// );
// })
// );
// if (repeat) {
// await new Promise((resolve) => {
// Modal[repeat.repeat_type ? 'confirm' : 'info']({
// class: 'custom-modal custom-modal-title-notice',
// title: translatedText.value.PleaseAnswerCarefully,
// content: repeat.alert_text,
// cancelText: translatedText.value.ContinueAnswer,
// okText: translatedText.value.ReviseAnswer,
// onCancel: () => {
// questions.value.forEach((question) => (question.answerIndex = ''));
// resolve();
// }
// });
// });
// }
// // 判断是作答还是预览
// if (!props.isAnswer) {
// loading.value = true;
// try {
// // 模拟接口
// const data = answerMock(questionsData.value, page.value);
// console.log('模拟作答数据', data);
// // 更新答案
// updateAnswer(data.answer_info_autofill);
// // 更新分页数组
// questionsData.value.answer.pages = data.pages;
// // 选项隐藏
// hideOptions(data.hide_options);
// // 更新action
// questionsData.value.action = data.action;
// if ([20004, 20011, 20016].includes(data.action.code)) {
// return (page.value = pages.value.length + 1);
// }
// callback(data.jump_to);
// } catch (error) {
// console.log(error);
// }
// return (loading.value = false);
// }
// 表单验证通过,开始答题
const cycle = [];
const questionsAnswer = [];
@@ -1110,7 +1152,7 @@ async function answer(callback, callbackBeforePage) {
loading.value = false;
} else {
console.log(errors);
const { error, title, question_index } = errors[0];
const { error, title, question_index: questionIndex } = errors[0];
const lines = (error || '')
.split('\n')
.filter((i) => !!i)
@@ -1118,7 +1160,7 @@ async function answer(callback, callbackBeforePage) {
msg.error(lines.join('\n'));
// 锚点
const anchor = document.querySelector(`#questionIndex${question_index}`);
const anchor = document.querySelector(`#questionIndex${questionIndex}`);
console.log(anchor, scrollbar.value);
scrollbar.value.scrollTo(0, anchor.offsetTop - (props.isMobile ? 20 : 40));
}
@@ -1179,118 +1221,121 @@ function jumpImmediately() {
}
}
// 关联引用
function onRelation({ options, value, list }, { question_type, question_index, related, answer }) {
// 关联
related.forEach((relationItem) => {
let relationOptions = [];
if (question_type === 9 || question_type === 10) {
// 矩阵选择
list.forEach((item) => {
if (item.type === relationItem.cite_type) {
relationOptions = [...relationOptions, ...item.options];
}
if (relationItem.relation_type === 1) {
relationOptions = relationOptions.filter((option) =>
question_type === 9 ? option.value : option.value?.length
);
} else if (relationItem.relation_type === 2) {
relationOptions = relationOptions.filter((option) =>
question_type === 9 ? !option.value : !option.value?.length
);
}
});
} else if (question_type === 11) {
// 矩阵打分
list.forEach((item) => {
if (item.type === relationItem.cite_type) {
relationOptions = [...relationOptions, ...item.options];
}
});
} else if (question_type === 25 || question_type === 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 (question_type === 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 (question_type === 1) {
// 单选
if (relationItem.relation_type === 1) return value === option.option_key;
return value !== option.option_key;
} else if (question_type === 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 (question_type >= 9 && question_type <= 11) {
letter = relationItem.cite_type === 1 ? 'R' : 'C';
}
option.option_key = `Q${question_index}${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 === question_index
);
relatedList.options = answer ? copyRelationOptions : [];
});
}
// // 关联引用
// function onRelation(
// { options, value, list },
// { question_type: questionType, question_index, 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${question_index}${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 === question_index
// );
// relatedList.options = answer ? copyRelationOptions : [];
// });
// }
</script>
<style lang="scss" scoped>
.main {

View File

@@ -6,12 +6,10 @@
import { defineComponent } from 'vue';
import { BrowsingRecordApi } from '../js/api';
const has3dPages = ['/answer'];
export default defineComponent({
components: {
},
components: {},
props: {
isStemMiddle: {
type: Boolean,
@@ -51,56 +49,6 @@ export default defineComponent({
default: false
}
},
computed: {
canUse3D() {
return this.config.is_three_dimensions && has3dPages.includes(this.$route.path);
},
surveyId() {
return this.config.scene;
},
// scene() {
// return this.config.scene_information;
// },
shelves() {
if (!this.scene) return [];
return this.scene.shelves || [];
},
shelf() {
if (!this.config.shelf) return this.shelves[0];
return this.shelves.find((x) => x.planetid == this.config.shelf);
},
wares() {
if (!this.shelf) return [];
return this.shelf.wares;
},
ware() {
return this.wares.find((x) => x.planetid == this.config.ware);
},
options() {
var options = [];
try {
this.question.list.forEach((item) => {
item.options.forEach((option) => {
// question_index为0代表当前题型为了支持复制题时引用的question_index错误的问题
var question_index = item.relation_question_index || 0;
options.push({
...option,
question_index
});
});
});
if (this.question.hideOptions) {
options = options.filter((option) => !this.question.hideOptions.includes(option.option_key));
}
} catch (e) {
return [];
}
return options;
},
defaultWare() {
return this.ware;
}
},
data() {
return {
scene: null,
@@ -140,6 +88,58 @@ export default defineComponent({
showTimeTimeoutId: 0
};
},
computed: {
canUse3D() {
return this.config.is_three_dimensions && has3dPages.includes(this.$route.path);
},
surveyId() {
return this.config.scene;
},
// scene() {
// return this.config.scene_information;
// },
shelves() {
if (!this.scene) return [];
return this.scene.shelves || [];
},
shelf() {
if (!this.config.shelf) return this.shelves[0];
return this.shelves.find((x) => x.planetid === this.config.shelf);
},
wares() {
if (!this.shelf) return [];
return this.shelf.wares;
},
ware() {
return this.wares.find((x) => x.planetid === this.config.ware);
},
options() {
let options = [];
try {
this.question.list.forEach((item) => {
item.options.forEach((option) => {
// question_index为0代表当前题型为了支持复制题时引用的question_index错误的问题
const questionIndex = item.relation_question_index || 0;
options.push({
...option,
question_index: questionIndex
});
});
});
if (this.question.hideOptions) {
options = options.filter(
(option) => !this.question.hideOptions.includes(option.option_key)
);
}
} catch (e) {
return [];
}
return options;
},
defaultWare() {
return this.ware;
}
},
beforeUnmount() {
this.pager.clear();
if (this.showTimeTimeoutId) clearTimeout(this.showTimeTimeoutId);
@@ -165,7 +165,7 @@ export default defineComponent({
// 解决缓存问题,答卷时加载场景信息
if (!this.scene) {
var res = await BrowsingRecordApi.getSurveysScene({
const res = await BrowsingRecordApi.getSurveysScene({
sn: this.$route.query.sn,
question_index: this.question.question_index
});
@@ -218,7 +218,7 @@ export default defineComponent({
},
onLoadingCompletion() {
this.shopData.shelves.forEach((shelf) => {
var wares = shelf.wares;
let wares = shelf.wares;
// 选项随机
if (this.options && this.options.length && this.config.is_binding_goods) {
@@ -226,13 +226,15 @@ export default defineComponent({
.filter((option) => !option.is_other)
.map((option) => {
return this.wares.find(
(ware) => ware.question_index == option.question_index && ware.option_index == option.option_index
(ware) =>
ware.question_index === option.question_index
&& ware.option_index === option.option_index
);
});
wares = wares.filter((x) => x);
}
if (shelf.layoutType == 'multiCols') {
if (shelf.layoutType === 'multiCols') {
shelf.cells = shelf.cells.map((cell, index) => {
return {
...cell,
@@ -271,19 +273,19 @@ export default defineComponent({
this.sceneAction = {
action: 'hold_to_cart'
};
var hold = this.hold;
const hold = this.hold;
this.hold = null;
var ware = this.wares.find((ware) => ware.planetid == hold.data.surveyWare.id);
const ware = this.wares.find((ware) => ware.planetid === hold.data.surveyWare.id);
if (!ware) return;
var option = null;
let option = null;
this.question.list.forEach((qgroup) => {
// 获取question_index考虑到关联选项
var question_index = qgroup.relation_question_index || 0;
if (question_index != ware.question_index) return;
const questionIndex = qgroup.relation_question_index || 0;
if (questionIndex !== ware.question_index) return;
qgroup.options.forEach((qoption) => {
if (qoption.option_index != ware.option_index) return;
if (qoption.option_index !== ware.option_index) return;
option = qoption;
});
});

View File

@@ -4,9 +4,9 @@
:shopData="shopData"
:surveyId="surveyId"
:hidden="true"
@onLoadingCompletion="onLoadingCompletion"
:page="page"
:page_shelves="page_shelves"
@on-loading-completion="onLoadingCompletion"
/>
</template>
@@ -52,7 +52,7 @@ export default {
// 3D资源预获取
try {
var target = arr.find((question) => question?.config?.is_three_dimensions);
const target = arr.find((question) => question?.config?.is_three_dimensions);
this.first3DQuestion = target;
if (!target) return;
@@ -61,7 +61,7 @@ export default {
if (!this.sceneInformation) {
// 解决缓存问题,答卷时加载场景信息
var res = await BrowsingRecordApi.getSurveysScene({
const res = await BrowsingRecordApi.getSurveysScene({
sn: this.$route.query.sn,
question_index: target.question_index
});
@@ -84,15 +84,15 @@ export default {
async onLoadingCompletion() {
// 3D资源预获取
try {
var target = this.first3DQuestion;
const target = this.first3DQuestion;
if (!target) return;
var scene = this.sceneInformation;
const scene = this.sceneInformation;
if (!scene) return;
scene.shelves.forEach((shelf) => {
var wares = shelf.wares;
const wares = shelf.wares;
shelf.cells = shelf.cells.map((cell, index) => {
return {
...cell,

View File

@@ -21,7 +21,7 @@ const props = defineProps({
fullText: { type: String, default: '' }
});
const {questionsData : data } = storeToRefs(useQuestionStore())
const { questionsData: data } = storeToRefs(useQuestionStore());
const text = computed(() => {
if (props.fullText) {
return props.fullText.split('\n');

View File

@@ -8,8 +8,8 @@
>
<div
class="pfe-button btn previous my-btn"
@click="previous"
:style="`color: ${buttonTextColor};background-color: ${buttonColor}`"
@click="previous"
>
<i class="iconfont">&#xe67f;</i>
<span>{{ prevButtonText }}</span>
@@ -19,8 +19,8 @@
<div
class="pfe-button btn next my-btn"
:class="nextDisabled ? 'disabled' : ''"
@click="next"
:style="`color: ${buttonTextColor};background-color: ${buttonColor}`"
@click="next"
>
<span>{{
showStart && page === 0
@@ -37,16 +37,10 @@
<script>
import { computed, defineComponent } from 'vue';
import {storeToRefs} from 'pinia';
import PfeButton from './PfeButton.vue';
import { storeToRefs } from 'pinia';
import { useQuestionStore } from '@/stores/Questions/useQuestionStore';
// 引入 Spin 组件
import { Spin as ASpin } from 'ant-design-vue';
export default defineComponent({
components: { PfeButton },
props: {
// 当前页数
page: {
@@ -123,10 +117,14 @@ export default defineComponent({
}
},
setup(props, context) {
const {questionsData} = storeToRefs(useQuestionStore())
const { questionsData } = storeToRefs(useQuestionStore());
const prevButtonText = computed(() => questionsData.value?.survey?.style?.up_button_text || '上一页');
const nextButtonText = computed(() => questionsData.value?.survey?.style?.next_button_text || '一页');
const prevButtonText = computed(
() => questionsData.value?.survey?.style?.up_button_text || '一页'
);
const nextButtonText = computed(
() => questionsData.value?.survey?.style?.next_button_text || '下一页'
);
// 上一页
function previous() {

View File

@@ -1,14 +1,14 @@
<template>
<div class="remark-section">
<div class="icon" v-if="isPreview">
<div v-if="isPreview" class="icon">
<i class="iconfont icon-pinglun" @click="openDrawer"></i>
</div>
</div>
</template>
<script>
import { defineComponent, ref, computed } from 'vue';import { useRoute, useRouter } from 'vue-router';
import {storeToRefs} from 'pinia';
import { defineComponent, ref, computed } from 'vue'; import { useRoute, useRouter } from 'vue-router';
import { storeToRefs } from 'pinia';
// import { useStore } from 'vuex';
// import {} from "@/stores/counter.js"
@@ -34,7 +34,7 @@ export default defineComponent({
default: undefined
}
},
setup(props) {
setup() {
const { visible } = storeToRefs(useCommonStore());
const route = useRoute();
const router = useRouter();
@@ -50,7 +50,7 @@ export default defineComponent({
});
return;
}
const title = props.title.trim();
// const title = props.title.trim();
visible.value = true;
}

View File

@@ -8,7 +8,7 @@
:class="isMobile ? 'm-title' : ''"
:style="`color: ${themeColor.stemColor}`"
/>
<Remark :title="label + '标题评论'" :type="1" v-if="!isAnswer" />
<Remark v-if="!isAnswer" :title="label + '标题评论'" :type="1" />
</div>
<div v-if="showDesc" class="desc-part" :class="isMobile ? 'm-desc-part' : ''">
<rich-text
@@ -17,8 +17,8 @@
:class="isMobile ? 'm-desc' : 'desc'"
:style="`color: ${themeColor.stemColor}`"
/>
<div style="margin-top: 32px" v-if="desc">
<Remark :title="label + '介绍语评论'" :type="2" v-if="!isAnswer" />
<div v-if="desc" style="margin-top: 32px">
<Remark v-if="!isAnswer" :title="label + '介绍语评论'" :type="2" />
</div>
</div>
<!-- <AnswerViewerPrefetch :questions="questions" />-->
@@ -30,12 +30,12 @@ import { defineComponent } from 'vue';
import RichText from '@/components/RichText.vue';
import Remark from '../../components/Remark/index.vue';
import AnswerViewerPrefetch from '../AnswerViewerPrefetch.vue';
// import AnswerViewerPrefetch from '../AnswerViewerPrefetch.vue';
import { useQuestionStore } from '@/stores/Questions/useQuestionStore';
import { storeToRefs } from 'pinia';
export default defineComponent({
components: { RichText, Remark, AnswerViewerPrefetch },
components: { RichText, Remark /* AnswerViewerPrefetch */ },
props: {
title: {
type: String,

View File

@@ -8,9 +8,9 @@
<div v-else-if="code === 20016" v-html="survey.quota_end_content" />
</div>
<div v-if="!isAnswer" class="comment">
<!-- <Remark v-if="code === 20004" title="结束语提前结束评论" :type="6" />-->
<!-- <Remark v-if="code === 20011" title="结束语成功完成评论" :type="5" />-->
<!-- <Remark v-if="code === 20016" title="配额超限页评论" :type="8" />-->
<!-- <Remark v-if="code === 20004" title="结束语提前结束评论" :type="6" />-->
<!-- <Remark v-if="code === 20011" title="结束语成功完成评论" :type="5" />-->
<!-- <Remark v-if="code === 20016" title="配额超限页评论" :type="8" />-->
</div>
<!-- 立即跳转动画 -->
<div v-if="isEndUrl" class="animation-wrapper">
@@ -28,7 +28,12 @@
<LangTranslate v-else translate-key="QLast_IosRedirectingPrompt" />
</div>
<div class="m-bottom">
<LangTranslate v-if="countTime > 0" translate-key="QLast_Stay" class="m-btn border-right" @click="stopJump" />
<LangTranslate
v-if="countTime > 0"
translate-key="QLast_Stay"
class="m-btn border-right"
@click="stopJump"
/>
<LangTranslate translate-key="QLast_Redirect" class="m-btn" @click="jump" />
</div>
</div>
@@ -46,20 +51,26 @@
</template>
<script>
import { computed, defineComponent, getCurrentInstance, ref, inject, onMounted } from 'vue';
import { computed, defineComponent, getCurrentInstance, ref, onMounted } from 'vue';
// import Remark from '@/views/Survey/views/components/Remark/index.vue';
// 屏蔽动效库
// import lottie from 'lottie-web';
import redJson from '@/views/Survey/views/Preview/components/json/red.json';
// import lottieJson from './json/lottie.json';
import LangTranslate from '../LangTranslate.vue';
import {storeToRefs} from 'pinia';
import { useQuestionStore} from "@/stores/Questions/useQuestionStore"
import { storeToRefs } from 'pinia';
import { useQuestionStore } from '@/stores/Questions/useQuestionStore';
export default defineComponent({
components: {
// Remark,
LangTranslate
},
props: {
action: {
type: Object,
default: () => {}
default: () => {
/* 占位 */
}
},
code: {
type: Number,
@@ -67,7 +78,9 @@ export default defineComponent({
},
survey: {
type: Object,
default: () => {}
default: () => {
/* 占位 */
}
},
// 是否答题模式
isAnswer: {
@@ -76,14 +89,10 @@ export default defineComponent({
},
isTemplate: { type: Boolean, default: false }
},
components: {
// Remark,
LangTranslate
},
setup(props) {
const { proxy } = getCurrentInstance();
// const questionsData = inject('questionsData'); // 问卷数据
const {questionsData} = storeToRefs(useQuestionStore());
const { questionsData } = storeToRefs(useQuestionStore());
console.log(11, questionsData, props.survey);
let countTimer; // 跳转计时器
const animation = ref(redJson); // 立即跳转动画
@@ -93,7 +102,9 @@ export default defineComponent({
const isEndUrl = computed(() => {
const code = props.action ? props.action.code : props.code;
return (
(code === 20004 && props.survey.screening_end_url_select && props.survey.screening_end_url)
(code === 20004
&& props.survey.screening_end_url_select
&& props.survey.screening_end_url)
|| (code === 20011 && props.survey.success_end_url_select && props.survey.success_end_url)
|| (code === 20016 && props.survey.quota_end_url_select && props.survey.quota_end_url)
);
@@ -255,6 +266,7 @@ export default defineComponent({
justify-content: center;
height: 100%;
background-color: rgba(1, 21, 53, 0.7);
//filter: alpha(opacity=50);
.red-animation {

View File

@@ -1,7 +1,10 @@
<template>
<a-radio-group v-model:value="value" @change="changeValue" :disabled="disabled" v-if="customRadio">
<a-radio-group
v-if="customRadio" v-model:value="value" :disabled="disabled"
@change="changeValue"
>
<div class="radio-group">
<div class="group" v-for="(group, groupIndex) in optionGroups" :key="groupIndex">
<div v-for="(group, groupIndex) in optionGroups" :key="groupIndex" class="group">
<div
v-if="group.title && !config.option_groups?.is_hide"
v-show="showGroupTitle(group.options)"
@@ -10,12 +13,14 @@
{{ group.title }}
</div>
<div
v-for="option in group.options"
v-show="!hideOptions.includes(option.option_key)"
:key="option.option_key"
class="radio theme-hover-default"
:class="group.title && !config.option_groups?.is_hide ? 'margin-left' : ''"
v-for="option in group.options"
:key="option.option_key"
:style="isMobile ? 'width: 100%' : `width: calc(100% / ${config.each_number || 1} - 33px)`"
v-show="!hideOptions.includes(option.option_key)"
:style="
isMobile ? 'width: 100%' : `width: calc(100% / ${config.each_number || 1} - 33px)`
"
@click="onChangeValue(option.option_key)"
>
<a-radio :value="option.option_key" @click.stop />
@@ -25,30 +30,29 @@
<a-input
v-if="option.is_other"
v-model:value="option.value"
@change="changeInput($event, option.option_key)"
:disabled="disabled"
@change="changeInput($event, option.option_key)"
/>
</div>
</div>
</div>
</a-radio-group>
<choice :element="question" v-else />
<choice v-else :element="question" />
</template>
<script setup>
import Choice from '@/views/Design/components/Questions/Choice.vue';
const customRadio = true;
</script>
<script>
import RichText from '@/components/RichText.vue';
import { computed, defineComponent, ref, watch } from 'vue';
import AnswerViewer from '@/views/Survey/views/Preview/AnswerViewer.vue';
import { compareArrayByField, randomOptions } from '@/utils/utils.js';
const customRadio = true;
export default defineComponent({
components: { RichText, AnswerViewer },
components: { RichText /* AnswerViewer */ },
props: {
// 题干
stem: {
@@ -64,12 +68,14 @@ export default defineComponent({
config: {
type: Object,
default: () => {
/* 占位 */
}
},
// 答案
answer: {
type: Object,
default: () => {
/* 占位 */
}
},
// 答案索引
@@ -223,7 +229,7 @@ export default defineComponent({
const index = optionGroups.value
.flatMap((group) => group.options.map((option) => option))
.findIndex((option) => option.option_key === value.value);
context.emit('update:answerIndex', index + '');
context.emit('update:answerIndex', `${index}`);
} else if (props.answerIndex) {
context.emit('update:answerIndex', '');
}
@@ -263,7 +269,10 @@ export default defineComponent({
}
// 清空值和答案
if (value.value && options.value.findIndex((option) => option.option_key === value.value) === -1) {
if (
value.value
&& options.value.findIndex((option) => option.option_key === value.value) === -1
) {
// 清空值
value.value = '';
// 清空答案
@@ -279,7 +288,10 @@ export default defineComponent({
watch(
() => options.value,
(val, oldVal) => {
if (compareArrayByField(val, oldVal || [], 'option_key') && compareArrayByField(val, oldVal || [], 'option')) {
if (
compareArrayByField(val, oldVal || [], 'option_key')
&& compareArrayByField(val, oldVal || [], 'option')
) {
return;
}
setOptionGroups();

View File

@@ -1,7 +1,7 @@
<template>
<div class="question">
<!-- 高级题型不显示 -->
<div class="question-inner-wrapper" v-if="questionType <= 100">
<div v-if="questionType <= 100" class="question-inner-wrapper">
<div class="title" :style="`color: ${themeColor.stemColor}`">
<span v-if="showTitle" class="question-inner-span" v-html="title"></span>
<!-- question.stem -->
@@ -9,13 +9,13 @@
<rich-text :nodes="`${newTitle}${tip}`" isPreview :isMobile="isMobile" />
</div>
</div>
<!-- <Remark-->
<!-- :title="title + '问题评论'"-->
<!-- :type="3"-->
<!-- :questionIndex="questionIndex"-->
<!-- v-if="!isAnswer"-->
<!-- style="margin-bottom: 22px"-->
<!-- />-->
<!-- <Remark-->
<!-- :title="title + '问题评论'"-->
<!-- :type="3"-->
<!-- :questionIndex="questionIndex"-->
<!-- v-if="!isAnswer"-->
<!-- style="margin-bottom: 22px"-->
<!-- />-->
</div>
<LangTranslate v-if="error && questionType <= 100" :full-text="error" class="error" />
<LangTranslate v-if="warning" :full-text="warning" class="error warning" />
@@ -33,7 +33,7 @@ import { useQuestionStore } from '@/stores/Questions/useQuestionStore';
import { storeToRefs } from 'pinia';
export default defineComponent({
components: { RichText, /*Remark,*/ LangTranslate },
components: { RichText, /* Remark, */ LangTranslate },
props: {
// 标题
stem: {
@@ -101,25 +101,25 @@ export default defineComponent({
const answerBorder = answerColor === '#333333' ? '#d9d9d9' : answerColor; // 边框颜色
const answerPlaceholder = toRgba(answerColor, 0.3); // placeholder颜色
// 鼠标 hover 时的背景色,目前用于:单选、多选、图片单选、图片多选的选项
const hoverBackgroundColor = themeColor.value?.buttonColor + '1E';
const hoverBackgroundColor = `${themeColor.value?.buttonColor}1E`;
// 16进制转rgba
function toRgba(color, opacity) {
return (
'rgba('
+ parseInt(color.substring(1, 3), 16)
+ ','
+ parseInt(color.substring(3, 5), 16)
+ ','
+ parseInt(color.substring(5, 7), 16)
+ ','
+ opacity
+ ')'
`rgba(${
parseInt(color.substring(1, 3), 16)
},${
parseInt(color.substring(3, 5), 16)
},${
parseInt(color.substring(5, 7), 16)
},${
opacity
})`
);
}
const replaceStr = (str, firIndex, lastIndex, char) => {
if (str.length == 0) {
if (str.length === 0) {
return '';
}
if (str.indexOf('<p>') < 0) {

View File

@@ -14,7 +14,6 @@ class AnswerApi {
}
});
}
/* 获取问卷详情 */
static getQuetionDetail(params) {
return request({
@@ -22,11 +21,10 @@ class AnswerApi {
url: `/console/surveys/${params.id}/detail`,
params: params.data,
headers: {
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') //store.state.answer.answerSessionId,
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') // store.state.answer.answerSessionId,
}
});
}
/* 答题 */
static answer(params) {
return request({
@@ -38,7 +36,6 @@ class AnswerApi {
}
});
}
/* 答题 */
static surveyPrevious(params) {
return request({
@@ -50,7 +47,6 @@ class AnswerApi {
}
});
}
/* 获取验证码 */
static getCode(params) {
return request({
@@ -58,11 +54,10 @@ class AnswerApi {
url: `/system/sms_codes`,
data: params.data,
headers: {
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') //store.state.answer.answerSessionId,
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') // store.state.answer.answerSessionId,
}
});
}
/* 问卷密码 */
static password(params) {
return request({
@@ -70,23 +65,20 @@ class AnswerApi {
url: `/answer/surveys/${params.id}/password`,
data: params.data,
headers: {
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') //store.state.answer.answerSessionId,
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') // store.state.answer.answerSessionId,
}
});
}
/* word导出 */
static download(sn, params = '') {
return request({
method: 'get',
url: `/console/survey_word_export/${sn}?${params}`,
headers: {
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') //store.state.answer.answerSessionId,
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') // store.state.answer.answerSessionId,
}
});
}
// 获取localStorage数据并检查过期时间
static getLocalStorageWithExpiration(key) {
const item = JSON.parse(localStorage.getItem(key));
@@ -113,7 +105,6 @@ class BrowsingRecordApi {
data: params.data
});
}
/* 获取3D资源 */
static getSurveysScene(params) {
return request({

View File

@@ -9,7 +9,8 @@ export const language = {
zh: (count) => `每行最少选${count}个。`
},
PleaseSelectAtLeastOneOptionsPerColumn: {
en: (count) => `Please select at least ${count} answer option${count > 1 ? 's' : ''} per column.`,
en: (count) =>
`Please select at least ${count} answer option${count > 1 ? 's' : ''} per column.`,
zh: (count) => `每列最少选${count}个。`
},
PleaseCategorizeAllOptions: {
@@ -29,11 +30,13 @@ export const language = {
zh: (count) => `最少选择${count}个。`
},
PleaseSelectAtLeastOneOptionsInTitleGroup: {
en: (count, title) => `Please select at least ${count} answer option${count > 1 ? 's' : ''} in ${title} group.`,
en: (count, title) =>
`Please select at least ${count} answer option${count > 1 ? 's' : ''} in ${title} group.`,
zh: (count, title) => `${title}最少选择${count}个。`
},
PleaseSelectAtMostOneOptionsInTitleGroup: {
en: (count, title) => `Please select at most ${count} answer option${count > 1 ? 's' : ''} in ${title} group.`,
en: (count, title) =>
`Please select at most ${count} answer option${count > 1 ? 's' : ''} in ${title} group.`,
zh: (count, title) => `${title}最多选择${count}个。`
},
PleaseSelectAtLeastOnePictures: {
@@ -87,7 +90,7 @@ export const language = {
zh: '去修改'
},
TheAnswerIsRepeatedMoreThanOneTimesPleaseRevise: {
en: (count, type) => `The answer is continuously repeated more than ${count} times, please revise.`,
en: (count) => `The answer is continuously repeated more than ${count} times, please revise.`,
zh: (count, type) => `答案连续重复超过${count}个,${type ? '建议' : '请'}修改。`
},
@@ -154,10 +157,8 @@ export const language = {
},
PleaseAnswerCarefullyOtherwiseRestart: {
en:
'Please be sure to read the questions carefully. Consecutive answers may invalidate the entire questionnaire. Your answer to each question is of great significance to us.',
zh:
'您在每一题的态度与观点均对我们有非常重要的意义,请您务必仔细阅读题目后回答,连续一致的答案可能会导致整个问卷的作废,请您重新作答。'
en: 'Please be sure to read the questions carefully. Consecutive answers may invalidate the entire questionnaire. Your answer to each question is of great significance to us.',
zh: '您在每一题的态度与观点均对我们有非常重要的意义,请您务必仔细阅读题目后回答,连续一致的答案可能会导致整个问卷的作废,请您重新作答。'
},
IGotIt: {
@@ -403,8 +404,7 @@ export const language = {
zh: '密码需要包含字母/数字/符号/字母和数字/字母和符号/符号和数字/字母、数字和符号'
},
ThePasswordNeedsToIncludeUpperLowerLettersNumbersSymbolsOrCombination: {
en:
'The password needs to include uppercase letters, lowercase letters, numbers, symbols, or a combination of uppercase and lowercase letters, numbers and symbols.',
en: 'The password needs to include uppercase letters, lowercase letters, numbers, symbols, or a combination of uppercase and lowercase letters, numbers and symbols.',
zh: '密码需要包含大/小写字母/数字/符号/大/小写字母和数字/大/小写字母和符号/符号和数字/大/小写字母、数字和符号'
},
ThePasswordMustBeACombinationOfLettersAndNumbers: {
@@ -502,19 +502,19 @@ export const language = {
zh: (title) => `${title} 上传失败`
},
EquipmentNotice: {
en: (title) =>
en: () =>
`Sorry, the number of submissions you have made under the current IP address has reached the limit. Thank you for participating.`,
zh: (title) => `很抱歉,您使用当前设备填写问卷已达到限制次数,感谢参与`
zh: () => `很抱歉,您使用当前设备填写问卷已达到限制次数,感谢参与`
},
SubmitIpNotice: {
en: (title) =>
en: () =>
`Sorry, the number of submissions you have made under the current IP address has reached the limit. Thank you for participating.`,
zh: (title) => `很抱歉您在当前IP地址下填写问卷已达到限制次数感谢参与`
zh: () => `很抱歉您在当前IP地址下填写问卷已达到限制次数感谢参与`
},
PrivatizationWxWorkIdentityNotice: {
en: (title) =>
en: () =>
`Sorry, the current environment does not allow you to answer this questionnaire.\nPlease click on the link or scan the code to answer through the privatized enterprise WeChat.`,
zh: (title) => `很抱歉,当前环境无法作答该问卷\n请通过私有化企业微信点击链接或扫码作答`
zh: () => `很抱歉,当前环境无法作答该问卷\n请通过私有化企业微信点击链接或扫码作答`
},
// QLast 提示
@@ -561,8 +561,8 @@ export function getLanguage(langArr = languageTypes) {
Object.keys(language).forEach((key) => {
result[key] = '';
let tempStr = [];
let tempFunc = [];
const tempStr = [];
const tempFunc = [];
l.forEach((lang) => {
const res = language[key][lang];

View File

@@ -1,5 +1,5 @@
import { createVNode } from 'vue';
import {ElMessage as message} from "element-plus"
import { ElMessage as message } from 'element-plus';
function getNode(text) {
const lines = (text || '').split('\n').filter((i) => !!i);

View File

@@ -2,18 +2,16 @@ import getlogicStatus from './logical';
// 更新code
function updateCode(action, logic, pages, page) {
const { question_index, skip_question_index } = logic;
const startIndex = pages.findIndex(
(page) => page.findIndex((questionIndex) => questionIndex === question_index) !== -1
);
const { skip_question_index: skipQuestionIndex } = logic;
const startIndex = pages.findIndex((page) => page.findIndex(() => true) !== -1);
if (startIndex < page) {
if (skip_question_index === -1) {
if (skipQuestionIndex === -1) {
action.code = 20011;
action.msg = '成功结束页';
} else if (skip_question_index === -2) {
} else if (skipQuestionIndex === -2) {
action.code = 20004;
action.msg = '甄别结束页';
} else if (skip_question_index === -3) {
} else if (skipQuestionIndex === -3) {
action.code = 20016;
action.msg = '配额超限页';
}
@@ -21,19 +19,19 @@ function updateCode(action, logic, pages, page) {
}
// 更新分页pages(题后跳转逻辑)
function updatePagesAfter(pages, logic, jumpTo, page) {
const { question_index, skip_question_index } = logic;
const startIndex = pages.findIndex(
(page) => page.findIndex((questionIndex) => questionIndex === question_index) !== -1
);
const { question_index: questionIndex, skip_question_index: skipQuestionIndex } = logic;
const startIndex = pages.findIndex((page) => page.findIndex(() => true) !== -1);
const endIndex = pages.findIndex(
(page) => page.findIndex((questionIndex) => questionIndex === skip_question_index) !== -1
(page) => page.findIndex((questionIndex) => questionIndex === skipQuestionIndex) !== -1
);
if (endIndex !== -1) {
const endQuestionIndex = pages[endIndex].findIndex((questionIndex) => questionIndex === skip_question_index);
const endQuestionIndex = pages[endIndex].findIndex(
(questionIndex) => questionIndex === skipQuestionIndex
);
pages[endIndex].splice(0, endQuestionIndex);
// 跳转到某页
if (startIndex > endIndex && startIndex < page) {
jumpTo.question_index = question_index;
jumpTo.question_index = questionIndex;
return (jumpTo.question_page = endIndex + 1);
}
pages.splice(startIndex + 1, endIndex - startIndex - 1);
@@ -49,7 +47,9 @@ function updatePagesBefore(pages, hideQuestionIndex) {
if (pages[pagesIndex].length === 1) {
pages.splice(pagesIndex, 1);
} else {
const pageIndex = pages[pagesIndex].findIndex((questionIndex) => questionIndex === hideQuestionIndex);
const pageIndex = pages[pagesIndex].findIndex(
(questionIndex) => questionIndex === hideQuestionIndex
);
pages[pagesIndex].splice(pageIndex, 1);
}
}
@@ -64,11 +64,11 @@ function autoFill(answerAutoFill, logic) {
}
// 选项隐藏逻辑
function updateOptionHidden(hide_options, logic) {
const { question_index, hide_option_index } = logic;
hide_options.question_index = question_index;
function updateOptionHidden(hideOptions, logic) {
const { question_index: questionIndex, hide_option_index: hideOptionIndex } = logic;
hideOptions.question_index = questionIndex;
if (logic.logicStatus) {
hide_options.option_key.push(...hide_option_index.map((opt) => opt.option_key));
hideOptions.option_key.push(...hideOptionIndex.map((opt) => opt.option_key));
}
}
@@ -105,7 +105,8 @@ export default function answerMock(questionsData, page) {
} else if (logic.skip_type === 4) {
// 只计算跳转后所在页面的隐藏逻辑,否则会出现只返回最后一道隐藏选项题目的情况,导致失效
const toPage = page + 1;
const hasHiddenLogicQuizPage = data.pages.findIndex((page) => page.includes(logic.question_index)) + 1;
const hasHiddenLogicQuizPage
= data.pages.findIndex((page) => page.includes(logic.question_index)) + 1;
if (hasHiddenLogicQuizPage === toPage) {
// 选项隐藏逻辑
updateOptionHidden(data.hide_options, logic);

View File

@@ -24,11 +24,11 @@ export default defineComponent({
}
},
setup(props) {
const questionStore = useQuestionStore()
const questionStore = useQuestionStore();
/**
* styleInfo 主题样式
*/
const {questionsData, styleInfo, page, pages,l} = storeToRefs(questionStore)
const { questionsData, styleInfo, page, pages } = storeToRefs(questionStore);
const route = useRoute();
// const questionsData = inject('questionsData'); // 问卷数据
@@ -38,7 +38,6 @@ export default defineComponent({
const scrollbar = ref(null);
let localPageInterval;
watch(
styleInfo,
(style) => {
@@ -67,10 +66,9 @@ export default defineComponent({
});
// 是否显示分页器
const showPage = computed(() => {
return [102, 104, 105, 201].includes(questions.value[0]?.question_type) ? false : true;
return ![102, 104, 105, 201].includes(questions.value[0]?.question_type);
});
// 分页计时器
watch(
page,
@@ -80,7 +78,8 @@ export default defineComponent({
const lastQuestionIndex = questions.value.slice(-1)[0]?.question_index;
console.log(questionsData.value);
const localPage = questionsData.value?.survey?.local_pages.find(
(localPage) => localPage.question_index === lastQuestionIndex && localPage.timer_config.is_short_time
(localPage) =>
localPage.question_index === lastQuestionIndex && localPage.timer_config.is_short_time
);
if (localPage) {
localPageTimer.value = localPage.timer_config;
@@ -107,7 +106,7 @@ export default defineComponent({
let prevNum = arr[0];
for (let i = 1; i < arr.length; i++) {
if (prevNum && arr[i] === prevNum) {
count++;
count += 1;
} else {
count = 1;
warnStart = i;
@@ -134,9 +133,13 @@ export default defineComponent({
if (questionType === 10) {
// 矩阵多选题
if (question.config.is_change_row_cell) {
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(config.min_select || 1);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(
config.min_select || 1
);
} else {
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerLine(config.min_select || 1);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerLine(
config.min_select || 1
);
}
} else if (questionType === 12) {
// 图片显示题,如果开启了仅显示并且开启了单图显示时长,需要确保用户每一个图片都点开看过了才能下一页
@@ -150,7 +153,11 @@ export default defineComponent({
} else if (!question.error) {
question.error = translatedText.value.ThisIsARequiredQuestion;
}
} else if (answer && questionType === 1 && Object.keys(answer).findIndex((value) => !answer[value]) !== -1) {
} else if (
answer
&& questionType === 1
&& Object.keys(answer).findIndex((value) => !answer[value]) !== -1
) {
// 单选题
isError = true;
question.error = translatedText.value.PleaseInputAValue;
@@ -167,7 +174,9 @@ export default defineComponent({
);
if (index === -1) {
isError = true;
question.error = translatedText.value.PleaseSelectAtLeastOneOptions(config.min_select);
question.error = translatedText.value.PleaseSelectAtLeastOneOptions(
config.min_select
);
} else {
question.error = '';
}
@@ -181,23 +190,28 @@ export default defineComponent({
// 选项分组最少选项数量
config.option_groups?.option_group.some((optionGroup) => {
const { min, title } = optionGroup;
const select = optionGroup.groups.filter((groups) => Object.keys(answer).includes(groups.option_key));
const select = optionGroup.groups.filter((groups) =>
Object.keys(answer).includes(groups.option_key)
);
if (title && select.length < min) {
// 判断隐藏选项
question.hideOptionsSet = new Set(question.hideOptions);
const options = question.list.flatMap((list) =>
list.options
.filter(({ option_key }) =>
.filter(({ option_key: optionKey }) =>
optionGroup.groups.some(
({ option_key: groupKey }) =>
groupKey === option_key && !question.hideOptionsSet.has(groupKey)
groupKey === optionKey && !question.hideOptionsSet.has(groupKey)
)
)
.map(({ option_key }) => option_key)
.map(({ option_key: optionKey }) => optionKey)
);
if (options.length >= min) {
isError = true;
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsInTitleGroup(min, title);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsInTitleGroup(
min,
title
);
}
}
return isError;
@@ -205,12 +219,21 @@ export default defineComponent({
}
} else if (answer && questionType === 10) {
// 矩阵多选题,列分组时,校验选项数量
const cellGroups = (config?.cell_option_groups?.option_group || []).filter((i) => i.groups?.length);
const cellGroups = (config?.cell_option_groups?.option_group || []).filter(
(i) => i.groups?.length
);
if (cellGroups.length) {
const rows = question.list.reduce((p, c) => [...p, ...(c.type === 1 ? c.options || [] : [])], []);
const cols = question.list.reduce((p, c) => [...p, ...(c.type === 2 ? c.options || [] : [])], []);
const rows = question.list.reduce(
(p, c) => [...p, ...(c.type === 1 ? c.options || [] : [])],
[]
);
const cols = question.list.reduce(
(p, c) => [...p, ...(c.type === 2 ? c.options || [] : [])],
[]
);
const freeCols = cols.filter(
(col) => !cellGroups.some((g) => g.groups.find((c) => c.option_key === col.option_key))
(col) =>
!cellGroups.some((g) => g.groups.find((c) => c.option_key === col.option_key))
);
const arr = rows.map((row) => {
@@ -220,7 +243,9 @@ export default defineComponent({
});
});
});
arr.forEach((a, idx) => a.push(freeCols.map((col) => `${rows[idx].option_key}_${col.option_key}`)));
arr.forEach((a, idx) =>
a.push(freeCols.map((col) => `${rows[idx].option_key}_${col.option_key}`))
);
const answered = Object.keys(answer);
cellGroups.forEach((group, idx) => {
@@ -259,20 +284,28 @@ export default defineComponent({
}
return p;
},
question.list.filter((i) => i.type === 1).flatMap((i) => i.options.map((i) => 0))
question.list.filter((i) => i.type === 1).flatMap((i) => i.options.map(() => 0))
);
if (minSelect && minSelect > Math.max(...perLineSelectedCount)) {
if (question.config.is_change_row_cell) {
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(config.min_select || 1);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(
config.min_select || 1
);
} else {
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerLine(config.min_select || 1);
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerLine(
config.min_select || 1
);
}
isError = true;
}
console.log('===', minSelect, Object.keys(answer), answer, perLineSelectedCount);
} else if (answer && questionType === 12) {
question.error = '';
} else if (answer && questionType === 14 && Object.keys(answer).length < config.min_select) {
} else if (
answer
&& questionType === 14
&& Object.keys(answer).length < config.min_select
) {
// 图片多选题
isError = true;
question.error = translatedText.value.PleaseSelectAtLeastOnePictures(config.min_select);
@@ -281,7 +314,9 @@ export default defineComponent({
if (Object.keys(answer).length < (+config.min_select || 0)) {
// 选项数量
isError = true;
question.error = translatedText.value.PleaseSelectAtLeastOneOptions(config.min_select);
question.error = translatedText.value.PleaseSelectAtLeastOneOptions(
config.min_select
);
}
} else if (answer && questionType === 17) {
// 恒定总和题
@@ -303,19 +338,21 @@ export default defineComponent({
question.error = '';
// 填空题
const { value } = answer;
let newValue = value.replace(/\n|\r|\r\n/g, '');
const newValue = value.replace(/\n|\r|\r\n/g, '');
switch (config.text_type) {
case 3: // 字母
isError = config.include_mark == 1
? !/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]]+$/.test(
isError
= config.include_mark === 1
? !/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]]+$/.test(
newValue
) || !newValue.length
: !/^[a-zA-Z]+$/.test(newValue) || !newValue.length;
question.error = isError ? translatedText.value.PleaseEnterEnglishLetters : '';
break;
case 4: // 中文
isError = config.include_mark == 1
? !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]])+$/.test(
isError
= config.include_mark === 1
? !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]])+$/.test(
newValue
) || !newValue.length
: !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/.test(
@@ -324,7 +361,8 @@ export default defineComponent({
question.error = isError ? translatedText.value.PleaseEnterChineseWords : '';
break;
case 5: // 邮箱
isError = !/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
isError
= !/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
);
question.error = isError ? translatedText.value.PleaseEnterACorrectEmail : '';
@@ -334,7 +372,8 @@ export default defineComponent({
question.error = isError ? translatedText.value.PleaseEnterACorrectPhone : '';
break;
case 7: // 身份证号
isError = !/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
isError
= !/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
value
);
question.error = isError ? translatedText.value.PleaseEnterACorrectID : '';
@@ -354,42 +393,53 @@ export default defineComponent({
switch (config.text_type) {
case 3: // 字母
if (
!/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]]+$/.test(
!/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]]+$/.test(
value
)
)
) {
question.error = translatedText.value.PleaseEnterEnglishLetters;
}
break;
case 4: // 中文
if (
!/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]])+$/.test(
!/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]])+$/.test(
value
)
)
) {
question.error = translatedText.value.PleaseEnterChineseWords;
}
break;
case 5: // 邮箱
if (
!/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
)
)
) {
question.error = translatedText.value.PleaseEnterACorrectEmail;
}
break;
case 6: // 手机号
if (!/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value))
if (!/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value)) {
question.error = translatedText.value.PleaseEnterACorrectPhone;
}
break;
case 7: // 身份证号
if (
!/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(value)
!/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
value
)
) {
question.error = translatedText.value.PleaseEnterACorrectID;
}
break;
default:
break;
}
if (!question.error && value.length < config.min && ![1, 2].includes(config.text_type)) {
if (
!question.error
&& value.length < config.min
&& ![1, 2].includes(config.text_type)
) {
question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min);
}
});
@@ -442,65 +492,6 @@ export default defineComponent({
});
});
}
// 质量控制(选择题)
const allPages = pages.value.flatMap((currentPages) => currentPages);
const allQuestions = allPages.map((questionIndex) => {
return questionsData.value.questions.find((question) => question.question_index === questionIndex);
});
const repeat = questionsData.value.survey.repeat_list?.find((repeat) =>
repeat.question_indexes.find(({ first_index, last_index }) => {
const firstIndex = allPages.findIndex((questionIndex) => questionIndex === first_index);
const lastIndex = allPages.findIndex((questionIndex) => questionIndex === last_index);
const currentQuestions = allQuestions
.slice(firstIndex, lastIndex + 1)
.filter((currentQuestions) => currentQuestions.question_type === repeat.question_type);
const answerIndexes = currentQuestions.map((question) => question.answerIndex);
return hasNConsecutiveNumbers(
answerIndexes,
repeat.allow_repeat_num + 1,
(warnStart, warnEnd) => {
currentQuestions.forEach((question, index) => {
if (index >= warnStart && index < warnEnd) {
if (repeat.repeat_type) {
question.warning = translatedText.value.TheAnswerIsRepeatedMoreThanOneTimesPleaseRevise(
repeat.allow_repeat_num,
repeat.repeat_type
);
} else {
question.error = translatedText.value.TheAnswerIsRepeatedMoreThanOneTimesPleaseRevise(
repeat.allow_repeat_num,
repeat.repeat_type
);
}
} else {
question.warning = '';
question.error = '';
}
});
},
() => {
currentQuestions.forEach((question) => {
question.warning = '';
});
}
);
})
);
if (repeat) {
await new Promise((resolve) => {
Modal[repeat.repeat_type ? 'confirm' : 'info']({
class: 'custom-modal custom-modal-title-notice',
title: translatedText.value.PleaseAnswerCarefully,
content: repeat.alert_text,
cancelText: translatedText.value.ContinueAnswer,
okText: translatedText.value.ReviseAnswer,
onCancel: () => {
questions.value.forEach((question) => (question.answerIndex = ''));
resolve();
}
});
});
}
// 判断是作答还是预览
if (!props.isAnswer) {
loading.value = true;
@@ -615,7 +606,7 @@ export default defineComponent({
loading.value = false;
} else {
console.log(errors);
const { error, title, question_index } = errors[0];
const { error, title, question_index: questionIndex } = errors[0];
const lines = (error || '')
.split('\n')
.filter((i) => !!i)
@@ -623,7 +614,7 @@ export default defineComponent({
msg.error(lines.join('\n'));
// 锚点
const anchor = document.querySelector(`#questionIndex${question_index}`);
const anchor = document.querySelector(`#questionIndex${questionIndex}`);
console.log(anchor, scrollbar.value);
scrollbar.value.scrollTo(0, anchor.offsetTop - (props.isMobile ? 20 : 40));
}
@@ -632,72 +623,77 @@ export default defineComponent({
}
}
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');
}
}
}
// 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');
// }
// }
// }
// 选项隐藏
function hideOptions(hide) {
const questionIndex = hide?.question_index;
if (questionIndex) {
const qustion = questionsData.value.questions.find((qustion) => qustion.question_index === questionIndex);
const qustion = questionsData.value.questions.find(
(qustion) => qustion.question_index === questionIndex
);
qustion.hideOptions = hide.option_key || [];
}
}
// 关联引用
function onRelation({ options, value, list }, { question_type, question_index, related, answer }) {
function onRelation(
{ options, value, list },
{ question_type: questionType, question_index: questionIndex, related, answer }
) {
// 关联
related.forEach((relationItem) => {
let relationOptions = [];
if (question_type === 9 || question_type === 10) {
if (questionType === 9 || questionType === 10) {
// 矩阵选择
list.forEach((item) => {
if (item.type === relationItem.cite_type) {
@@ -705,22 +701,22 @@ export default defineComponent({
}
if (relationItem.relation_type === 1) {
relationOptions = relationOptions.filter((option) =>
question_type === 9 ? option.value : option.value?.length
questionType === 9 ? option.value : option.value?.length
);
} else if (relationItem.relation_type === 2) {
relationOptions = relationOptions.filter((option) =>
question_type === 9 ? !option.value : !option.value?.length
questionType === 9 ? !option.value : !option.value?.length
);
}
});
} else if (question_type === 11) {
} else if (questionType === 11) {
// 矩阵打分
list.forEach((item) => {
if (item.type === relationItem.cite_type) {
relationOptions = [...relationOptions, ...item.options];
}
});
} else if (question_type === 25 || question_type === 26) {
} else if (questionType === 25 || questionType === 26) {
// 热区题
relationOptions = options.filter((option) => {
if (relationItem.relation_type === 1) {
@@ -732,7 +728,7 @@ export default defineComponent({
}
return true;
});
} else if (question_type === 105) {
} else if (questionType === 105) {
// MXD
options.forEach((currentOptions) => {
currentOptions.forEach((option) => {
@@ -761,11 +757,11 @@ export default defineComponent({
} else {
// 过滤选中/未选中选项
relationOptions = options.filter((option) => {
if (question_type === 1) {
if (questionType === 1) {
// 单选
if (relationItem.relation_type === 1) return value === option.option_key;
return value !== option.option_key;
} else if (question_type === 2) {
} else if (questionType === 2) {
// 多选
if (relationItem.relation_type === 1) return value.includes(option.option_key);
return !value.includes(option.option_key);
@@ -784,10 +780,10 @@ export default defineComponent({
if (option.option_key[0] !== 'Q') {
let letter = 'A';
// 矩阵题行、列
if (question_type >= 9 && question_type <= 11) {
if (questionType >= 9 && questionType <= 11) {
letter = relationItem.cite_type === 1 ? 'R' : 'C';
}
option.option_key = `Q${question_index}${letter}${option.option_key}`;
option.option_key = `Q${questionIndex}${letter}${option.option_key}`;
}
// 其他项特殊处理
if (option.is_other && option.value) {
@@ -799,7 +795,7 @@ export default defineComponent({
});
// 更新关联题列表
const relatedList = question.list.find(
(relatedListItem) => relatedListItem.relation_question_index === question_index
(relatedListItem) => relatedListItem.relation_question_index === questionIndex
);
relatedList.options = answer ? copyRelationOptions : [];
});
@@ -818,7 +814,8 @@ export default defineComponent({
const evt1 = {};
if ([1].includes(question.question_type)) {
evt1.value = Object.keys(question.answer)
evt1.value
= Object.keys(question.answer)
.map((key) => (question.answer[key] ? key : undefined))
.filter((i) => !!i)?.[0] || undefined;
evt1.options = question.list.flatMap((i) => i.options);