feat: 完成 preview 组件的功能
- 新增 preview 组件相关的资源 - 调用 web 端部分 API - 相关题目添加答案配置
This commit is contained in:
58
src/components/RichText.vue
Normal file
58
src/components/RichText.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div v-if="isPreview"
|
||||
v-html="nodes"
|
||||
style="min-width: 22px"
|
||||
:class="isMobile ? 'mobile-text' : ''"
|
||||
@click="onClick"></div>
|
||||
<div v-else v-html="nodes"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
// 富文本内容
|
||||
nodes: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 是否开启预览图片功能
|
||||
isPreview: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否移动端
|
||||
isMobile: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick(evt) {
|
||||
const targetTagName = (evt.target || evt.srcElement).tagName || '';
|
||||
if (targetTagName.toLowerCase() === 'img') {
|
||||
evt.stopPropagation()
|
||||
evt.cancelBubble = true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(p) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
:deep(img) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mobile-text {
|
||||
:deep(img) {
|
||||
max-width: 90% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1 +1 @@
|
||||
export const surveyQuestion = '/api/api/console/surveys/RWNK9BYp/questions';
|
||||
export const surveyQuestion = 'https://yls-api-uat.dctest.digitalyili.com/api/console/surveys/RWNK9BYp/questions';
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import layout from '@/layouts/index.vue';
|
||||
import Design from '@/views/Design/Index.vue';
|
||||
import Preview from '@/views/Design/Preview.vue';
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
@@ -50,7 +49,7 @@ const router = createRouter({
|
||||
path: '/preview',
|
||||
name: 'preview',
|
||||
meta: {},
|
||||
component: Preview
|
||||
component: ()=> import ( '@/views/Survey/views/Preview/Index.vue')
|
||||
},
|
||||
{
|
||||
path: '/create',
|
||||
|
||||
50
src/stores/Questions/useQuestionStore.ts
Normal file
50
src/stores/Questions/useQuestionStore.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
export const useQuestionStore = defineStore('questionStore', () => {
|
||||
const questionsData = ref();
|
||||
|
||||
// styleInfo 主题样式
|
||||
const styleInfo = computed(() => questionsData.value?.survey?.style || {});
|
||||
// 当前页数
|
||||
const page = ref(0);
|
||||
// 分页
|
||||
const pages = computed(() => questionsData.value?.answer?.pages || []);
|
||||
// 加载
|
||||
const loading = ref(false);
|
||||
// 上一页时的加载状态
|
||||
const prevLoading = ref(false);
|
||||
// 分页计时器
|
||||
const localPageTimer = ref({});
|
||||
// 当前页问卷
|
||||
const questions = computed(() => {
|
||||
const currentPages = pages.value[page.value - 1] || [];
|
||||
return (questionsData.value?.questions || []).filter((quetion: any) =>
|
||||
currentPages.find((index: any) => quetion.question_index === index)
|
||||
);
|
||||
});
|
||||
|
||||
// 是否显示分页器
|
||||
const showPage = computed(() => {
|
||||
return ![102, 104, 105, 201].includes(questions.value[0]?.question_type);
|
||||
});
|
||||
|
||||
// 作用未知
|
||||
const l = ref({});
|
||||
// 主题颜色
|
||||
const themeColor = ref({});
|
||||
|
||||
return {
|
||||
questionsData,
|
||||
styleInfo,
|
||||
page,
|
||||
pages,
|
||||
l,
|
||||
themeColor,
|
||||
loading,
|
||||
prevLoading,
|
||||
localPageTimer,
|
||||
questions,showPage
|
||||
|
||||
};
|
||||
});
|
||||
11
src/utils/directives/useVFocus.ts
Normal file
11
src/utils/directives/useVFocus.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { Directive } from 'vue';
|
||||
|
||||
/**
|
||||
* 自定义指令,用于在元素挂载后自动获取焦点
|
||||
*/
|
||||
export const vFocus: Directive = {
|
||||
mounted(el: HTMLInputElement) {
|
||||
el.focus();
|
||||
}
|
||||
};
|
||||
|
||||
126
src/utils/utils.js
Normal file
126
src/utils/utils.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 生成一个按数字顺序的名字,如:名字1,名字2,名字3
|
||||
* @param names {string[]} 要生成的名字前缀列表
|
||||
* @param list {string[]} 已经有的名字列表
|
||||
* @param options {object}
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function getSequenceName(names, list, options) {
|
||||
const result = [];
|
||||
const start = options?.start ?? 1; // 从数字几开始
|
||||
|
||||
names.forEach((name) => {
|
||||
const sequence = [];
|
||||
if (!list.length) {
|
||||
result.push(name + start);
|
||||
return;
|
||||
}
|
||||
list
|
||||
.map((i) => getDomText(i))
|
||||
.filter((i) => !!i)
|
||||
.forEach((item) => {
|
||||
if (item.startsWith(name)) {
|
||||
const index = Number(item.substring(name.length));
|
||||
if (!isNaN(index) && index >= start) {
|
||||
sequence.push(index);
|
||||
}
|
||||
}
|
||||
});
|
||||
sequence.sort((a, b) => a - b);
|
||||
if (!sequence.includes(start)) {
|
||||
result.push(`${name}${start}`);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < sequence.length; i += 1) {
|
||||
if (!sequence[i + 1] || sequence[i] + 1 !== sequence[i + 1]) {
|
||||
result.push(`${name}${sequence[i] + 1}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取富文本中的文字
|
||||
* @param dom {string} 富文本
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDomText(dom) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = dom;
|
||||
return div.textContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断富文本是否与列表中的富文本重复。重复则返回 true,不同则返回 false
|
||||
* @param list {string[]} 待判断的富文本列表
|
||||
* @param comparedList {string[]} 用于比较的富文本列表
|
||||
* @param options {object}
|
||||
* @returns {boolean[]}
|
||||
*/
|
||||
export function isRichRepeat(list, comparedList, options) {
|
||||
const imageAsDifference = options?.imageAsDifference !== false; // 不比较图片。只要存在图片,则认为两段富文本不同
|
||||
|
||||
return list.map((rich) => {
|
||||
return comparedList.some((compared) => {
|
||||
if (imageAsDifference) {
|
||||
if (rich.includes('<img') || compared.includes('<img')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return getDomText(rich) === getDomText(compared);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断富文本是否为空。
|
||||
* @param list {string[]} 待判断的富文本列表
|
||||
* @param options {object}
|
||||
* @returns {boolean[]}
|
||||
*/
|
||||
export function isRichEmpty(list, options) {
|
||||
const imageAsEmpty = options?.imageAsEmpty === true; // 不判断图片。只要存在图片,则认为两段富文本不为空
|
||||
|
||||
return list.map((rich) => {
|
||||
if (!imageAsEmpty) {
|
||||
if (rich.includes('<img')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !getDomText(rich);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 使数组 options 里的内容按随机的方式,重新排序。不改变原数组,返回新的排好序的数组。
|
||||
* @param options
|
||||
* @param isRandom
|
||||
*/
|
||||
export function randomOptions(options, isRandom = true) {
|
||||
if (!isRandom) {
|
||||
return options;
|
||||
}
|
||||
return [...(options || [])].sort(() => Math.random() - 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 field 字段,判断两个对象数组是否相同
|
||||
* @param arr1
|
||||
* @param arr2
|
||||
* @param field
|
||||
*/
|
||||
export function compareArrayByField(arr1, arr2, field) {
|
||||
const arr1Fields = Array.from(new Set(arr1.map((i) => i[field])));
|
||||
const arr2Fields = Array.from(new Set(arr2.map((i) => i[field])));
|
||||
|
||||
if (arr1Fields.length !== arr2Fields.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return arr1Fields.every((i) => arr2Fields.includes(i));
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
</template>
|
||||
<template #input>
|
||||
<template v-for="(item, optionIndex) in element.options" :key="item.id">
|
||||
<van-radio-group v-if="element.question_type === 1">
|
||||
<van-radio-group v-if="element.question_type === 1" v-model="choiceValue">
|
||||
<option-action
|
||||
v-model:data="element.options[optionIndex]"
|
||||
:active="active"
|
||||
@@ -28,6 +28,7 @@
|
||||
:disabled="it.disabled"
|
||||
icon-size="0.45rem"
|
||||
>
|
||||
<!-- 自定义文本 -->
|
||||
<template #default>
|
||||
<div class="flex align-center van-cell">
|
||||
<contenteditable v-model="it.option" :active="active"></contenteditable>
|
||||
@@ -73,6 +74,8 @@
|
||||
<script setup>
|
||||
import OptionAction from '@/views/Design/components/ActionCompoents/OptionAction.vue';
|
||||
import { defineAsyncComponent, toRefs } from 'vue';
|
||||
|
||||
const choiceValue = ref('checked');
|
||||
const Contenteditable = defineAsyncComponent(() => import('@/components/contenteditable.vue'));
|
||||
const props = defineProps({
|
||||
element: {
|
||||
|
||||
99
src/views/Design/components/Questions/MatrixCheckbox.vue
Normal file
99
src/views/Design/components/Questions/MatrixCheckbox.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<table class="matrix-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<td v-for="col in columns" :key="col.option">
|
||||
<!-- 编辑状态,单次点击出输入框,失焦后关闭 -->
|
||||
<input
|
||||
v-if="col.editor"
|
||||
v-model="col.option"
|
||||
v-focus
|
||||
type="text"
|
||||
@focusout="col.editor = false"
|
||||
@click="handleRowNameChange(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"/>
|
||||
<td v-for="(col, colIndex) in columns" :key="colIndex">
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="`${rowIndex + 1}_${colIndex + 1}`"
|
||||
:checked="isOptionChecked(rowIndex, colIndex)"
|
||||
@change="handleColNameChange(row.option, col.option, $event)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits } from 'vue';
|
||||
import {vFocus} from '@/utils/directives/useVFocus';
|
||||
|
||||
const props = defineProps<{
|
||||
rows: { option: string }[];
|
||||
columns: { option: string, editor?: boolean }[];
|
||||
questionType: number;
|
||||
matrixAnswer: { [key: string]: any };
|
||||
rowRecord: (number | string)[];
|
||||
}>();
|
||||
|
||||
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 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);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.matrix-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.matrix-table th,
|
||||
.matrix-table td {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
91
src/views/Design/components/Questions/MatrixRadio.vue
Normal file
91
src/views/Design/components/Questions/MatrixRadio.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<table class="matrix-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<td v-for="col in columns" :key="col.option">
|
||||
<!-- 编辑状态,单次点击出输入框,失焦后关闭 -->
|
||||
<input
|
||||
v-if="col.editor"
|
||||
v-model="col.option"
|
||||
v-focus
|
||||
type="text"
|
||||
@focusout="col.editor = false"
|
||||
@click="handleRowNameChange(col.option!)"
|
||||
/>
|
||||
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, rowIndex) in rows" :key="rowIndex">
|
||||
<th v-html="row.option"></th>
|
||||
<td v-for="(col, colIndex) in columns" :key="colIndex">
|
||||
<input
|
||||
type="radio"
|
||||
:name="`R${rowIndex + 1}`"
|
||||
:value="`${rowIndex + 1}_${colIndex + 1}`"
|
||||
:checked="isOptionChecked(rowIndex, colIndex)"
|
||||
@change="handleColNameChange(row.option, col.option, $event)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits } from 'vue';
|
||||
import {vFocus} from '@/utils/directives/useVFocus';
|
||||
|
||||
let props = defineProps<{
|
||||
rows: { option: string }[];
|
||||
columns: { option: string, editor?: boolean }[];
|
||||
questionType: number;
|
||||
matrixAnswer: { [key: string]: any };
|
||||
rowRecord: (number | string)[];
|
||||
}>();
|
||||
|
||||
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 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);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.matrix-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.matrix-table th,
|
||||
.matrix-table td {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
74
src/views/Design/components/Questions/MatrixText.vue
Normal file
74
src/views/Design/components/Questions/MatrixText.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<table class="matrix-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<td v-for="col in columns" :key="col.option">
|
||||
<!-- 编辑状态,单次点击出输入框,失焦后关闭 -->
|
||||
<input
|
||||
v-if="col.editor"
|
||||
v-model="col.option"
|
||||
v-focus
|
||||
type="text"
|
||||
@focusout="col.editor = false"
|
||||
@click="handleRowNameChange(col.option!)"
|
||||
/>
|
||||
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, rowIndex) in rows" :key="rowIndex">
|
||||
<th v-html="row.option"></th>
|
||||
<td v-for="(col, colIndex) in columns" :key="colIndex">
|
||||
<input
|
||||
type="text"
|
||||
:value="matrixAnswer[`${rowIndex + 1}_${colIndex + 1}`]"
|
||||
@change="handleColNameChange(row.option, col.option, $event, rowIndex, colIndex)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits } from 'vue';
|
||||
import {vFocus } from "@/utils/directives/useVFocus"
|
||||
|
||||
const props = defineProps<{
|
||||
rows: { option: string }[];
|
||||
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 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 };
|
||||
|
||||
emits('update:matrixAnswer', newMatrixAnswer);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.matrix-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.matrix-table th,
|
||||
.matrix-table td {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -24,7 +24,7 @@
|
||||
:key="optionIndex"
|
||||
@click="chooseOption(item)"
|
||||
>
|
||||
<RateCharacter :config="element.config"></RateCharacter>
|
||||
<RateCharacter v-model="answerValue" :config="element.config"></RateCharacter>
|
||||
<div class="tips">
|
||||
<p>{{ element.config.prompt_left }}</p>
|
||||
<p>{{ element.config.prompt_center }}</p>
|
||||
@@ -38,7 +38,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import RateCharacter from './RateCharacter.vue';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -56,6 +56,16 @@ 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 chooseId = ref('');
|
||||
// 创建一个本地副本以保存更改
|
||||
|
||||
@@ -47,6 +47,8 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
const model = defineModel();
|
||||
|
||||
const renderScore = (min, max, interval) => {
|
||||
const result = [];
|
||||
for (let i = min; i <= max; i += interval) {
|
||||
@@ -61,21 +63,24 @@ const renderScore = (min, max, interval) => {
|
||||
}
|
||||
rateItem.value = result;
|
||||
};
|
||||
// 重置颜色
|
||||
const getItem = (value) => {
|
||||
// chooseId.value = value.id;
|
||||
model.value = value.label;
|
||||
rateItem.value.forEach((item, index) => {
|
||||
rateItem.value[index].active = item.label <= value.label;
|
||||
});
|
||||
};
|
||||
|
||||
watch(model, () => {
|
||||
getItem({ label: model.value, active: false });
|
||||
});
|
||||
|
||||
// 监听 min、max 和 score_interval 的变化
|
||||
watch(
|
||||
() => [props.config.min, props.config.max, props.config.score_interval],
|
||||
(newValues) => {
|
||||
const [newMin, newMax, newScoreInterval] = newValues;
|
||||
// this.renderScore();
|
||||
renderScore(newMin, newMax, newScoreInterval);
|
||||
// 在这里可以添加对这些值变化的处理逻辑
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
@@ -83,7 +88,6 @@ watch(
|
||||
|
||||
<style scoped lang="scss">
|
||||
ul {
|
||||
// border: 1px solid red;
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@@ -95,8 +99,6 @@ ul {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
color: #666;
|
||||
|
||||
// border: 1px solid red;
|
||||
}
|
||||
|
||||
.active_item {
|
||||
|
||||
1400
src/views/Survey/views/Preview/Index.vue
Normal file
1400
src/views/Survey/views/Preview/Index.vue
Normal file
File diff suppressed because it is too large
Load Diff
351
src/views/Survey/views/Preview/components/AnswerViewer.vue
Normal file
351
src/views/Survey/views/Preview/components/AnswerViewer.vue
Normal file
@@ -0,0 +1,351 @@
|
||||
<template>
|
||||
<slot></slot>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { BrowsingRecordApi } from '../js/api';
|
||||
|
||||
|
||||
const has3dPages = ['/answer'];
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
isStemMiddle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 题干
|
||||
stem: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 配置
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
// 样本SN
|
||||
answerSn: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 问卷SN
|
||||
answerSurveySn: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
question: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
cartWaresLength: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 是否移动端
|
||||
isMobile: {
|
||||
type: Boolean,
|
||||
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,
|
||||
|
||||
shopData: null,
|
||||
page: null,
|
||||
page_shelves: null,
|
||||
sceneAction: null,
|
||||
elCart: null,
|
||||
hold: null,
|
||||
|
||||
// 用于控制下一页点击
|
||||
pager: {
|
||||
// 判断能否点击
|
||||
activate: false,
|
||||
timeoutId: 0,
|
||||
// 初始化点击延迟
|
||||
init(time = 3000) {
|
||||
this.activate = false;
|
||||
if (this.timeoutId) {
|
||||
clearTimeout(this.timeoutId);
|
||||
this.timeoutId = 0;
|
||||
}
|
||||
this.timeoutId = setTimeout(() => {
|
||||
this.activate = true;
|
||||
}, time);
|
||||
},
|
||||
// 清空延迟
|
||||
clear() {
|
||||
if (this.timeoutId) {
|
||||
clearTimeout(this.timeoutId);
|
||||
this.timeoutId = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
showTimeTimeoutId: 0
|
||||
};
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.pager.clear();
|
||||
if (this.showTimeTimeoutId) clearTimeout(this.showTimeTimeoutId);
|
||||
},
|
||||
async mounted() {
|
||||
if (!this.canUse3D) return;
|
||||
|
||||
if (this.config.is_binding_goods) {
|
||||
this.$refs.SceneGuidance?.show({
|
||||
isSelect: true,
|
||||
isMobile: this.isMobile
|
||||
});
|
||||
} else {
|
||||
this.$refs.SceneGuidance?.show({
|
||||
isSelect: false,
|
||||
isMobile: this.isMobile
|
||||
});
|
||||
}
|
||||
|
||||
this.pager.init();
|
||||
|
||||
this.scene = JSON.parse(this.config.scene_information);
|
||||
|
||||
// 解决缓存问题,答卷时加载场景信息
|
||||
if (!this.scene) {
|
||||
var res = await BrowsingRecordApi.getSurveysScene({
|
||||
sn: this.$route.query.sn,
|
||||
question_index: this.question.question_index
|
||||
});
|
||||
this.scene = JSON.parse(res.data?.scene_information);
|
||||
}
|
||||
|
||||
console.log('scene', this.scene);
|
||||
|
||||
this.shopData = buildShopDataDemo(this.scene);
|
||||
this.$refs.SceneSurveyViewer.init();
|
||||
|
||||
if (this.config.is_show_time && this.config.show_time) {
|
||||
this.showTimeTimeoutId = setTimeout(() => {
|
||||
this.showTimeTimeoutId = 0;
|
||||
this.$emit('next');
|
||||
}, this.config.show_time * 1000);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
next() {
|
||||
console.log(this.pager.activate);
|
||||
if (!this.pager.activate) {
|
||||
return;
|
||||
}
|
||||
this.pager.init();
|
||||
|
||||
this.$emit(
|
||||
'next',
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
this.sceneAction = {
|
||||
action: 'hold_to_shelf'
|
||||
};
|
||||
setTimeout(() => resolve(), 100);
|
||||
})
|
||||
);
|
||||
},
|
||||
previous() {
|
||||
if (!this.pager.activate) {
|
||||
return;
|
||||
}
|
||||
this.pager.init();
|
||||
|
||||
this.sceneAction = {
|
||||
action: 'hold_to_shelf'
|
||||
};
|
||||
setTimeout(() => {
|
||||
this.$emit('previous');
|
||||
}, 100);
|
||||
},
|
||||
onLoadingCompletion() {
|
||||
this.shopData.shelves.forEach((shelf) => {
|
||||
var wares = shelf.wares;
|
||||
|
||||
// 选项随机
|
||||
if (this.options && this.options.length && this.config.is_binding_goods) {
|
||||
wares = this.options
|
||||
.filter((option) => !option.is_other)
|
||||
.map((option) => {
|
||||
return this.wares.find(
|
||||
(ware) => ware.question_index == option.question_index && ware.option_index == option.option_index
|
||||
);
|
||||
});
|
||||
wares = wares.filter((x) => x);
|
||||
}
|
||||
|
||||
if (shelf.layoutType == 'multiCols') {
|
||||
shelf.cells = shelf.cells.map((cell, index) => {
|
||||
return {
|
||||
...cell,
|
||||
...wares[Math.floor(index / shelf.originCells.length) % wares.length],
|
||||
showSign: !!this.config.is_price_tag,
|
||||
showLogo: !!this.config.is_brand
|
||||
};
|
||||
});
|
||||
} else {
|
||||
shelf.cells = this.shelf.cells.map((cell, index) => {
|
||||
return {
|
||||
...cell,
|
||||
...wares[index % this.wares.length],
|
||||
showSign: !!this.config.is_price_tag,
|
||||
showLogo: !!this.config.is_brand
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.page_shelves = {
|
||||
shelves: this.shopData.shelves
|
||||
};
|
||||
},
|
||||
onFromSceneHoldToShelf() {
|
||||
this.hold = null;
|
||||
// this.sceneAction = {
|
||||
// action: "hold_to_shelf",
|
||||
// };
|
||||
},
|
||||
onPageCompletion() {
|
||||
this.elCart = this.$refs.cart;
|
||||
},
|
||||
onUiHoldToCart() {
|
||||
if (!this.hold) return;
|
||||
this.sceneAction = {
|
||||
action: 'hold_to_cart'
|
||||
};
|
||||
var hold = this.hold;
|
||||
this.hold = null;
|
||||
|
||||
var ware = this.wares.find((ware) => ware.planetid == hold.data.surveyWare.id);
|
||||
if (!ware) return;
|
||||
|
||||
var option = null;
|
||||
this.question.list.forEach((qgroup) => {
|
||||
// 获取question_index,考虑到关联选项
|
||||
var question_index = qgroup.relation_question_index || 0;
|
||||
if (question_index != ware.question_index) return;
|
||||
qgroup.options.forEach((qoption) => {
|
||||
if (qoption.option_index != ware.option_index) return;
|
||||
option = qoption;
|
||||
});
|
||||
});
|
||||
|
||||
if (!option) return;
|
||||
|
||||
this.$emit('onHoldToCart', option);
|
||||
},
|
||||
onHold(hold) {
|
||||
this.hold = hold;
|
||||
},
|
||||
// 用户行为记录
|
||||
onBehaviorFlush(data) {
|
||||
// 判断是否记录
|
||||
if (!this.config.is_behavior) return;
|
||||
|
||||
if (!data[0]) return;
|
||||
|
||||
if (!this.answerSn || !this.answerSurveySn) return;
|
||||
|
||||
BrowsingRecordApi.browsingRecordSurveys({
|
||||
sn: this.answerSurveySn,
|
||||
data: {
|
||||
sn: this.answerSn,
|
||||
question_index: this.question.question_index,
|
||||
question_type: this.question.question_type,
|
||||
planet_id: data[0].wareId,
|
||||
shelve_planet_id: data[0].shelfId,
|
||||
action_info: data
|
||||
}
|
||||
}).catch((e) => console.error(e));
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'viewer.utils';
|
||||
|
||||
.answerviewer {
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
// width: 100vw;
|
||||
// height: 100vh;
|
||||
// position: fixed;
|
||||
background-color: #fff;
|
||||
|
||||
// z-index: 99;
|
||||
}
|
||||
|
||||
// .answerviewerVague {
|
||||
// .q-content-mask {
|
||||
// position: fixed;
|
||||
// left: 0;
|
||||
// right: 0;
|
||||
// top: 0;
|
||||
// bottom: 0;
|
||||
// background-color: rgba($color: #000000, $alpha: 0.3);
|
||||
// }
|
||||
// :deep(canvas) {
|
||||
// filter: blur(5px);
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<SceneSurveyViewer
|
||||
ref="SceneSurveyViewer"
|
||||
:shopData="shopData"
|
||||
:surveyId="surveyId"
|
||||
:hidden="true"
|
||||
@onLoadingCompletion="onLoadingCompletion"
|
||||
:page="page"
|
||||
:page_shelves="page_shelves"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BrowsingRecordApi } from '../js/api';
|
||||
import SceneSurveyViewer from './SceneSurveyViewerPage/Index.vue';
|
||||
|
||||
const has3dPages = ['/answer'];
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SceneSurveyViewer
|
||||
},
|
||||
props: {
|
||||
// 问题列表
|
||||
questions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
first3DQuestion: null,
|
||||
sceneInformation: null,
|
||||
// 场景信息
|
||||
shopData: null,
|
||||
// 场景ID
|
||||
surveyId: null,
|
||||
// 商品摆放信息
|
||||
page: null,
|
||||
page_shelves: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
canUse3D() {
|
||||
return has3dPages.includes(this.$route.path);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
questions: {
|
||||
async handler(arr) {
|
||||
if (!this.canUse3D) return;
|
||||
|
||||
// 3D资源预获取
|
||||
try {
|
||||
var target = arr.find((question) => question?.config?.is_three_dimensions);
|
||||
this.first3DQuestion = target;
|
||||
|
||||
if (!target) return;
|
||||
|
||||
this.sceneInformation = JSON.parse(target?.config?.scene_information);
|
||||
|
||||
if (!this.sceneInformation) {
|
||||
// 解决缓存问题,答卷时加载场景信息
|
||||
var res = await BrowsingRecordApi.getSurveysScene({
|
||||
sn: this.$route.query.sn,
|
||||
question_index: target.question_index
|
||||
});
|
||||
this.sceneInformation = JSON.parse(res.data?.scene_information);
|
||||
}
|
||||
|
||||
if (!this.sceneInformation) return;
|
||||
// 初始化
|
||||
this.surveyId = target.config.scene;
|
||||
this.$refs.SceneSurveyViewer.init();
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onLoadingCompletion() {
|
||||
// 3D资源预获取
|
||||
try {
|
||||
var target = this.first3DQuestion;
|
||||
|
||||
if (!target) return;
|
||||
|
||||
var scene = this.sceneInformation;
|
||||
if (!scene) return;
|
||||
|
||||
scene.shelves.forEach((shelf) => {
|
||||
var wares = shelf.wares;
|
||||
shelf.cells = shelf.cells.map((cell, index) => {
|
||||
return {
|
||||
...cell,
|
||||
...wares[index % wares.length],
|
||||
showSign: !!target.config?.is_price_tag,
|
||||
showLogo: false
|
||||
};
|
||||
});
|
||||
});
|
||||
this.page_shelves = {
|
||||
shelves: this.shopData.shelves
|
||||
};
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
49
src/views/Survey/views/Preview/components/LangTranslate.vue
Normal file
49
src/views/Survey/views/Preview/components/LangTranslate.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot name="prefix" />
|
||||
<template v-for="(item, index) in text" :key="index">
|
||||
{{ item }}
|
||||
<br v-if="index !== text.length - 1" />
|
||||
</template>
|
||||
<slot name="suffix" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, defineProps } from 'vue';
|
||||
import { getLanguage } from '../js/language';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useQuestionStore } from '@/stores/Questions/useQuestionStore';
|
||||
const props = defineProps({
|
||||
langTypes: { type: Array, default: () => undefined },
|
||||
translateKey: { type: String, default: '' },
|
||||
translateParams: { type: Array, default: () => [] },
|
||||
fullText: { type: String, default: '' }
|
||||
});
|
||||
|
||||
const {questionsData : data } = storeToRefs(useQuestionStore())
|
||||
const text = computed(() => {
|
||||
if (props.fullText) {
|
||||
return props.fullText.split('\n');
|
||||
}
|
||||
if (!props.translateKey) {
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
const style = data.value.survey?.style || {};
|
||||
const styleTypes = data.value.languageType
|
||||
? data.value.languageType
|
||||
: [style.is_en_tips ? 'en' : '', style.is_cn_tips ? 'zh' : ''].filter((i) => !!i);
|
||||
const types = props.langTypes ? props.langTypes : styleTypes;
|
||||
const result = getLanguage(types)[props.translateKey];
|
||||
|
||||
if (typeof result === 'string') {
|
||||
return result.split('\n');
|
||||
} else {
|
||||
return result(...(props.translateParams || [])).split('\n');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<div class="pagination-mob">
|
||||
<!-- mob端 -->
|
||||
<a-spin
|
||||
v-show="showPrevious && page > min && page < pages"
|
||||
:spinning="prevLoading"
|
||||
wrapperClassName="spin prev-spin"
|
||||
>
|
||||
<div
|
||||
class="pfe-button btn previous my-btn"
|
||||
@click="previous"
|
||||
:style="`color: ${buttonTextColor};background-color: ${buttonColor}`"
|
||||
>
|
||||
<i class="iconfont"></i>
|
||||
<span>{{ prevButtonText }}</span>
|
||||
</div>
|
||||
</a-spin>
|
||||
<a-spin v-show="page < pages" :spinning="loading" wrapperClassName="spin">
|
||||
<div
|
||||
class="pfe-button btn next my-btn"
|
||||
:class="nextDisabled ? 'disabled' : ''"
|
||||
@click="next"
|
||||
:style="`color: ${buttonTextColor};background-color: ${buttonColor}`"
|
||||
>
|
||||
<span>{{
|
||||
showStart && page === 0
|
||||
? startText
|
||||
: showSubmit && page + 1 === pages
|
||||
? submitText
|
||||
: nextText || nextButtonText
|
||||
}}</span>
|
||||
<i v-show="page + 1 !== pages" class="iconfont"></i>
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import {storeToRefs} from 'pinia';
|
||||
import PfeButton from './PfeButton.vue';
|
||||
import { useQuestionStore } from '@/stores/Questions/useQuestionStore';
|
||||
|
||||
// 引入 Spin 组件
|
||||
import { Spin as ASpin } from 'ant-design-vue';
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: { PfeButton },
|
||||
props: {
|
||||
// 当前页数
|
||||
page: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 总页数
|
||||
pages: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 最小页数
|
||||
min: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 显示上一页按钮
|
||||
showPrevious: {
|
||||
type: [Boolean, Number],
|
||||
default: false
|
||||
},
|
||||
// 显示开始按钮
|
||||
showStart: {
|
||||
type: [Boolean, Number],
|
||||
default: false
|
||||
},
|
||||
// 开始按钮文字
|
||||
startText: {
|
||||
type: String,
|
||||
default: '开始'
|
||||
},
|
||||
// 显示提交按钮
|
||||
showSubmit: {
|
||||
type: [Boolean, Number],
|
||||
default: false
|
||||
},
|
||||
// 提交按钮文字
|
||||
submitText: {
|
||||
type: String,
|
||||
default: '提交'
|
||||
},
|
||||
// 按钮背景颜色
|
||||
buttonColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 按钮文字颜色
|
||||
buttonTextColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 下一页按钮文字
|
||||
nextText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 下一页按钮禁用
|
||||
nextDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否移动端
|
||||
isMobile: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
prevLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, context) {
|
||||
const {questionsData} = storeToRefs(useQuestionStore())
|
||||
|
||||
const prevButtonText = computed(() => questionsData.value?.survey?.style?.up_button_text || '上一页');
|
||||
const nextButtonText = computed(() => questionsData.value?.survey?.style?.next_button_text || '下一页');
|
||||
|
||||
// 上一页
|
||||
function previous() {
|
||||
if (props.page > props.min) {
|
||||
context.emit('previous');
|
||||
context.emit('update:page', props.page - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 下一页
|
||||
function next() {
|
||||
if (props.nextDisabled) return;
|
||||
if (props.page < props.pages) {
|
||||
context.emit('next');
|
||||
context.emit('update:page', props.page + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
prevButtonText,
|
||||
nextButtonText,
|
||||
previous,
|
||||
next
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.p-btn {
|
||||
min-width: 96px;
|
||||
margin: 0 12px;
|
||||
border: 0;
|
||||
|
||||
:deep(span) {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-mob {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.spin {
|
||||
width: calc(50% - 30px);
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.prev-spin {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: calc(50% - 30px);
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.iconfont {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.previous {
|
||||
background-color: #fff;
|
||||
color: #70b936;
|
||||
|
||||
// border: 1px solid $yili-default-color;
|
||||
//margin-right: 20px;
|
||||
|
||||
.iconfont {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.next {
|
||||
background: #70b936;
|
||||
color: #fff;
|
||||
|
||||
.iconfont {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.my-btn:not(.disabled):active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="pfe-button" :class="`type-${type} size-${size}`">
|
||||
<i v-if="icon" class="iconfont left-icon">{{ icon }}</i>
|
||||
<span>{{ text }}</span>
|
||||
<i v-if="rightIcon" class="iconfont right-icon">{{ rightIcon }}</i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
rightIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pfe-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
.left-icon {
|
||||
margin-right: 6px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
margin-left: 6px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.type-primary {
|
||||
border: 1px solid #70b936;
|
||||
background: #70b936;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.type-primary:hover {
|
||||
border: 1px solid #70b936;
|
||||
background: #70b936;
|
||||
}
|
||||
|
||||
.type-default {
|
||||
border: 1px solid #70b936;
|
||||
background-color: #fff;
|
||||
color: #70b936;
|
||||
}
|
||||
|
||||
.type-default:hover {
|
||||
color: #70b936;
|
||||
}
|
||||
|
||||
.size-default {
|
||||
height: 42px;
|
||||
padding: 0 20px;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.size-small {
|
||||
height: 32px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
.left-icon {
|
||||
margin-right: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
margin-left: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="progress">
|
||||
<div class="bar" :style="`width: ${percent}%;background-color: ${color}`" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
percent: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.progress {
|
||||
height: 8px;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
|
||||
.bar {
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
transition: width 0.5s;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
76
src/views/Survey/views/Preview/components/Remark/index.vue
Normal file
76
src/views/Survey/views/Preview/components/Remark/index.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="remark-section">
|
||||
<div class="icon" v-if="isPreview">
|
||||
<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 { useStore } from 'vuex';
|
||||
// import {} from "@/stores/counter.js"
|
||||
|
||||
// 屏蔽 cheerio
|
||||
// import * as cheerio from 'cheerio';
|
||||
|
||||
// import useEmitter from '@/composables/useEmitter';
|
||||
import { ElMessage as message } from 'element-plus';
|
||||
import { useCommonStore } from '@/stores/modules/common';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
questionIndex: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { visible } = storeToRefs(useCommonStore());
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const sn = computed(() => route.query.sn);
|
||||
const isLogin = ref(window.localStorage.getItem('plantToken'));
|
||||
const isPreview = computed(() => route.path === '/preview');
|
||||
|
||||
function openDrawer() {
|
||||
if (!isLogin.value) {
|
||||
message.success('您尚未登陆,请先登陆,再评论');
|
||||
router.push({
|
||||
name: 'login'
|
||||
});
|
||||
return;
|
||||
}
|
||||
const title = props.title.trim();
|
||||
|
||||
visible.value = true;
|
||||
}
|
||||
return {
|
||||
sn,
|
||||
// visible,
|
||||
openDrawer,
|
||||
isLogin,
|
||||
isPreview
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.remark-section {
|
||||
cursor: pointer;
|
||||
|
||||
.icon-jieshuyu {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<div class="base">
|
||||
<div ref="target" class="container-viewer-1-"></div>
|
||||
<div ref="elCart" style="position: absolute; right: 80px; bottom: 80px"></div>
|
||||
<div v-if="spinning" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; background-color: #fff">
|
||||
<a-spin style="position: absolute; top: 50%; left: 50%" />
|
||||
</div>
|
||||
<div v-if="freezeRotY" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const viewer_ = null;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
// JoyStick,
|
||||
// WayPointsList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
surveyId: null,
|
||||
shopData: null,
|
||||
page: null,
|
||||
page_shelves: null,
|
||||
sceneAction: null,
|
||||
elCart: null,
|
||||
defaultWare: null,
|
||||
|
||||
isLocked: null,
|
||||
freezeRotY: false,
|
||||
|
||||
spinning: true,
|
||||
|
||||
// 此变量只是
|
||||
hold: null,
|
||||
|
||||
isWayPointMode: false,
|
||||
useJoyStick: false
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
page(newVal /* oldVal */) {
|
||||
// console.log('page ........', newVal, oldVal)
|
||||
if (viewer_) {
|
||||
viewer_.flyAnimation = !(this.defaultWare?.planetid && this.isLocked);
|
||||
viewer_.frostFarScene = this.isLocked;
|
||||
|
||||
viewer_.arrange(newVal).then(() => {
|
||||
// this.$emit("onPageCompletion");
|
||||
setTimeout(() => {
|
||||
this.spinning = false;
|
||||
}, 200);
|
||||
window.parent.postMessage(
|
||||
JSON.stringify({
|
||||
command: 'onPageCompletion'
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
|
||||
// #20221018
|
||||
viewer_.hold({
|
||||
wareId: this.defaultWare?.planetid,
|
||||
keepHold: this.isLocked
|
||||
// 组件内置的固定角度有问题,单独通过遮罩实现
|
||||
// freezeRotY: this.freezeRotY ? "0" : undefined,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
page_shelves(newVal, oldVal) {
|
||||
console.log('page ........', newVal, oldVal);
|
||||
if (viewer_) {
|
||||
viewer_.arrange_shelves(newVal).then(() => {
|
||||
// this.$emit("onPageCompletion");
|
||||
setTimeout(() => {
|
||||
this.spinning = false;
|
||||
}, 200);
|
||||
window.parent.postMessage(
|
||||
JSON.stringify({
|
||||
command: 'onPageCompletion'
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
|
||||
// #20221018
|
||||
viewer_.hold({
|
||||
wareId: this.defaultWare?.planetid,
|
||||
keepHold: this.isLocked
|
||||
// 组件内置的固定角度有问题,单独通过遮罩实现
|
||||
// freezeRotY: this.freezeRotY ? "0" : undefined,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
sceneAction(newVal /* oldVal */) {
|
||||
if (viewer_ && newVal) {
|
||||
viewer_.action(newVal.action);
|
||||
}
|
||||
},
|
||||
elCart(newVal /* oldVal */) {
|
||||
if (viewer_ && newVal) {
|
||||
// viewer_.elCart = newVal;
|
||||
viewer_.elCart = this.$refs.elCart;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('message', (e) => {
|
||||
let data = null;
|
||||
try {
|
||||
data = JSON.parse(e.data);
|
||||
} catch (e) {
|
||||
// mark
|
||||
}
|
||||
if (data?.command === 'setData') {
|
||||
Object.keys(data.data).forEach((key) => {
|
||||
this[key] = data.data[key];
|
||||
});
|
||||
this.$nextTick(() => this.tryInitView());
|
||||
}
|
||||
});
|
||||
|
||||
window.parent.postMessage(
|
||||
JSON.stringify({
|
||||
command: 'getData'
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
},
|
||||
// beforeUnmount: function () {
|
||||
// if (viewer_) {
|
||||
// viewer_.dispose();
|
||||
// viewer_ = null;
|
||||
// }
|
||||
// },
|
||||
|
||||
methods: {
|
||||
debounce(func, wait) {
|
||||
let timeout;
|
||||
return function(...args) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||
};
|
||||
},
|
||||
joyChange(e) {
|
||||
if (viewer_) {
|
||||
viewer_.doJoystick(e);
|
||||
}
|
||||
},
|
||||
|
||||
gotoWayPoint(e) {
|
||||
if (viewer_) {
|
||||
viewer_.gotoWayPoint(e.wayPoint);
|
||||
}
|
||||
},
|
||||
|
||||
tryInitView() {
|
||||
if (!viewer_) {
|
||||
// const isHall = this.shopData.type === 'hall';
|
||||
// this.useJoyStick = this.shopData.type == "hall";
|
||||
// useJoyStick: isHall && this.shopData?.hall.useRoaming // #20230802
|
||||
/* useJoyStick: isHall; *//* && isHallCanWalk(this.shopData.hall); */ // #20230811
|
||||
|
||||
const sd = JSON.parse(JSON.stringify(this.shopData || {}));
|
||||
sd.shelves.forEach((shelve) => {
|
||||
shelve.hideWhenSurvey = true; // hideWhenSurvey 改成 true 时,不显示展台的紫色边框
|
||||
});
|
||||
|
||||
// viewer_ = new SurveyViewer({
|
||||
// container: this.$refs.target,
|
||||
// surveyId: this.surveyId,
|
||||
// shopData: sd,
|
||||
//
|
||||
// prefixAsset: '/shelves-v5-asset' // v5, #20230104
|
||||
// });
|
||||
viewer_.on('loadingCompletion', () => {
|
||||
// this.$emit("onLoadingCompletion");
|
||||
window.parent.postMessage(
|
||||
JSON.stringify({
|
||||
command: 'onLoadingCompletion'
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
});
|
||||
|
||||
const changeHoldDebounce = this.debounce((d) => (this.hold = d), 500);
|
||||
viewer_.on('hold', (d) => {
|
||||
this.hold = null;
|
||||
// this.$emit("onHold", d);
|
||||
window.parent.postMessage(
|
||||
JSON.stringify({
|
||||
command: 'onHold',
|
||||
data: d
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
|
||||
changeHoldDebounce(d);
|
||||
});
|
||||
viewer_.on('from_scene_hold_to_shelf', () => {
|
||||
// this.$emit("onFromSceneHoldToShelf");
|
||||
|
||||
if (!this.hold) return;
|
||||
|
||||
this.sceneAction = {
|
||||
action: 'hold_to_shelf'
|
||||
};
|
||||
window.parent.postMessage(
|
||||
JSON.stringify({
|
||||
command: 'onFromSceneHoldToShelf'
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
|
||||
this.hold = null;
|
||||
});
|
||||
viewer_.on('behavior_flush', (q1) => {
|
||||
// this.$emit("onBehaviorFlush", q1);
|
||||
window.parent.postMessage(
|
||||
JSON.stringify({
|
||||
command: 'onBehaviorFlush',
|
||||
data: q1
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
});
|
||||
|
||||
viewer_.startup();
|
||||
}
|
||||
return viewer_;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.base {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div.container-viewer-1- {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div.container-viewer-1-:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
//div.container-viewer-1- > > > canvas:focus {
|
||||
// outline: none;
|
||||
//}
|
||||
|
||||
.bottom-points-list {
|
||||
bottom: 140px;
|
||||
|
||||
:deep(.yo-thumb .yo-obj-fit-cover) {
|
||||
object-fit: cover;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.yo-thumb) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1427
src/views/Survey/views/Preview/components/json/red.json
Normal file
1427
src/views/Survey/views/Preview/components/json/red.json
Normal file
File diff suppressed because it is too large
Load Diff
137
src/views/Survey/views/Preview/components/questions/QFirst.vue
Normal file
137
src/views/Survey/views/Preview/components/questions/QFirst.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div class="question-introduce-container">
|
||||
<div v-if="showTitle" class="title-part">
|
||||
<rich-text
|
||||
isPreview
|
||||
:nodes="title"
|
||||
class="title"
|
||||
:class="isMobile ? 'm-title' : ''"
|
||||
:style="`color: ${themeColor.stemColor}`"
|
||||
/>
|
||||
<Remark :title="label + '标题评论'" :type="1" v-if="!isAnswer" />
|
||||
</div>
|
||||
<div v-if="showDesc" class="desc-part" :class="isMobile ? 'm-desc-part' : ''">
|
||||
<rich-text
|
||||
isPreview
|
||||
:nodes="desc"
|
||||
: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>
|
||||
</div>
|
||||
<!-- <AnswerViewerPrefetch :questions="questions" />-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import RichText from '@/components/RichText.vue';
|
||||
import Remark from '../../components/Remark/index.vue';
|
||||
|
||||
import AnswerViewerPrefetch from '../AnswerViewerPrefetch.vue';
|
||||
import { useQuestionStore } from '@/stores/Questions/useQuestionStore';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
export default defineComponent({
|
||||
components: { RichText, Remark, AnswerViewerPrefetch },
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
desc: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 是否移动端
|
||||
isMobile: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否答题模式
|
||||
isAnswer: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 显示标题
|
||||
showTitle: {
|
||||
type: [Boolean, Number],
|
||||
default: false
|
||||
},
|
||||
// 显示介绍语
|
||||
showDesc: {
|
||||
type: [Boolean, Number],
|
||||
default: false
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//
|
||||
questions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const { themeColor } = storeToRefs(useQuestionStore());
|
||||
return { themeColor };
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.question-introduce-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title-part {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.desc-part {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.m-desc-part {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
flex: 1;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.m-title {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.m-desc {
|
||||
flex: 1;
|
||||
margin-top: 30px;
|
||||
|
||||
:deep(img) {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
325
src/views/Survey/views/Preview/components/questions/QLast.vue
Normal file
325
src/views/Survey/views/Preview/components/questions/QLast.vue
Normal file
@@ -0,0 +1,325 @@
|
||||
<template>
|
||||
<div class="question-concluse-wrapper">
|
||||
<div class="content">
|
||||
<div v-if="isTemplate" v-html="survey.success_end_content" />
|
||||
|
||||
<div v-if="code === 20004" v-html="survey.screening_end_content" />
|
||||
<div v-else-if="code === 20011" v-html="survey.success_end_content" />
|
||||
<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" />-->
|
||||
</div>
|
||||
<!-- 立即跳转动画 -->
|
||||
<div v-if="isEndUrl" class="animation-wrapper">
|
||||
<div ref="animation" class="animation" @click="toEndUrl"></div>
|
||||
</div>
|
||||
<!-- 跳转弹窗 -->
|
||||
<div v-if="isEndUrl && countTime >= 0" class="mask">
|
||||
<div class="modal">
|
||||
<div class="m-title">
|
||||
<LangTranslate
|
||||
v-if="countTime > 0"
|
||||
translate-key="QLast_RedirectingIn5Seconds"
|
||||
:translate-params="[countTime]"
|
||||
/>
|
||||
<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 translate-key="QLast_Redirect" class="m-btn" @click="jump" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LangTranslate
|
||||
v-if="isAnswer && questionsData.survey?.style?.is_yip"
|
||||
translate-key="PoweredByDigitalTechnologyCenterYIP"
|
||||
class="footer"
|
||||
>
|
||||
<template #prefix>
|
||||
<img src="https://diaoyan-files.automark.cc/yili_logo.png" alt="" class="yip-icon" />
|
||||
</template>
|
||||
</LangTranslate>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, defineComponent, getCurrentInstance, ref, inject, 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"
|
||||
export default defineComponent({
|
||||
props: {
|
||||
action: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
code: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
survey: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
// 是否答题模式
|
||||
isAnswer: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isTemplate: { type: Boolean, default: false }
|
||||
},
|
||||
components: {
|
||||
// Remark,
|
||||
LangTranslate
|
||||
},
|
||||
setup(props) {
|
||||
const { proxy } = getCurrentInstance();
|
||||
// const questionsData = inject('questionsData'); // 问卷数据
|
||||
const {questionsData} = storeToRefs(useQuestionStore());
|
||||
console.log(11, questionsData, props.survey);
|
||||
let countTimer; // 跳转计时器
|
||||
const animation = ref(redJson); // 立即跳转动画
|
||||
const countTime = ref(0); // 跳转倒计时
|
||||
|
||||
// 是否有跳转链接
|
||||
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 === 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)
|
||||
);
|
||||
});
|
||||
|
||||
// 跳转
|
||||
function toEndUrl() {
|
||||
switch (props.action.code) {
|
||||
case 20004: // 被甄别
|
||||
if (props.survey.screening_end_url_select && props.survey.screening_end_url) {
|
||||
const url = props.survey.screening_end_url;
|
||||
toUrl(url);
|
||||
}
|
||||
break;
|
||||
case 20011: // 成功
|
||||
if (props.survey.success_end_url_select && props.survey.success_end_url) {
|
||||
const url = props.survey.success_end_url;
|
||||
toUrl(url);
|
||||
}
|
||||
break;
|
||||
case 20016: // 配额超限
|
||||
if (props.survey.quota_end_url_select && props.survey.quota_end_url) {
|
||||
const url = props.survey.quota_end_url;
|
||||
toUrl(url);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转链接
|
||||
function toUrl(url) {
|
||||
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 cuontDown() {
|
||||
if (isEndUrl.value) {
|
||||
if (props.action.code === 20004) {
|
||||
countTime.value = props.survey.screening_standing_time;
|
||||
} else if (props.action.code === 20011) {
|
||||
countTime.value = props.survey.success_standing_time;
|
||||
} else if (props.action.code === 20016) {
|
||||
countTime.value = props.survey.quota_standing_time;
|
||||
}
|
||||
|
||||
countTimer = setInterval(() => {
|
||||
if (countTime.value === 0) {
|
||||
toEndUrl();
|
||||
clearInterval(countTimer);
|
||||
} else {
|
||||
countTime.value -= 1;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 停止跳转
|
||||
function stopJump() {
|
||||
countTime.value = -1;
|
||||
clearInterval(countTimer);
|
||||
}
|
||||
|
||||
// 立即跳转
|
||||
function jump() {
|
||||
toEndUrl();
|
||||
stopJump();
|
||||
}
|
||||
|
||||
function loadAnimation() {
|
||||
// 立即跳转动画
|
||||
if (animation.value) {
|
||||
// lottie.loadAnimation({
|
||||
// container: animation.value,
|
||||
// animationData: lottieJson
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
cuontDown();
|
||||
loadAnimation();
|
||||
});
|
||||
|
||||
return {
|
||||
questionsData,
|
||||
animation,
|
||||
isEndUrl,
|
||||
toEndUrl,
|
||||
countTime,
|
||||
stopJump,
|
||||
jump
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.question-concluse-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.comment {
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.animation-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.animation {
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
margin: 50px auto 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
background-color: rgba(1, 21, 53, 0.7);
|
||||
//filter: alpha(opacity=50);
|
||||
|
||||
.red-animation {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.receive {
|
||||
width: 120px;
|
||||
height: 34px;
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(135deg, #f78f4f 0%, #e94435 100%);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 34px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal {
|
||||
width: 80%;
|
||||
max-width: 300px;
|
||||
border-radius: 10px;
|
||||
background-color: #fff;
|
||||
text-align: center;
|
||||
|
||||
.m-title {
|
||||
padding: 30px 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.m-bottom {
|
||||
display: flex;
|
||||
|
||||
.m-btn {
|
||||
flex: 1;
|
||||
padding: 20px 0;
|
||||
border-top: 1px solid #f5f5f5;
|
||||
color: #1c6fff;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.m-btn.border-right {
|
||||
border-right: 1px solid #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
color: #a3a3a3;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
|
||||
.yip-icon {
|
||||
width: auto !important;
|
||||
height: 20px !important;
|
||||
margin-right: 6px;
|
||||
margin-bottom: 0 !important;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
378
src/views/Survey/views/Preview/components/questions/QRadio.vue
Normal file
378
src/views/Survey/views/Preview/components/questions/QRadio.vue
Normal file
@@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<a-radio-group v-model:value="value" @change="changeValue" :disabled="disabled" v-if="customRadio">
|
||||
<div class="radio-group">
|
||||
<div class="group" v-for="(group, groupIndex) in optionGroups" :key="groupIndex">
|
||||
<div
|
||||
v-if="group.title && !config.option_groups?.is_hide"
|
||||
v-show="showGroupTitle(group.options)"
|
||||
class="group-title answer-color"
|
||||
>
|
||||
{{ group.title }}
|
||||
</div>
|
||||
<div
|
||||
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)"
|
||||
@click="onChangeValue(option.option_key)"
|
||||
>
|
||||
<a-radio :value="option.option_key" @click.stop />
|
||||
<div class="option scrollbar answer-color">
|
||||
<rich-text :nodes="option.option" isPreview />
|
||||
</div>
|
||||
<a-input
|
||||
v-if="option.is_other"
|
||||
v-model:value="option.value"
|
||||
@change="changeInput($event, option.option_key)"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-radio-group>
|
||||
<choice :element="question" v-else />
|
||||
</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';
|
||||
|
||||
export default defineComponent({
|
||||
components: { RichText, AnswerViewer },
|
||||
props: {
|
||||
// 题干
|
||||
stem: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 列表
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 配置
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
}
|
||||
},
|
||||
// 答案
|
||||
answer: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
}
|
||||
},
|
||||
// 答案索引
|
||||
answerIndex: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 隐藏选项
|
||||
hideOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 是否禁用
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否移动端
|
||||
isMobile: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 样本SN
|
||||
answerSn: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
answerSurveySn: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
question: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup(props, context) {
|
||||
const value = ref(''); // 值
|
||||
const options = ref([]); // 选项
|
||||
const optionGroups = ref([]); // 分组
|
||||
|
||||
console.log(`radio input question: `, props.question);
|
||||
|
||||
// 初始化
|
||||
function init() {
|
||||
props.list.forEach((list) => {
|
||||
options.value = [...options.value, ...list.options];
|
||||
});
|
||||
if (props.answer) {
|
||||
value.value = Object.keys(props.answer)[0];
|
||||
options.value.forEach((option) => {
|
||||
if (option.is_other && option.option_key === value.value) {
|
||||
option.value = props.answer[value.value];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
// 选项分组
|
||||
function setOptionGroups() {
|
||||
optionGroups.value = [];
|
||||
const copyOptions = JSON.parse(JSON.stringify(options.value));
|
||||
props.config.option_groups?.option_group.forEach((optionGroup) => {
|
||||
const group = {
|
||||
title: optionGroup.title,
|
||||
options: []
|
||||
};
|
||||
optionGroup.groups.forEach((groups) => {
|
||||
const index = copyOptions.findIndex((option) => option.option_key === groups.option_key);
|
||||
if (index === -1) return;
|
||||
group.options.push(copyOptions.splice(index, 1)[0]);
|
||||
});
|
||||
group.options = sortOptions(
|
||||
group.options,
|
||||
props.question.config.select_random && props.question.config.option_group_random_inside
|
||||
);
|
||||
optionGroups.value.push(group);
|
||||
});
|
||||
optionGroups.value = sortOptions(
|
||||
optionGroups.value,
|
||||
props.question.config.select_random && props.question.config.option_group_random_outside
|
||||
);
|
||||
// 若是 group 是undefined ,默认给一个空对象
|
||||
const group = optionGroups.value.find((group) => !group.title) ?? {};
|
||||
// console.log(group);
|
||||
group.options = sortOptions(copyOptions, props.question.config.select_random);
|
||||
}
|
||||
|
||||
// 排序(固定项和其他项不参与随机)
|
||||
function sortOptions(oldOptions, isRandom) {
|
||||
const sorts = [];
|
||||
const fixed = [];
|
||||
const others = [];
|
||||
const removeOther = [];
|
||||
oldOptions.forEach((option) => {
|
||||
if (option.is_remove_other) {
|
||||
removeOther.push(option);
|
||||
} else if (option.is_other) {
|
||||
others.push(option);
|
||||
} else if (option.is_fixed) {
|
||||
fixed.push(option);
|
||||
} else {
|
||||
sorts.push(option);
|
||||
}
|
||||
});
|
||||
return [...randomOptions(sorts, isRandom), ...fixed, ...others, ...removeOther];
|
||||
}
|
||||
|
||||
// 选择回调
|
||||
function changeValue(e) {
|
||||
// 更新答案
|
||||
const option = options.value.find((option) => option.option_key === e.target.value);
|
||||
context.emit('update:answer', {
|
||||
[e.target.value]: option.value || (option.is_other ? '' : '1')
|
||||
});
|
||||
// 清空未选中项输入框值
|
||||
options.value.forEach((option) => {
|
||||
if (option.is_other && option.option_key !== e.target.value) {
|
||||
option.value = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onChangeValue(key) {
|
||||
value.value = key;
|
||||
changeValue({ target: { value: key } });
|
||||
}
|
||||
|
||||
// 输入回调
|
||||
function changeInput(e, key) {
|
||||
const option = options.value.find((option) => option.option_key === key);
|
||||
option.value = e.target.value;
|
||||
value.value = key;
|
||||
// 更新答案
|
||||
context.emit('update:answer', { [key]: e.target.value });
|
||||
}
|
||||
|
||||
// 监听答案
|
||||
watch(
|
||||
() => props.answer,
|
||||
() => {
|
||||
context.emit('changeAnswer', {
|
||||
options: optionGroups.value.flatMap((group) => group.options || []),
|
||||
value: value.value
|
||||
});
|
||||
// 质量控制
|
||||
const timer = setTimeout(() => {
|
||||
if (value.value) {
|
||||
const index = optionGroups.value
|
||||
.flatMap((group) => group.options.map((option) => option))
|
||||
.findIndex((option) => option.option_key === value.value);
|
||||
context.emit('update:answerIndex', index + '');
|
||||
} else if (props.answerIndex) {
|
||||
context.emit('update:answerIndex', '');
|
||||
}
|
||||
clearTimeout(timer);
|
||||
});
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
// 监听list,更新关联选项
|
||||
watch(
|
||||
() => props.list,
|
||||
() => {
|
||||
// 更新关联题选项
|
||||
let newOptions = [];
|
||||
props.list.forEach((list) => {
|
||||
newOptions = [...newOptions, ...list.options];
|
||||
});
|
||||
// 其他项
|
||||
newOptions.forEach((option) => {
|
||||
if (option.is_other && option.option_key === value.value) {
|
||||
const timer = setTimeout(() => {
|
||||
option.value = props.answer[value.value];
|
||||
clearTimeout(timer);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
!compareArrayByField(options.value, newOptions, 'option_key')
|
||||
|| !compareArrayByField(options.value, newOptions, 'option')
|
||||
) {
|
||||
options.value = newOptions;
|
||||
}
|
||||
|
||||
// 清空值和答案
|
||||
if (value.value && options.value.findIndex((option) => option.option_key === value.value) === -1) {
|
||||
// 清空值
|
||||
value.value = '';
|
||||
// 清空答案
|
||||
context.emit('update:answer', null);
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
|
||||
// 监听list,更新关联选项
|
||||
watch(
|
||||
() => options.value,
|
||||
(val, oldVal) => {
|
||||
if (compareArrayByField(val, oldVal || [], 'option_key') && compareArrayByField(val, oldVal || [], 'option')) {
|
||||
return;
|
||||
}
|
||||
setOptionGroups();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
const onHoldToCart = (target) => {
|
||||
value.value = target.option_key;
|
||||
|
||||
changeValue({
|
||||
target: {
|
||||
value: target.option_key
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cartWaresLength = computed(() => {
|
||||
if (!props.answer) return 0;
|
||||
return Object.keys(props.answer).length;
|
||||
});
|
||||
|
||||
console.log(`car wares length:`, cartWaresLength.value);
|
||||
|
||||
// 显示分组标题
|
||||
function showGroupTitle(groupOptions) {
|
||||
const option = groupOptions.find((option) => !props.hideOptions.includes(option.option_key));
|
||||
return !!option;
|
||||
}
|
||||
|
||||
return {
|
||||
value,
|
||||
options,
|
||||
optionGroups,
|
||||
changeValue,
|
||||
onChangeValue,
|
||||
changeInput,
|
||||
onHoldToCart,
|
||||
cartWaresLength,
|
||||
showGroupTitle
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.radio-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: -18px;
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
|
||||
.group-title {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.radio {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 32px;
|
||||
margin-bottom: 6px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 3px;
|
||||
|
||||
:deep(.ant-radio-wrapper) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.option {
|
||||
overflow: auto;
|
||||
margin-right: 8px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.margin-left {
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
.ant-input {
|
||||
width: 225px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-radio-group {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
447
src/views/Survey/views/Preview/components/questions/Question.vue
Normal file
447
src/views/Survey/views/Preview/components/questions/Question.vue
Normal file
@@ -0,0 +1,447 @@
|
||||
<template>
|
||||
<div class="question">
|
||||
<!-- 高级题型不显示 -->
|
||||
<div class="question-inner-wrapper" v-if="questionType <= 100">
|
||||
<div class="title" :style="`color: ${themeColor.stemColor}`">
|
||||
<span v-if="showTitle" class="question-inner-span" v-html="title"></span>
|
||||
<!-- question.stem -->
|
||||
<div class="stem">
|
||||
<rich-text :nodes="`${newTitle}${tip}`" isPreview :isMobile="isMobile" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- <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" />
|
||||
<!-- 题 -->
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RichText from '@/components/RichText.vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
// import Remark from '../components/Remark/index.vue';
|
||||
import LangTranslate from '../LangTranslate.vue';
|
||||
import { useQuestionStore } from '@/stores/Questions/useQuestionStore';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
export default defineComponent({
|
||||
components: { RichText, /*Remark,*/ LangTranslate },
|
||||
props: {
|
||||
// 标题
|
||||
stem: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 标题
|
||||
tip: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 错误
|
||||
error: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 警告
|
||||
warning: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 题型
|
||||
questionType: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 题号
|
||||
questionIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 问卷列表
|
||||
questions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 是否显示题号
|
||||
showTitle: {
|
||||
type: [Boolean, Number],
|
||||
default: false
|
||||
},
|
||||
// 是否移动端
|
||||
isMobile: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否答题模式
|
||||
isAnswer: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
// const themeColor = inject('themeColor'); // 主题颜色
|
||||
const { themeColor } = storeToRefs(useQuestionStore());
|
||||
const buttonColor = themeColor.value?.buttonColor; // 按钮颜色
|
||||
const buttonTextColor = themeColor.value?.buttonTextColor; // 按钮文字颜色
|
||||
const answerColor = themeColor.value?.answerColor || '#333333'; // 答案颜色
|
||||
const answerColor30 = toRgba(answerColor, 0.3); // 背景颜色(透明度30s)
|
||||
const answerColor10 = toRgba(answerColor, 0.1); // 背景颜色(透明度10)
|
||||
const answerBorder = answerColor === '#333333' ? '#d9d9d9' : answerColor; // 边框颜色
|
||||
const answerPlaceholder = toRgba(answerColor, 0.3); // placeholder颜色
|
||||
// 鼠标 hover 时的背景色,目前用于:单选、多选、图片单选、图片多选的选项
|
||||
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
|
||||
+ ')'
|
||||
);
|
||||
}
|
||||
|
||||
const replaceStr = (str, firIndex, lastIndex, char) => {
|
||||
if (str.length == 0) {
|
||||
return '';
|
||||
}
|
||||
if (str.indexOf('<p>') < 0) {
|
||||
return str;
|
||||
}
|
||||
const strAry = str.split('');
|
||||
strAry[firIndex] = char;
|
||||
strAry[lastIndex] = char;
|
||||
return strAry.join('');
|
||||
};
|
||||
|
||||
const newTitle = computed(() => {
|
||||
const cycleIndexStr = props.title?.match(/\.\d/)?.[0] || ''; // 有循环体组的题,需要在没找到被关联的题目情况下,在循环题组内再查找一次
|
||||
let title = props.stem;
|
||||
const matchArr = title.match(/(\[%cite\(.*?\)%\])/g) || [];
|
||||
matchArr.forEach((matchValue) => {
|
||||
const value = matchValue.replace('[%cite(', '').replace(')%]', '');
|
||||
let replacement = ''; // 替换文本
|
||||
// 查找引用问题
|
||||
const question = props.questions.find((question) => {
|
||||
// 矩阵题
|
||||
if (question.question_type >= 8 && question.question_type <= 11) {
|
||||
return question.title === value.split('_R')[0].split('_C')[0];
|
||||
}
|
||||
// 排序题
|
||||
if (question.question_type === 16) {
|
||||
return question.title === value.split('_A')[0];
|
||||
}
|
||||
return question.title === value;
|
||||
})
|
||||
|| props.questions.find((question) => {
|
||||
// 矩阵题
|
||||
if (question.question_type >= 8 && question.question_type <= 11) {
|
||||
return question.title === (value + cycleIndexStr).split('_R')[0].split('_C')[0];
|
||||
}
|
||||
// 排序题
|
||||
if (question.question_type === 16) {
|
||||
return question.title === (value + cycleIndexStr).split('_A')[0];
|
||||
}
|
||||
return question.title === value + cycleIndexStr;
|
||||
});
|
||||
if (question) {
|
||||
let options = []; // 选项
|
||||
question.list.forEach((list) => {
|
||||
options = [...options, ...list.options];
|
||||
});
|
||||
if (question.answer) {
|
||||
const { answer } = question;
|
||||
if (question.question_type === 1) {
|
||||
// 查找引用选项(单选)
|
||||
const option = options.find((option) => option.option_key === Object.keys(answer)[0]);
|
||||
if (answer[option.option_key] === '1') {
|
||||
replacement = option.option;
|
||||
} else {
|
||||
replacement = answer[option.option_key];
|
||||
}
|
||||
} else if (question.question_type === 2 && Object.keys(answer).length >= question.config.min_select) {
|
||||
// 查找引用选项(多选)
|
||||
options.forEach((option) => {
|
||||
if (answer[option.option_key] === '1') {
|
||||
replacement += option.option;
|
||||
} else if (answer[option.option_key]) {
|
||||
replacement += answer[option.option_key];
|
||||
}
|
||||
});
|
||||
} else if (question.question_type === 4) {
|
||||
// 查找引用选项(填空)
|
||||
replacement = answer.value;
|
||||
} else if (question.question_type === 5 && options.length > 0) {
|
||||
// 查找引用选项(打分)
|
||||
options.forEach((option) => {
|
||||
replacement += option.value;
|
||||
});
|
||||
} else if (question.question_type === 17) {
|
||||
// 查找引用选项(恒定总和)
|
||||
options.forEach((option) => {
|
||||
replacement += option.option;
|
||||
});
|
||||
} else if (question.question_type >= 8 && question.question_type <= 11) {
|
||||
// 查找引用选项(矩阵题)
|
||||
// const val = value.split("_").reverse()[0];
|
||||
// let row = [];
|
||||
// let col = [];
|
||||
// question.list.forEach((list) => {
|
||||
// if (list.type === 1) {
|
||||
// row = [...row, ...list.options];
|
||||
// } else if (list.type === 2) {
|
||||
// col = [...col, ...list.options];
|
||||
// }
|
||||
// });
|
||||
// if (val[0] === "R") {
|
||||
// col.forEach((colItem) => {
|
||||
// replacement += colItem.option;
|
||||
// });
|
||||
// } else if (val[0] === "C") {
|
||||
// row.forEach((rowItem) => {
|
||||
// replacement += rowItem.option;
|
||||
// });
|
||||
// }
|
||||
} else if (question.question_type === 16) {
|
||||
// 查找引用选项(排序)
|
||||
let optionKey;
|
||||
const sort = value.split('_A')[1] * 1;
|
||||
Object.keys(answer).forEach((key) => {
|
||||
if (answer[key] === sort) {
|
||||
optionKey = key;
|
||||
}
|
||||
});
|
||||
const option = options.find((option) => option.option_key === optionKey);
|
||||
replacement += option?.option || '';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 引用的p标签变成span标签
|
||||
replacement = replaceStr(replacement, 1, replacement.length - 2, 'span');
|
||||
|
||||
replacement = replacement.replace(/<\/{0,1}(?!img).+?>/gi, '');
|
||||
|
||||
title = title.replace(`${matchValue}`, replacement);
|
||||
// .replaceAll("<label><p ", "<label><span ")
|
||||
// .replaceAll("<label><p>", "<label><span>")
|
||||
// .replaceAll("</p></label>", "</span></label>");
|
||||
console.log('title', title);
|
||||
});
|
||||
return title;
|
||||
});
|
||||
|
||||
return {
|
||||
newTitle,
|
||||
buttonColor,
|
||||
buttonTextColor,
|
||||
themeColor,
|
||||
answerColor,
|
||||
answerColor30,
|
||||
answerColor10,
|
||||
hoverBackgroundColor,
|
||||
answerBorder,
|
||||
answerPlaceholder
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.question {
|
||||
.question-inner-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 24px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
> span {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stem {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
margin: -22px 0 10px;
|
||||
padding-left: 2em;
|
||||
color: #ff374f;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: #f7860f;
|
||||
}
|
||||
|
||||
:deep(p) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// .question-inner-span {
|
||||
// width: 100%;
|
||||
// }
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.theme-hover-default) {
|
||||
&:hover {
|
||||
background-color: v-bind(hoverbackgroundcolor);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-radio-inner) {
|
||||
border-color: v-bind(answercolor) !important;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
:deep(.ant-radio-inner::after) {
|
||||
background-color: v-bind(answercolor);
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-inner) {
|
||||
border-color: v-bind(answercolor) !important;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||
background-color: v-bind(answercolor);
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
border-color: v-bind(answerborder) !important;
|
||||
background-color: transparent;
|
||||
color: v-bind(answercolor);
|
||||
}
|
||||
|
||||
:deep(.ant-input-number) {
|
||||
border-color: v-bind(answerborder) !important;
|
||||
background-color: transparent;
|
||||
color: v-bind(answercolor);
|
||||
}
|
||||
|
||||
:deep(.ant-select-selector) {
|
||||
border-color: v-bind(answerborder) !important;
|
||||
background-color: transparent !important;
|
||||
color: v-bind(answercolor);
|
||||
}
|
||||
|
||||
:deep(.ant-input:hover) {
|
||||
border-color: v-bind(answercolor);
|
||||
}
|
||||
|
||||
:deep(.ant-input::placeholder) {
|
||||
color: v-bind(answerplaceholder) !important;
|
||||
}
|
||||
|
||||
:deep(.ant-select-selection-placeholder) {
|
||||
color: v-bind(answerplaceholder) !important;
|
||||
}
|
||||
|
||||
// :deep(.ant-rate) {
|
||||
// color: v-bind(answerColor) !important;
|
||||
// }
|
||||
|
||||
// :deep(.ant-rate-star-zero .ant-rate-star-second) {
|
||||
// color: v-bind(answerColor30);
|
||||
// }
|
||||
|
||||
// 打分组件样式 start
|
||||
:deep(.num-item), // 旧的组件的样式
|
||||
:deep(.rate-wrapper .number-item .content) {
|
||||
border-color: v-bind(answercolor) !important;
|
||||
color: v-bind(answercolor) !important;
|
||||
}
|
||||
|
||||
:deep(.num-item-active), // 旧的组件的样式
|
||||
:deep(.rate-wrapper .number-item.active .content) {
|
||||
border-color: v-bind(answercolor) !important;
|
||||
background-color: v-bind(answercolor) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
:deep(.rich-rate-item-active) {
|
||||
border-color: v-bind(answercolor) !important;
|
||||
background-color: v-bind(answercolor) !important;
|
||||
}
|
||||
|
||||
:deep(.step-inner) {
|
||||
border-color: v-bind(answercolor) !important;
|
||||
}
|
||||
|
||||
:deep(.ant-slider-track), // 旧的组件的样式
|
||||
:deep(.slider-bar::before) {
|
||||
background-color: v-bind(answercolor) !important;
|
||||
}
|
||||
|
||||
:deep(.slider-step .slider-dot.selected .content::before) {
|
||||
border-color: v-bind(answercolor) !important;
|
||||
}
|
||||
|
||||
:deep(.ant-slider-handle) {
|
||||
border-color: v-bind(answercolor) !important;
|
||||
}
|
||||
|
||||
:deep(.ant-slider-dot-active) {
|
||||
border-color: v-bind(answercolor) !important;
|
||||
}
|
||||
|
||||
:deep(.ant-slider-mark-text) {
|
||||
color: v-bind(answercolor);
|
||||
}
|
||||
|
||||
// 打分组件样式 end
|
||||
|
||||
:deep(.ant-upload) {
|
||||
border-color: v-bind(answerborder) !important;
|
||||
background-color: v-bind(answercolor10) !important;
|
||||
}
|
||||
|
||||
:deep(.pfe-button) {
|
||||
background-color: v-bind(buttoncolor) !important;
|
||||
color: v-bind(buttontextcolor) !important;
|
||||
}
|
||||
|
||||
// 公共样式
|
||||
:deep(.answer-color) {
|
||||
color: v-bind(answercolor) !important;
|
||||
}
|
||||
|
||||
:deep(.answer-border) {
|
||||
border-color: v-bind(answerborder) !important;
|
||||
}
|
||||
|
||||
:deep(.answer-background30) {
|
||||
background-color: v-bind(answercolor30) !important;
|
||||
}
|
||||
|
||||
:deep(.answer-background10) {
|
||||
background-color: v-bind(answercolor10) !important;
|
||||
}
|
||||
</style>
|
||||
516
src/views/Survey/views/Preview/components/questions/mock.json
Normal file
516
src/views/Survey/views/Preview/components/questions/mock.json
Normal file
@@ -0,0 +1,516 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"name": "商品1",
|
||||
"box": {
|
||||
"position": { "x": -0.6093719155636137, "y": 1.1925115714503842, "z": 0.45 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"size": { "x": 0.3736, "y": 0.2259, "z": 0.2179 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": -0.006, "y": -0.029369916622918046, "z": 0.1847 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware1",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can1-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can1.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 190
|
||||
},
|
||||
{
|
||||
"name": "商品2",
|
||||
"box": {
|
||||
"position": { "x": -0.18520626992561548, "y": 1.1908614160723439, "z": 0.45 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"size": { "x": 0.3736, "y": 0.2259, "z": 0.2179 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": -0.006, "y": -0.03174731410176801, "z": 0.1847 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware2",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can2-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can2.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 225
|
||||
},
|
||||
{
|
||||
"name": "商品3",
|
||||
"box": {
|
||||
"position": { "x": 0.2287325535366465, "y": 1.1892382329257407, "z": 0.45 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"size": { "x": 0.3736, "y": 0.2259, "z": 0.2179 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": -0.006, "y": -0.028571714546698213, "z": 0.1847 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware3",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can3-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can3.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 210
|
||||
},
|
||||
{
|
||||
"name": "商品4",
|
||||
"box": {
|
||||
"position": { "x": 0.661393068886308, "y": 1.1909523674363642, "z": 0.4537964589333506 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"size": { "x": 0.3736, "y": 0.2259, "z": 0.2179 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": -0.006, "y": -0.03136253199426353, "z": 0.1847 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware4",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can4-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can4.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 195
|
||||
},
|
||||
{
|
||||
"name": "商品1",
|
||||
"box": {
|
||||
"position": { "x": -0.605500512913119, "y": 0.8538858659191146, "z": 0.45017966628074646 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"size": { "x": 0.37358155846595764, "y": 0.225911445915699, "z": 0.21794167160987854 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": {
|
||||
"x": -0.0027324557304382324,
|
||||
"y": -0.03184773059511403,
|
||||
"z": 0.1743204391002655
|
||||
},
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 0.7393795847892761, "y": 0.05661093816161156, "z": 0.05661093816161156 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.1499999999999999, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware1",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can1-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can1.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 190
|
||||
},
|
||||
{
|
||||
"name": "商品2",
|
||||
"box": {
|
||||
"position": {
|
||||
"x": -0.18658027999214435,
|
||||
"y": 0.8512643658064176,
|
||||
"z": 0.45017966628074646
|
||||
},
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"size": { "x": 0.37358155846595764, "y": 0.225911445915699, "z": 0.21794167160987854 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": 0, "y": -0.02759083972388149, "z": 0.1743204391002655 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 0.7393795847892761, "y": 0.05661093816161156, "z": 0.05661093816161156 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.1499999999999999, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware2",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can2-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can2.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 225
|
||||
},
|
||||
{
|
||||
"name": "商品3",
|
||||
"box": {
|
||||
"position": { "x": 0.2230628973331703, "y": 0.8511807287232012, "z": 0.45017966628074646 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"size": { "x": 0.37358155846595764, "y": 0.225911445915699, "z": 0.21794167160987854 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": {
|
||||
"x": -0.005585700273513794,
|
||||
"y": -0.032213962561231047,
|
||||
"z": 0.1743204391002655
|
||||
},
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 0.7393795847892761, "y": 0.05661093816161156, "z": 0.05661093816161156 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.1499999999999999, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware3",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can3-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can3.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 210
|
||||
},
|
||||
{
|
||||
"name": "商品4",
|
||||
"box": {
|
||||
"position": { "x": 0.6551478924840007, "y": 0.8483097259524031, "z": 0.45 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"size": { "x": 0.3736, "y": 0.2259, "z": 0.2179 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": -0.006, "y": -0.02673691703609271, "z": 0.1847 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware4",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can4-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can4.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 195
|
||||
},
|
||||
{
|
||||
"name": "商品1",
|
||||
"box": {
|
||||
"position": { "x": -0.60433011332925, "y": 0.5125224579062782, "z": 0.45017966628074646 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"size": { "x": 0.37358155846595764, "y": 0.225911445915699, "z": 0.21794167160987854 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": {
|
||||
"x": -0.0027324557304382324,
|
||||
"y": -0.034508354476310954,
|
||||
"z": 0.1743204391002655
|
||||
},
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 0.7393795847892761, "y": 0.05661093816161156, "z": 0.05661093816161156 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15000000000000002, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware1",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can1-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can1.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 190
|
||||
},
|
||||
{
|
||||
"name": "商品2",
|
||||
"box": {
|
||||
"position": { "x": -0.1914175066620415, "y": 0.5095275839341008, "z": 0.45017966628074646 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"size": { "x": 0.37358155846595764, "y": 0.225911445915699, "z": 0.21794167160987854 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": 0, "y": -0.027170852492222397, "z": 0.1743204391002655 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 0.7393795847892761, "y": 0.05661093816161156, "z": 0.05661093816161156 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15000000000000002, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware2",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can2-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can2.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 225
|
||||
},
|
||||
{
|
||||
"name": "商品3",
|
||||
"box": {
|
||||
"position": { "x": 0.22360906333448538, "y": 0.5101818344822482, "z": 0.45017966628074646 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"size": { "x": 0.37358155846595764, "y": 0.225911445915699, "z": 0.21794167160987854 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": {
|
||||
"x": -0.005585700273513794,
|
||||
"y": -0.030842453533999448,
|
||||
"z": 0.1743204391002655
|
||||
},
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 0.7393795847892761, "y": 0.05661093816161156, "z": 0.05661093816161156 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15000000000000002, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware3",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can3-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can3.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 210
|
||||
},
|
||||
{
|
||||
"name": "商品4",
|
||||
"box": {
|
||||
"position": { "x": 0.6482247451401058, "y": 0.504394091002413, "z": 0.45 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"size": { "x": 0.3736, "y": 0.2259, "z": 0.2179 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": -0.006, "y": -0.02471250348374021, "z": 0.1847 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware4",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can4-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can4.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 195
|
||||
},
|
||||
{
|
||||
"name": "商品1",
|
||||
"box": {
|
||||
"position": { "x": -0.6134215690979794, "y": 0.1945505440235138, "z": 0.45017966628074646 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"size": { "x": 0.37358155846595764, "y": 0.225911445915699, "z": 0.21794167160987854 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": {
|
||||
"x": -0.0027324557304382324,
|
||||
"y": -0.024228371171756358,
|
||||
"z": 0.1846828854084015
|
||||
},
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 0.7393795847892761, "y": 0.05661093816161156, "z": 0.05661093816161156 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15000000000000002, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware1",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can1-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can1.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 190
|
||||
},
|
||||
{
|
||||
"name": "商品2",
|
||||
"box": {
|
||||
"position": {
|
||||
"x": -0.19111044416229633,
|
||||
"y": 0.1945505440235138,
|
||||
"z": 0.45017966628074646
|
||||
},
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"size": { "x": 0.37358155846595764, "y": 0.225911445915699, "z": 0.21794167160987854 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": 0, "y": -0.021009267483951266, "z": 0.1846828854084015 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 0.7393795847892761, "y": 0.05661093816161156, "z": 0.05661093816161156 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15000000000000002, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware2",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can2-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can2.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 225
|
||||
},
|
||||
{
|
||||
"name": "商品3",
|
||||
"box": {
|
||||
"position": { "x": 0.21581759477578777, "y": 0.1945505440235138, "z": 0.45017966628074646 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"size": { "x": 0.37358155846595764, "y": 0.225911445915699, "z": 0.21794167160987854 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": {
|
||||
"x": -0.005585700273513794,
|
||||
"y": -0.020448554878266023,
|
||||
"z": 0.1846828854084015
|
||||
},
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 0.7393795847892761, "y": 0.05661093816161156, "z": 0.05661093816161156 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15000000000000002, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware3",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can3-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can3.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 210
|
||||
},
|
||||
{
|
||||
"name": "商品4",
|
||||
"box": {
|
||||
"position": { "x": 0.6432047193626257, "y": 0.19147498005043284, "z": 0.45 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"size": { "x": 0.3736, "y": 0.2259, "z": 0.2179 },
|
||||
"child": {
|
||||
"sign": {
|
||||
"position": { "x": -0.006, "y": -0.013996807328698846, "z": 0.1847 },
|
||||
"rotation": { "x": 0, "y": 0, "z": 0 },
|
||||
"scale": { "x": 1, "y": 1, "z": 1 }
|
||||
},
|
||||
"logo": { "position": { "x": 0, "y": 0.15, "z": 0 } }
|
||||
}
|
||||
},
|
||||
"pileNumber": 6,
|
||||
"pileRotationY": 0,
|
||||
"planetid": "ware4",
|
||||
"surveyWare": {
|
||||
"type": 1,
|
||||
"commodity": {
|
||||
"url": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can4-lod0.glb"
|
||||
},
|
||||
"texture": null,
|
||||
"urlThumb": "https://diaoyanxingqiu.oss-cn-zhangjiakou.aliyuncs.com/3d/shelves/shelves-v3-store/v3.4/ware/milkpowder-can4.png"
|
||||
},
|
||||
"surveyLogo": "",
|
||||
"surveyPrice": 195
|
||||
}
|
||||
]
|
||||
}
|
||||
301
src/views/Survey/views/Preview/components/viewer.utils.scss
Normal file
301
src/views/Survey/views/Preview/components/viewer.utils.scss
Normal file
@@ -0,0 +1,301 @@
|
||||
/* PC端样式 - 开始 */
|
||||
.pc .page {
|
||||
z-index: 999;
|
||||
vertical-align: top;
|
||||
width: 180px;
|
||||
height: 52px;
|
||||
padding: 0;
|
||||
border-radius: 5px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 52px;
|
||||
text-align: center;
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.pc .page .iconfont {
|
||||
vertical-align: bottom;
|
||||
font-size: 26px;
|
||||
line-height: 52px;
|
||||
}
|
||||
|
||||
.pc .cart .iconfont {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.pc .pageNext {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: calc(50% + 20px);
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
.pc .pagePre {
|
||||
position: absolute;
|
||||
right: calc(50% + 20px);
|
||||
bottom: 0;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
.pc .pageNext::before {
|
||||
content: '下一页';
|
||||
vertical-align: top;
|
||||
padding-right: 10px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.pc .pagePre::after {
|
||||
content: '上一页';
|
||||
vertical-align: top;
|
||||
padding-left: 10px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.pc .cart {
|
||||
position: fixed;
|
||||
right: 72px;
|
||||
bottom: 160px;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pc .cart-num {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 6px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: #f00;
|
||||
color: #ffff;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pc .q-content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 998;
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
text-align: center;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.pc .q-content-top {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
z-index: 997;
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
line-height: 30px;
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
//.pc .q-content-top * {
|
||||
//}
|
||||
|
||||
.pc .q-content-top > *:nth-child(2) {
|
||||
/* margin-top: 10px; */
|
||||
}
|
||||
|
||||
.pc .q-content-top .ant-radio {
|
||||
height: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.pc .q-content-top .title {
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.pc .q-content-top .ant-radio-inner::after {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
/* PC端样式 - 结束 */
|
||||
|
||||
/* 移动端样式 - 开始 */
|
||||
.mobile .page {
|
||||
z-index: 999;
|
||||
vertical-align: top;
|
||||
width: 120px;
|
||||
padding: 0;
|
||||
border-radius: 5px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
line-height: 42px;
|
||||
text-align: center;
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mobile .page .iconfont {
|
||||
position: relative;
|
||||
bottom: -2px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.mobile .cart .iconfont {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.mobile .pageNext {
|
||||
position: absolute;
|
||||
right: 32px;
|
||||
bottom: 0;
|
||||
padding-left: 10px;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
.mobile .pagePre {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 32px;
|
||||
padding-right: 10px;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
.mobile .pageNext::before {
|
||||
content: '下一页';
|
||||
vertical-align: top;
|
||||
padding-right: 10px;
|
||||
line-height: 43px;
|
||||
}
|
||||
|
||||
.mobile .pagePre::after {
|
||||
content: '上一页';
|
||||
vertical-align: top;
|
||||
padding-left: 10px;
|
||||
line-height: 43px;
|
||||
}
|
||||
|
||||
.mobile .cart {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
bottom: 90px;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mobile .cart-num {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background-color: #f00;
|
||||
color: #ffff;
|
||||
font-size: 10px;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mobile .q-content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 998;
|
||||
display: inline-block;
|
||||
min-width: 350px;
|
||||
text-align: center;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.mobile .q-content-top {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
z-index: 997;
|
||||
width: 90%;
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
line-height: 30px;
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
//.mobile .q-content-top * {
|
||||
//}
|
||||
|
||||
.mobile .q-content-top > *:nth-child(2) {
|
||||
/* margin-top: 10px; */
|
||||
}
|
||||
|
||||
//.mobile .q-content-top .ant-radio {
|
||||
//}
|
||||
|
||||
.mobile .q-content-top .title {
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.mobile .q-content-top .ant-radio-inner::after {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
/* 移动端样式 - 结束 */
|
||||
|
||||
/* 公共样式 - 开始 */
|
||||
.q-content-top .radio-group {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.q-content-top .radio-group > .radio {
|
||||
width: auto !important;
|
||||
margin-right: 16px !important;
|
||||
margin-bottom: 0 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.q-content-top .ant-radio-inner {
|
||||
border-color: #fff !important;
|
||||
}
|
||||
|
||||
.q-content-top .ant-checkbox-inner {
|
||||
border-color: #fff !important;
|
||||
}
|
||||
|
||||
.q-content-top .checkbox-group > .checkbox {
|
||||
width: auto !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.q-content-top p {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.q-content-top .ant-input {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.q-content-top .ant-input::-webkit-input-placeholder {
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
/* 公共样式 - 结束 */
|
||||
126
src/views/Survey/views/Preview/js/api.js
Normal file
126
src/views/Survey/views/Preview/js/api.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import request from '@/utils/request';
|
||||
import { useCounterStore } from '@/stores/counter';
|
||||
|
||||
class AnswerApi {
|
||||
/* 获取问卷 */
|
||||
static getQuetions(params) {
|
||||
return request({
|
||||
method: 'GET',
|
||||
url: `/answer/surveys/${params.id}`,
|
||||
params: { ...params.data, time: Date.now() },
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache',
|
||||
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 获取问卷详情 */
|
||||
static getQuetionDetail(params) {
|
||||
return request({
|
||||
method: 'GET',
|
||||
url: `/console/surveys/${params.id}/detail`,
|
||||
params: params.data,
|
||||
headers: {
|
||||
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') //store.state.answer.answerSessionId,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 答题 */
|
||||
static answer(params) {
|
||||
return request({
|
||||
method: 'POST',
|
||||
url: `/answer/surveys/${params.id}`,
|
||||
data: params.data,
|
||||
headers: {
|
||||
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 答题 */
|
||||
static surveyPrevious(params) {
|
||||
return request({
|
||||
method: 'POST',
|
||||
url: `/answer/surveys/${params.id}/prev`,
|
||||
data: params.data,
|
||||
headers: {
|
||||
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 获取验证码 */
|
||||
static getCode(params) {
|
||||
return request({
|
||||
method: 'POST',
|
||||
url: `/system/sms_codes`,
|
||||
data: params.data,
|
||||
headers: {
|
||||
'answer-session-id': AnswerApi.getLocalStorageWithExpiration('answer-session-id') //store.state.answer.answerSessionId,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 问卷密码 */
|
||||
static password(params) {
|
||||
return request({
|
||||
method: 'PATCH',
|
||||
url: `/answer/surveys/${params.id}/password`,
|
||||
data: params.data,
|
||||
headers: {
|
||||
'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,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取localStorage数据,并检查过期时间
|
||||
static getLocalStorageWithExpiration(key) {
|
||||
const item = JSON.parse(localStorage.getItem(key));
|
||||
|
||||
if (!item) {
|
||||
return useCounterStore?.state?.answer?.answerSessionId || '';
|
||||
}
|
||||
|
||||
if (!item || new Date().getTime() > item.expirationDate) {
|
||||
// 数据过期,删除数据
|
||||
localStorage.removeItem(key);
|
||||
return null;
|
||||
}
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
|
||||
class BrowsingRecordApi {
|
||||
/* 用户行为记录 */
|
||||
static browsingRecordSurveys(params) {
|
||||
return request({
|
||||
method: 'post',
|
||||
url: `/console/browsingRecord/surveys/${params.sn}/add`,
|
||||
data: params.data
|
||||
});
|
||||
}
|
||||
|
||||
/* 获取3D资源 */
|
||||
static getSurveysScene(params) {
|
||||
return request({
|
||||
method: 'get',
|
||||
url: `/answer/surveys/${params.sn}/scene?question_index=${params.question_index}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { AnswerApi, BrowsingRecordApi };
|
||||
586
src/views/Survey/views/Preview/js/language.js
Normal file
586
src/views/Survey/views/Preview/js/language.js
Normal file
@@ -0,0 +1,586 @@
|
||||
export const language = {
|
||||
PoweredByDigitalTechnologyCenterYIP: {
|
||||
en: 'Powered by Yili Data Technology Center',
|
||||
zh: '由数字科技中心YIP提供支持'
|
||||
},
|
||||
|
||||
PleaseSelectAtLeastOneOptionsPerLine: {
|
||||
en: (count) => `Please select at least ${count} answer option${count > 1 ? 's' : ''} per row.`,
|
||||
zh: (count) => `每行最少选${count}个。`
|
||||
},
|
||||
PleaseSelectAtLeastOneOptionsPerColumn: {
|
||||
en: (count) => `Please select at least ${count} answer option${count > 1 ? 's' : ''} per column.`,
|
||||
zh: (count) => `每列最少选${count}个。`
|
||||
},
|
||||
PleaseCategorizeAllOptions: {
|
||||
en: 'Please categorize all answer options.',
|
||||
zh: '部分选项未归类。'
|
||||
},
|
||||
ThisIsARequiredQuestion: {
|
||||
en: 'This is compulsory.',
|
||||
zh: '这是一道必答题。'
|
||||
},
|
||||
PleaseInputAValue: {
|
||||
en: 'Please input a value.',
|
||||
zh: '请输入。'
|
||||
},
|
||||
PleaseSelectAtLeastOneOptions: {
|
||||
en: (count) => `Please select at least ${count} answer option${count > 1 ? 's' : ''}.`,
|
||||
zh: (count) => `最少选择${count}个。`
|
||||
},
|
||||
PleaseSelectAtLeastOneOptionsInTitleGroup: {
|
||||
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.`,
|
||||
zh: (count, title) => `${title}最多选择${count}个。`
|
||||
},
|
||||
PleaseSelectAtLeastOnePictures: {
|
||||
en: (count) => `Please select at least ${count} picture${count > 1 ? 's' : ''}.`,
|
||||
zh: (count) => `最少选择${count}个。`
|
||||
},
|
||||
PleaseLetTheSumEqualToTotal: {
|
||||
en: (count) => `Please let the sum equal to ${count}.`,
|
||||
zh: (count) => `请让总和等于${count}。`
|
||||
},
|
||||
PleaseUploadAtLeastOneFiles: {
|
||||
en: (count) => `Please upload at least ${count} file${count > 1 ? 's' : ''}.`,
|
||||
zh: (count) => `最少上传${count}个文件。`
|
||||
},
|
||||
PleaseEnterEnglishLetters: {
|
||||
en: 'Please enter English letters.',
|
||||
zh: '请输入字母。'
|
||||
},
|
||||
PleaseEnterChineseWords: {
|
||||
en: 'Please enter Chinese characters.',
|
||||
zh: '请输入中文。'
|
||||
},
|
||||
PleaseEnterACorrectEmail: {
|
||||
en: 'Please enter a valid email.',
|
||||
zh: '请输入正确的email。'
|
||||
},
|
||||
PleaseEnterACorrectPhone: {
|
||||
en: 'Please enter a valid phone number.',
|
||||
zh: '请输入正确的手机号。'
|
||||
},
|
||||
PleaseEnterACorrectID: {
|
||||
en: 'Please enter a valid ID card number.',
|
||||
zh: '请输入正确的身份证号。'
|
||||
},
|
||||
PleaseEnterMoreThanOneCharacters: {
|
||||
en: (count) => `Please enter more than ${count} character${count > 1 ? 's' : ''}.`,
|
||||
zh: (count) => `请输入大于${count}个字符。`
|
||||
},
|
||||
|
||||
// 质量控制
|
||||
PleaseAnswerCarefully: {
|
||||
en: 'Please answer carefully.',
|
||||
zh: '请认真作答。'
|
||||
},
|
||||
ContinueAnswer: {
|
||||
en: 'Continue',
|
||||
zh: '继续作答'
|
||||
},
|
||||
ReviseAnswer: {
|
||||
en: 'Revise',
|
||||
zh: '去修改'
|
||||
},
|
||||
TheAnswerIsRepeatedMoreThanOneTimesPleaseRevise: {
|
||||
en: (count, type) => `The answer is continuously repeated more than ${count} times, please revise.`,
|
||||
zh: (count, type) => `答案连续重复超过${count}个,${type ? '建议' : '请'}修改。`
|
||||
},
|
||||
|
||||
// 预览
|
||||
QuestionRemark: {
|
||||
en: (title) => `${title} remark`,
|
||||
zh: (title) => `${title}问题评论`
|
||||
},
|
||||
|
||||
// 问卷结束的各种提示
|
||||
SurveyEndNotice: {
|
||||
en: 'This survey has ended. We look forward to your participation next time.',
|
||||
zh: '本次调研活动已经结束,感谢您的参与,请您持续关注'
|
||||
},
|
||||
SurveyNotStartNotice: {
|
||||
en: 'This survey has not yet started.',
|
||||
zh: '答题时间还没有开始'
|
||||
},
|
||||
SurveyTemporarilySuspendedNotice: {
|
||||
en: 'This survey is temporarily suspended for collection',
|
||||
zh: '此问卷暂停收集'
|
||||
},
|
||||
LinkDisabledNotice: {
|
||||
en: 'The provider has not enabled this link. Please try again later',
|
||||
zh: '投放者未启用该链接,请稍后重试'
|
||||
},
|
||||
FeedbackNotice: {
|
||||
en: 'Thank you for participating in this survey. We look forward to your participation next time',
|
||||
zh: '感谢您的反馈,期待您下次参与'
|
||||
},
|
||||
SubmittedNotice: {
|
||||
en: 'You have submitted the questionnaire. We look forward to your participation next time.',
|
||||
zh: '您已提交答卷,期待您下次反馈'
|
||||
},
|
||||
SurveyFilledNotice: {
|
||||
en: 'You have already filled out this questionnaire',
|
||||
zh: '您已填过此问卷'
|
||||
},
|
||||
FillOutNewSurvey: {
|
||||
en: 'Fill out new one',
|
||||
zh: '填份新的'
|
||||
},
|
||||
ReviseSurvey: {
|
||||
en: 'Revise',
|
||||
zh: '修改内容'
|
||||
},
|
||||
|
||||
// 问题的提示文字
|
||||
UploadSuccessful: {
|
||||
en: 'Upload successful.',
|
||||
zh: '上传成功'
|
||||
},
|
||||
UploadFailed: {
|
||||
en: 'Upload failed.',
|
||||
zh: '上传失败'
|
||||
},
|
||||
Loading: {
|
||||
en: 'Loading',
|
||||
zh: '加载中'
|
||||
},
|
||||
Start: {
|
||||
en: 'Start',
|
||||
zh: '开始'
|
||||
},
|
||||
|
||||
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:
|
||||
'您在每一题的态度与观点均对我们有非常重要的意义,请您务必仔细阅读题目后回答,连续一致的答案可能会导致整个问卷的作废,请您重新作答。'
|
||||
},
|
||||
|
||||
IGotIt: {
|
||||
en: 'Got it',
|
||||
zh: '我知道啦'
|
||||
},
|
||||
|
||||
// BPTO
|
||||
Product: {
|
||||
en: 'Product',
|
||||
zh: '产品'
|
||||
},
|
||||
Character: {
|
||||
en: 'Character',
|
||||
zh: '特性'
|
||||
},
|
||||
Price: {
|
||||
en: 'Price',
|
||||
zh: '价格'
|
||||
},
|
||||
PleaseSelect: {
|
||||
en: 'Please select',
|
||||
zh: '请选择'
|
||||
},
|
||||
|
||||
// CBC
|
||||
YouHaveNotAnsweredThisQuestionAnswerToContinue: {
|
||||
en: 'You have not answered this question. You can continue after answering!',
|
||||
zh: '您没有对该问题进行回答,回答后可继续做答!'
|
||||
},
|
||||
ProductNumber: {
|
||||
en: 'Product Number',
|
||||
zh: '商品编号'
|
||||
},
|
||||
ProductName: {
|
||||
en: 'Product Name',
|
||||
zh: '商品名称'
|
||||
},
|
||||
ProductDescription: {
|
||||
en: 'Product Description',
|
||||
zh: '商品介绍'
|
||||
},
|
||||
ProductDetail: {
|
||||
en: 'Product detail',
|
||||
zh: '商品详情'
|
||||
},
|
||||
Above: {
|
||||
en: 'Above',
|
||||
zh: '以上'
|
||||
},
|
||||
ChooseNeither: {
|
||||
en: 'Choose neither',
|
||||
zh: '都不选'
|
||||
},
|
||||
|
||||
// KANO
|
||||
HaveSomeFeature: {
|
||||
en: (feature) => `Have the feature ${feature}`,
|
||||
zh: (feature) => `具备${feature}`
|
||||
},
|
||||
DoNotHaveSomeFeature: {
|
||||
en: (feature) => `Do not have the feature ${feature}`,
|
||||
zh: (feature) => `不具备${feature}`
|
||||
},
|
||||
Dislike: {
|
||||
en: 'Dislike',
|
||||
zh: '不喜欢'
|
||||
},
|
||||
Tolerable: {
|
||||
en: 'Tolerable',
|
||||
zh: '能忍受'
|
||||
},
|
||||
DoesNotMatter: {
|
||||
en: 'Does not matter',
|
||||
zh: '无所谓'
|
||||
},
|
||||
ShouldBe: {
|
||||
en: 'Should be',
|
||||
zh: '理应如此'
|
||||
},
|
||||
Like: {
|
||||
en: 'Like',
|
||||
zh: '喜欢'
|
||||
},
|
||||
|
||||
// MaxDiff
|
||||
PleaseSelectSome: {
|
||||
en: (option) => `Please select ${option}`,
|
||||
zh: (option) => `请选择${option}`
|
||||
},
|
||||
|
||||
// PSM
|
||||
PleaseSelectAValueThatIsNoLessThanOneAndNoMoreThanOne: {
|
||||
en: (min, max) => `Please select a value that is no less than ${min} and no more than ${max}`,
|
||||
zh: (min, max) => `请选择不小于${min}不大于${max}的值`
|
||||
},
|
||||
PleaseSelectAValueThatIsNoMoreThanOne: {
|
||||
en: (min, max) => `Please select a value that is no more than ${max}`,
|
||||
zh: (min, max) => `请选择不大于${max}的值`
|
||||
},
|
||||
|
||||
// 密码
|
||||
PleaseEnterPassword: {
|
||||
en: 'Please enter password',
|
||||
zh: '请输入密码'
|
||||
},
|
||||
|
||||
// 填空题
|
||||
PleaseInputANumber: {
|
||||
en: 'Please input a number',
|
||||
zh: '请输入数字'
|
||||
},
|
||||
PleaseEnterANumberGreaterThanOrEqualToOne: {
|
||||
en: (count) => `Please enter a number greater than or equal to ${count}`,
|
||||
zh: (count) => `请输入大于等于${count}的数字`
|
||||
},
|
||||
PleaseEnterANumberLessThanOrEqualToOne: {
|
||||
en: (count) => `Please enter a number less than or equal to ${count}`,
|
||||
zh: (count) => `请输入小于等于${count}的数字`
|
||||
},
|
||||
|
||||
// 级联题
|
||||
PleaseSelectAProvince: {
|
||||
en: 'Please select a province',
|
||||
zh: '请选择省份'
|
||||
},
|
||||
PleaseSelectACity: {
|
||||
en: 'Please select a city',
|
||||
zh: '请选择城市'
|
||||
},
|
||||
PleaseSelectADistrict: {
|
||||
en: 'Please select a district',
|
||||
zh: '请选择区县'
|
||||
},
|
||||
|
||||
// 分类题
|
||||
Option: {
|
||||
en: 'Answer Option',
|
||||
zh: '选项'
|
||||
},
|
||||
Category: {
|
||||
en: 'Category',
|
||||
zh: '分类'
|
||||
},
|
||||
BracketDragAndDropOptionsHere: {
|
||||
en: '(Drag and drop options here)',
|
||||
zh: '(选项拖入此处)'
|
||||
},
|
||||
|
||||
// 知情同意书题
|
||||
OneSecondsLeftToRead: {
|
||||
en: (count) => `${count} seconds left to read`,
|
||||
zh: (count) => `还需阅读${count}秒`
|
||||
},
|
||||
|
||||
// 热区题
|
||||
PleaseSelectOneAreas: {
|
||||
en: (count) => `Please select ${count} areas`,
|
||||
zh: (count) => `请选择${count}个选区`
|
||||
},
|
||||
PleaseSelectOneFavoriteAreas: {
|
||||
en: (count) => `Please select ${count} areas you like`,
|
||||
zh: (count) => `请选择${count}个喜欢选区`
|
||||
},
|
||||
PleaseSelectOneDislikeAreas: {
|
||||
en: (count) => `Please select ${count} areas you dislike`,
|
||||
zh: (count) => `请选择${count}个不喜欢选区`
|
||||
},
|
||||
|
||||
// 图片题
|
||||
PleaseClickToViewAllPictures: {
|
||||
en: 'Please click to view all pictures',
|
||||
zh: '请点击查看所有图片'
|
||||
},
|
||||
ClickToViewPicture: {
|
||||
en: 'Click to view picture',
|
||||
zh: '点击查看图片'
|
||||
},
|
||||
AutoDestructionAfterViewingTimeout: {
|
||||
en: 'Auto destruction after viewing timeout',
|
||||
zh: '查看超时后自动焚毁'
|
||||
},
|
||||
ImageHasExpired: {
|
||||
en: 'Image has expired',
|
||||
zh: '图片已失效'
|
||||
},
|
||||
PleaseClickToViewPicture: {
|
||||
en: 'Click to view picture',
|
||||
zh: '请点击查看图片'
|
||||
},
|
||||
NoteCantViewAfterTimeLimit: {
|
||||
en: 'Note: Can\'t view after time limit',
|
||||
zh: '注意:超过显示时间限制后将无法再次查看'
|
||||
},
|
||||
DisplayTimeLimitExceeded: {
|
||||
en: 'Display time limit exceeded',
|
||||
zh: '已超过显示时间限制'
|
||||
},
|
||||
AutomaticallyCloseAfterOneSecond: {
|
||||
en: (count) => `Automatically close after ${count} seconds`,
|
||||
zh: (count) => `${count}秒后自动关闭`
|
||||
},
|
||||
|
||||
// 多项填空
|
||||
ContentIsRequired: {
|
||||
en: 'is required',
|
||||
zh: '必答'
|
||||
},
|
||||
ContentShouldNotBeTheSameAsOtherBlanks: {
|
||||
en: 'should not be the same as other blanks',
|
||||
zh: '的内容请勿与其他空相同'
|
||||
},
|
||||
TheBlankIs: {
|
||||
en: (message) => `The blank ${message}`,
|
||||
zh: (message) => `填空${message}`
|
||||
},
|
||||
|
||||
// 地图题
|
||||
ObtainGeographicalLocation: {
|
||||
en: 'Obtain geographical location',
|
||||
zh: '获取地理位置'
|
||||
},
|
||||
LongitudeAndLatitude: {
|
||||
en: (lng, lat) => `Longitude: ${lng}, Latitude: ${lat}`,
|
||||
zh: (lng, lat) => `经度:${lng}, 纬度:${lat}`
|
||||
},
|
||||
PleaseEnterTheLocationOrStreetYouWantToSearchFor: {
|
||||
en: 'Please enter the location or street you want to search for',
|
||||
zh: '请输入要搜索的地点、街道'
|
||||
},
|
||||
|
||||
// 密码题
|
||||
PleaseEnterAtLeastOneCharacters: {
|
||||
en: (count) => `Please enter at least ${count} characters.`,
|
||||
zh: (count) => `请至少输入${count}位`
|
||||
},
|
||||
ThePasswordNeedsToIncludeLettersNumbersOrBoth: {
|
||||
en: 'The password needs to include letters, numbers, or both letters and numbers.',
|
||||
zh: '密码需要包含字母/数字/字母和数字'
|
||||
},
|
||||
ThePasswordNeedsToIncludeLettersNumbersSymbolsOrCombination: {
|
||||
en: 'The password needs to include letters, numbers, symbols, or a combination of letters, numbers, and symbols.',
|
||||
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.',
|
||||
zh: '密码需要包含大/小写字母/数字/符号/大/小写字母和数字/大/小写字母和符号/符号和数字/大/小写字母、数字和符号'
|
||||
},
|
||||
ThePasswordMustBeACombinationOfLettersAndNumbers: {
|
||||
en: 'The password must be a combination of letters and numbers.',
|
||||
zh: '密码必须为字母和数字'
|
||||
},
|
||||
ThePasswordsEnteredDoNotMatch: {
|
||||
en: 'The passwords entered do not match.',
|
||||
zh: '两次密码输入不一致'
|
||||
},
|
||||
|
||||
// 手机号题
|
||||
GetCaptcha: {
|
||||
en: 'Get Captcha',
|
||||
zh: '获取验证码'
|
||||
},
|
||||
SentSuccessfully: {
|
||||
en: 'Sent successfully.',
|
||||
zh: '发送成功'
|
||||
},
|
||||
PleaseEnterAValidPhoneNumber: {
|
||||
en: 'Please enter a valid phone number.',
|
||||
zh: '请输入正确的手机号'
|
||||
},
|
||||
|
||||
// 签名题
|
||||
Clear: {
|
||||
en: 'Clear',
|
||||
zh: '清空'
|
||||
},
|
||||
Undo: {
|
||||
en: 'Undo',
|
||||
zh: '撤销'
|
||||
},
|
||||
Eraser: {
|
||||
en: 'Eraser',
|
||||
zh: '橡皮'
|
||||
},
|
||||
Brush: {
|
||||
en: 'Brush',
|
||||
zh: '画笔'
|
||||
},
|
||||
CompleteAndUpload: {
|
||||
en: 'Upload',
|
||||
zh: '完成并上传'
|
||||
},
|
||||
|
||||
// 恒定总和题
|
||||
Sum: {
|
||||
en: 'Sum',
|
||||
zh: '总和'
|
||||
},
|
||||
|
||||
// 上传文件题的提示
|
||||
ClickOrDragTheFileHereToUpload: {
|
||||
en: 'Click or drag the file here to upload.',
|
||||
zh: '点击或将文件拖拽到这里上传'
|
||||
},
|
||||
UploadFileDesc: {
|
||||
en: (min, max, minSize, maxSize, type) => {
|
||||
let result = `Upload at least ${min} files, up to a maximum of ${max} files, with each file size between ${minSize}MB and ${maxSize}MB`;
|
||||
if (type) {
|
||||
result += `, and the file format limited to ${type}`;
|
||||
}
|
||||
result += '.';
|
||||
return result;
|
||||
},
|
||||
zh: (min, max, minSize, maxSize, type) => {
|
||||
let result = `最少上传${min}个文件,最多上传${max}个文件,每个文件不小于${minSize}M不大于${maxSize}M`;
|
||||
if (type) {
|
||||
result += `,文件格式限制为${type}`;
|
||||
}
|
||||
result += '。';
|
||||
return result;
|
||||
}
|
||||
},
|
||||
FileSizeIsLessThanOneMB: {
|
||||
en: (size) => `File size is less than ${size}MB.`,
|
||||
zh: (size) => `文件小于${size}M`
|
||||
},
|
||||
FileSizeIsGreaterThanOneMB: {
|
||||
en: (size) => `File size is greater than ${size}MB.`,
|
||||
zh: (size) => `文件大于${size}M`
|
||||
},
|
||||
FileFormatIsIncorrect: {
|
||||
en: 'The file format is incorrect.',
|
||||
zh: '文件格式不正确'
|
||||
},
|
||||
UploadFileSuccessful: {
|
||||
en: (title) => `Upload ${title} successful.`,
|
||||
zh: (title) => `${title} 上传成功`
|
||||
},
|
||||
UploadFileFailed: {
|
||||
en: (title) => `Upload ${title} failed.`,
|
||||
zh: (title) => `${title} 上传失败`
|
||||
},
|
||||
EquipmentNotice: {
|
||||
en: (title) =>
|
||||
`Sorry, the number of submissions you have made under the current IP address has reached the limit. Thank you for participating.`,
|
||||
zh: (title) => `很抱歉,您使用当前设备填写问卷已达到限制次数,感谢参与`
|
||||
},
|
||||
SubmitIpNotice: {
|
||||
en: (title) =>
|
||||
`Sorry, the number of submissions you have made under the current IP address has reached the limit. Thank you for participating.`,
|
||||
zh: (title) => `很抱歉,您在当前IP地址下填写问卷已达到限制次数,感谢参与`
|
||||
},
|
||||
PrivatizationWxWorkIdentityNotice: {
|
||||
en: (title) =>
|
||||
`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请通过私有化企业微信点击链接或扫码作答`
|
||||
},
|
||||
|
||||
// QLast 提示
|
||||
QLast_Stay: {
|
||||
en: 'Cancel',
|
||||
zh: '停止跳转'
|
||||
},
|
||||
QLast_Redirect: {
|
||||
en: 'Continue',
|
||||
zh: '立即跳转'
|
||||
},
|
||||
QLast_RedirectingIn5Seconds: {
|
||||
en: (countdown) => `You will be redirecting to another page in ${countdown} seconds.`,
|
||||
zh: (countdown) => `发布者设置了作答后跳转,页面将于【${countdown}】秒后跳转`
|
||||
},
|
||||
QLast_IosRedirectingPrompt: {
|
||||
en: 'If you are using an Apple (iOS) device,please click the button below to jump now.',
|
||||
zh: '页面跳转中,如您使用苹果(iOS)设备或页面长时间未跳转,请手动点击下方按钮进行跳转'
|
||||
}
|
||||
};
|
||||
|
||||
export const allLanguageTypes = Object.keys(language[Object.keys(language)[0]]);
|
||||
export const languageTypes = ['zh'];
|
||||
|
||||
export function setLanguageTypes(types) {
|
||||
languageTypes.push(...types);
|
||||
}
|
||||
|
||||
export function getLanguage(langArr = languageTypes) {
|
||||
const l = [];
|
||||
if (!langArr) {
|
||||
l.push('zh');
|
||||
} else if (Array.isArray(langArr)) {
|
||||
if (langArr.length) {
|
||||
l.push(...langArr);
|
||||
} else {
|
||||
l.push('zh');
|
||||
}
|
||||
} else {
|
||||
l.push(langArr);
|
||||
}
|
||||
|
||||
const result = {};
|
||||
Object.keys(language).forEach((key) => {
|
||||
result[key] = '';
|
||||
|
||||
let tempStr = [];
|
||||
let tempFunc = [];
|
||||
|
||||
l.forEach((lang) => {
|
||||
const res = language[key][lang];
|
||||
|
||||
if (typeof res === 'string') {
|
||||
tempStr.push(language[key][lang]);
|
||||
} else {
|
||||
tempFunc.push(language[key][lang]);
|
||||
}
|
||||
});
|
||||
|
||||
if (tempStr.length) {
|
||||
result[key] = tempStr.join('\n');
|
||||
}
|
||||
if (tempFunc.length) {
|
||||
result[key] = (...rest) => tempFunc.map((func) => func(...rest)).join('\n');
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
35
src/views/Survey/views/Preview/js/message.js
Normal file
35
src/views/Survey/views/Preview/js/message.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { createVNode } from 'vue';
|
||||
import {ElMessage as message} from "element-plus"
|
||||
function getNode(text) {
|
||||
const lines = (text || '').split('\n').filter((i) => !!i);
|
||||
|
||||
return createVNode(
|
||||
'div',
|
||||
{
|
||||
style: 'display:inline-flex;flex-direction:column;text-align:left;'
|
||||
},
|
||||
lines.map((i) => createVNode('div', {}, i))
|
||||
);
|
||||
}
|
||||
|
||||
function getOptions(options, duration, onClose) {
|
||||
if (typeof options === 'string') {
|
||||
return {
|
||||
content: getNode(options),
|
||||
duration,
|
||||
onClose
|
||||
};
|
||||
}
|
||||
return {
|
||||
...(options || {}),
|
||||
content: getNode(options?.content || ''),
|
||||
duration: duration ?? options?.duration,
|
||||
onClose: onClose ?? options?.onClose
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
error: (...rest) => message.error(getOptions(...rest)),
|
||||
warn: (...rest) => message.warn(getOptions(...rest)),
|
||||
success: (...rest) => message.success(getOptions(...rest))
|
||||
};
|
||||
130
src/views/Survey/views/Preview/js/mock.js
Normal file
130
src/views/Survey/views/Preview/js/mock.js
Normal file
@@ -0,0 +1,130 @@
|
||||
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
|
||||
);
|
||||
if (startIndex < page) {
|
||||
if (skip_question_index === -1) {
|
||||
action.code = 20011;
|
||||
action.msg = '成功结束页';
|
||||
} else if (skip_question_index === -2) {
|
||||
action.code = 20004;
|
||||
action.msg = '甄别结束页';
|
||||
} else if (skip_question_index === -3) {
|
||||
action.code = 20016;
|
||||
action.msg = '配额超限页';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新分页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 endIndex = pages.findIndex(
|
||||
(page) => page.findIndex((questionIndex) => questionIndex === skip_question_index) !== -1
|
||||
);
|
||||
if (endIndex !== -1) {
|
||||
const endQuestionIndex = pages[endIndex].findIndex((questionIndex) => questionIndex === skip_question_index);
|
||||
pages[endIndex].splice(0, endQuestionIndex);
|
||||
// 跳转到某页
|
||||
if (startIndex > endIndex && startIndex < page) {
|
||||
jumpTo.question_index = question_index;
|
||||
return (jumpTo.question_page = endIndex + 1);
|
||||
}
|
||||
pages.splice(startIndex + 1, endIndex - startIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新分页pages(题前设置逻辑)
|
||||
function updatePagesBefore(pages, hideQuestionIndex) {
|
||||
const pagesIndex = pages.findIndex(
|
||||
(page) => page.findIndex((questionIndex) => questionIndex === hideQuestionIndex) !== -1
|
||||
);
|
||||
if (pagesIndex === -1) return;
|
||||
if (pages[pagesIndex].length === 1) {
|
||||
pages.splice(pagesIndex, 1);
|
||||
} else {
|
||||
const pageIndex = pages[pagesIndex].findIndex((questionIndex) => questionIndex === hideQuestionIndex);
|
||||
pages[pagesIndex].splice(pageIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 自动填写
|
||||
function autoFill(answerAutoFill, logic) {
|
||||
answerAutoFill.push({
|
||||
answer: logic.autofill.answer_insert,
|
||||
question_index: logic.question_index,
|
||||
question_type: logic.autofill.question_type
|
||||
});
|
||||
}
|
||||
|
||||
// 选项隐藏逻辑
|
||||
function updateOptionHidden(hide_options, logic) {
|
||||
const { question_index, hide_option_index } = logic;
|
||||
hide_options.question_index = question_index;
|
||||
if (logic.logicStatus) {
|
||||
hide_options.option_key.push(...hide_option_index.map((opt) => opt.option_key));
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟答题接口
|
||||
export default function answerMock(questionsData, page) {
|
||||
const data = {
|
||||
action: { code: 20010, msg: '答案已记录' },
|
||||
jump_to: {},
|
||||
hide_options: {
|
||||
option_key: []
|
||||
},
|
||||
answer_info_autofill: [],
|
||||
pages: JSON.parse(JSON.stringify(questionsData.answer.pages_init))
|
||||
};
|
||||
const logics = getlogicStatus(questionsData);
|
||||
logics.forEach((logic) => {
|
||||
if (logic.logicStatus && logic.skip_type === 0) {
|
||||
// 20240731
|
||||
// fix: Q1,Q2,Q3,Q4,Q5. Q1 A 跳转至 Q3; Q1 B 跳转至 Q2; Q2 always 跳转至 Q5. 当 Q1 选择 A, 页面空白/作答结束
|
||||
if (!data.pages[page - 1].includes(logic.question_index)) {
|
||||
return;
|
||||
}
|
||||
// 题后跳转逻辑
|
||||
if (logic.skip_question_index < 0) {
|
||||
return updateCode(data.action, logic, data.pages, page);
|
||||
}
|
||||
updatePagesAfter(data.pages, logic, data.jump_to, page);
|
||||
} else if (logic.logicStatus && logic.skip_type === 1) {
|
||||
// 题前设置逻辑
|
||||
updatePagesBefore(data.pages, logic.question_index);
|
||||
} else if (logic.logicStatus && logic.skip_type === 3) {
|
||||
// 自动填写逻辑
|
||||
autoFill(data.answer_info_autofill, logic);
|
||||
} else if (logic.skip_type === 4) {
|
||||
// 只计算跳转后所在页面的隐藏逻辑,否则会出现只返回最后一道隐藏选项题目的情况,导致失效
|
||||
const toPage = page + 1;
|
||||
const hasHiddenLogicQuizPage = data.pages.findIndex((page) => page.includes(logic.question_index)) + 1;
|
||||
if (hasHiddenLogicQuizPage === toPage) {
|
||||
// 选项隐藏逻辑
|
||||
updateOptionHidden(data.hide_options, logic);
|
||||
}
|
||||
}
|
||||
});
|
||||
// 更新问卷状态
|
||||
if (page === data.pages.length) {
|
||||
data.action.code = 20011;
|
||||
data.action.msg = '成功结束页';
|
||||
}
|
||||
// 拒绝知情同意书
|
||||
// const refuseIndex = questionsData.questions.findIndex(
|
||||
// (question) => question.question_type === 23 && question.answer?.value === "2"
|
||||
// );
|
||||
// if (refuseIndex !== -1) {
|
||||
// data.action.code = 20013;
|
||||
// data.action.msg = "不同意继续参与,已结束作答";
|
||||
// }
|
||||
// 返回数据
|
||||
return data;
|
||||
}
|
||||
875
src/views/Survey/views/Preview/js/questions.js
Normal file
875
src/views/Survey/views/Preview/js/questions.js
Normal file
@@ -0,0 +1,875 @@
|
||||
import { AnswerApi } from '../js/api';
|
||||
import { computed, getCurrentInstance, defineComponent, provide, ref, watch } from 'vue';
|
||||
import answerMock from './mock';
|
||||
import { useRoute } from 'vue-router';
|
||||
import msg from './message';
|
||||
import LangTranslate from '../components/LangTranslate.vue';
|
||||
import { useQuestionStore } from '@/stores/Questions/useQuestionStore.ts';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
LangTranslate
|
||||
},
|
||||
props: {
|
||||
// 是否答题模式
|
||||
isAnswer: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否模板预览
|
||||
isTemplate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const questionStore = useQuestionStore()
|
||||
/**
|
||||
* styleInfo 主题样式
|
||||
*/
|
||||
const {questionsData, styleInfo, page, pages,l} = storeToRefs(questionStore)
|
||||
const route = useRoute();
|
||||
// const questionsData = inject('questionsData'); // 问卷数据
|
||||
|
||||
const translatedText = computed(() => questionsData.value.language || {});
|
||||
const startAnswerTime = new Date().getTime(); // 开始答题时间戳
|
||||
const { proxy } = getCurrentInstance();
|
||||
const scrollbar = ref(null);
|
||||
let localPageInterval;
|
||||
|
||||
|
||||
watch(
|
||||
styleInfo,
|
||||
(style) => {
|
||||
console.log(`style value: `, style);
|
||||
if (questionsData.value?.survey?.style && !style.is_home && !page.value) {
|
||||
page.value = 1;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
// 主题颜色
|
||||
const themeColor = computed(() => ({
|
||||
stemColor: styleInfo.value.stem_color,
|
||||
answerColor: styleInfo.value.answer_color,
|
||||
buttonColor: styleInfo.value.button_color,
|
||||
buttonTextColor: styleInfo.value.button_text_color
|
||||
}));
|
||||
provide('themeColor', themeColor);
|
||||
|
||||
// 当前页问卷
|
||||
const questions = computed(() => {
|
||||
const currentPages = pages.value[page.value - 1] || [];
|
||||
return (questionsData.value?.questions || []).filter((quetion) =>
|
||||
currentPages.find((index) => quetion.question_index === index)
|
||||
);
|
||||
});
|
||||
// 是否显示分页器
|
||||
const showPage = computed(() => {
|
||||
return [102, 104, 105, 201].includes(questions.value[0]?.question_type) ? false : true;
|
||||
});
|
||||
|
||||
|
||||
// 分页计时器
|
||||
watch(
|
||||
page,
|
||||
() => {
|
||||
localPageTimer.value = {};
|
||||
clearInterval(localPageInterval);
|
||||
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
|
||||
);
|
||||
if (localPage) {
|
||||
localPageTimer.value = localPage.timer_config;
|
||||
localPageTimer.value.short_time = localPageTimer.value.short_time * 1;
|
||||
localPageInterval = setInterval(() => {
|
||||
if (localPageTimer.value.short_time === 0) {
|
||||
// 停止计时
|
||||
return clearInterval(localPageInterval);
|
||||
}
|
||||
localPageTimer.value.short_time -= 1;
|
||||
}, 1000);
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
// 要搜索的数组和要查找的连续数字的数量n
|
||||
function hasNConsecutiveNumbers(arr, n, onTrue, onFalse) {
|
||||
let count = 1;
|
||||
let warnStart = 0;
|
||||
let prevNum = arr[0];
|
||||
for (let i = 1; i < arr.length; i++) {
|
||||
if (prevNum && arr[i] === prevNum) {
|
||||
count++;
|
||||
} else {
|
||||
count = 1;
|
||||
warnStart = i;
|
||||
}
|
||||
if (count === n) {
|
||||
if (onTrue) onTrue(warnStart, warnStart + n);
|
||||
return true;
|
||||
}
|
||||
prevNum = arr[i];
|
||||
}
|
||||
if (onFalse) onFalse();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 答题
|
||||
async function answer(callback, callbackBeforePage) {
|
||||
if ((questions.value.length || !questionsData.value.questions.length) && !props.isTemplate) {
|
||||
// 表单验证(当前页)
|
||||
const errors = questions.value.filter((question) => {
|
||||
const { config, answer, question_type: questionType } = question;
|
||||
let isError = false;
|
||||
if (config.is_required && !answer) {
|
||||
isError = true;
|
||||
if (questionType === 10) {
|
||||
// 矩阵多选题
|
||||
if (question.config.is_change_row_cell) {
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(config.min_select || 1);
|
||||
} else {
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerLine(config.min_select || 1);
|
||||
}
|
||||
} else if (questionType === 12) {
|
||||
// 图片显示题,如果开启了仅显示并且开启了单图显示时长,需要确保用户每一个图片都点开看过了才能下一页
|
||||
if (!answer?.read && config.picture_type === 0 && config.countdown_type === 1) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseClickToViewAllPictures;
|
||||
}
|
||||
} else if (questionType === 15) {
|
||||
// 分类题
|
||||
question.error = translatedText.value.PleaseCategorizeAllOptions;
|
||||
} else if (!question.error) {
|
||||
question.error = translatedText.value.ThisIsARequiredQuestion;
|
||||
}
|
||||
} else if (answer && questionType === 1 && Object.keys(answer).findIndex((value) => !answer[value]) !== -1) {
|
||||
// 单选题
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseInputAValue;
|
||||
} else if (answer && questionType === 2) {
|
||||
// 多选题
|
||||
if (Object.keys(answer).length < config.min_select) {
|
||||
// 选项数量
|
||||
let options = [];
|
||||
question.list.forEach((list) => {
|
||||
options = [...options, ...list.options];
|
||||
});
|
||||
const index = options.findIndex(
|
||||
(option) => option.option_key === Object.keys(answer)[0] && option.is_remove_other
|
||||
);
|
||||
if (index === -1) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOneOptions(config.min_select);
|
||||
} else {
|
||||
question.error = '';
|
||||
}
|
||||
} else if (Object.keys(answer).findIndex((value) => !answer[value]) !== -1) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseInputAValue;
|
||||
} else {
|
||||
question.error = '';
|
||||
}
|
||||
if (!question.error) {
|
||||
// 选项分组最少选项数量
|
||||
config.option_groups?.option_group.some((optionGroup) => {
|
||||
const { min, title } = optionGroup;
|
||||
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 }) =>
|
||||
optionGroup.groups.some(
|
||||
({ option_key: groupKey }) =>
|
||||
groupKey === option_key && !question.hideOptionsSet.has(groupKey)
|
||||
)
|
||||
)
|
||||
.map(({ option_key }) => option_key)
|
||||
);
|
||||
if (options.length >= min) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsInTitleGroup(min, title);
|
||||
}
|
||||
}
|
||||
return isError;
|
||||
});
|
||||
}
|
||||
} else if (answer && questionType === 10) {
|
||||
// 矩阵多选题,列分组时,校验选项数量
|
||||
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 freeCols = cols.filter(
|
||||
(col) => !cellGroups.some((g) => g.groups.find((c) => c.option_key === col.option_key))
|
||||
);
|
||||
|
||||
const arr = rows.map((row) => {
|
||||
return cellGroups.map((group) => {
|
||||
return group.groups.map((col) => {
|
||||
return `${row.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) => {
|
||||
if (!group.groups?.length || (!group.max && !group.min) || isError) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < arr.length; i += 1) {
|
||||
const row = arr[i];
|
||||
const selectedCount = row[idx].filter((key) => answered.includes(key)).length;
|
||||
if (group.min && selectedCount < group.min) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsInTitleGroup(
|
||||
group.min,
|
||||
group.title
|
||||
);
|
||||
break;
|
||||
}
|
||||
if (group.max && selectedCount > group.max) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseSelectAtMostOneOptionsInTitleGroup(
|
||||
group.max,
|
||||
group.title
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// 最少选择数量校验
|
||||
const minSelect = +config.min_select || 0;
|
||||
const perLineSelectedCount = Object.keys(answer).reduce(
|
||||
(p, c) => {
|
||||
if (+answer[c]) {
|
||||
p[+c.split('_')[0] - 1] += 1;
|
||||
}
|
||||
return p;
|
||||
},
|
||||
question.list.filter((i) => i.type === 1).flatMap((i) => i.options.map((i) => 0))
|
||||
);
|
||||
if (minSelect && minSelect > Math.max(...perLineSelectedCount)) {
|
||||
if (question.config.is_change_row_cell) {
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOneOptionsPerColumn(config.min_select || 1);
|
||||
} else {
|
||||
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) {
|
||||
// 图片多选题
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOnePictures(config.min_select);
|
||||
} else if (answer && questionType === 16) {
|
||||
// 排序题
|
||||
if (Object.keys(answer).length < (+config.min_select || 0)) {
|
||||
// 选项数量
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOneOptions(config.min_select);
|
||||
}
|
||||
} else if (answer && questionType === 17) {
|
||||
// 恒定总和题
|
||||
let sum = 0;
|
||||
Object.keys(answer).forEach((key) => {
|
||||
sum += answer[key] * 1;
|
||||
});
|
||||
if (sum === config.total) {
|
||||
question.error = '';
|
||||
} else {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseLetTheSumEqualToTotal(config.total);
|
||||
}
|
||||
} else if (answer && questionType === 18 && answer.length < config.min_number) {
|
||||
// 文件上传题
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseUploadAtLeastOneFiles(config.min_number);
|
||||
} else if (answer && questionType === 4) {
|
||||
question.error = '';
|
||||
// 填空题
|
||||
const { value } = answer;
|
||||
let newValue = value.replace(/\n|\r|\r\n/g, '');
|
||||
switch (config.text_type) {
|
||||
case 3: // 字母
|
||||
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(
|
||||
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(
|
||||
newValue
|
||||
) || !newValue.length;
|
||||
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(
|
||||
value
|
||||
);
|
||||
question.error = isError ? translatedText.value.PleaseEnterACorrectEmail : '';
|
||||
break;
|
||||
case 6: // 手机号
|
||||
isError = !/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value);
|
||||
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(
|
||||
value
|
||||
);
|
||||
question.error = isError ? translatedText.value.PleaseEnterACorrectID : '';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!isError && value.length < config.min && ![1, 2].includes(config.text_type)) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min);
|
||||
}
|
||||
} else if (answer && questionType === 8) {
|
||||
// 矩阵填空题
|
||||
question.error = '';
|
||||
Object.keys(answer).forEach((key) => {
|
||||
const value = answer[key];
|
||||
switch (config.text_type) {
|
||||
case 3: // 字母
|
||||
if (
|
||||
!/^[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(
|
||||
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))
|
||||
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)
|
||||
)
|
||||
question.error = translatedText.value.PleaseEnterACorrectID;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!question.error && value.length < config.min && ![1, 2].includes(config.text_type)) {
|
||||
question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min);
|
||||
}
|
||||
});
|
||||
if (question.error) isError = true;
|
||||
} else if (questionType === 27 && question.error) {
|
||||
// 多项填空题
|
||||
isError = true;
|
||||
} else {
|
||||
question.error = '';
|
||||
}
|
||||
return isError;
|
||||
});
|
||||
if (!errors.length) {
|
||||
// 质量控制
|
||||
const question = questions.value.find((question) => {
|
||||
const { config, answer, question_type: questionType } = question;
|
||||
if (questionType === 9 && config.is_repeat) {
|
||||
// 矩阵单选
|
||||
const arr = Object.keys(answer).map((key) => key.split('_')[1]);
|
||||
return hasNConsecutiveNumbers(arr, config.allow_repeat_num + 1);
|
||||
} else if (questionType === 10 && config.is_repeat) {
|
||||
// 矩阵多选
|
||||
let arr = [];
|
||||
const obj = {};
|
||||
for (const key in answer) {
|
||||
const [index, value] = key.split('_');
|
||||
if (obj[index] === undefined) {
|
||||
obj[index] = arr.length;
|
||||
arr.push([value]);
|
||||
} else {
|
||||
arr[obj[index]].push(value);
|
||||
}
|
||||
}
|
||||
arr = arr.map((subArr) => subArr.sort().join());
|
||||
return hasNConsecutiveNumbers(arr, config.allow_repeat_num + 1);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (question) {
|
||||
await new Promise((resolve) => {
|
||||
Modal[question.config.repeat_type ? 'confirm' : 'info']({
|
||||
class: 'custom-modal custom-modal-title-notice',
|
||||
title: translatedText.value.PleaseAnswerCarefully,
|
||||
content: question.config.alert_text,
|
||||
cancelText: translatedText.value.ContinueAnswer,
|
||||
okText: translatedText.value.ReviseAnswer,
|
||||
onCancel: () => {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// 质量控制(选择题)
|
||||
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 cycle = [];
|
||||
const questionsAnswer = [];
|
||||
questions.value.forEach((question) => {
|
||||
questionsAnswer.push({
|
||||
question_index: question.question_index,
|
||||
answer: question.answer || {}
|
||||
});
|
||||
// 循环
|
||||
if (question.question_type === 1 || question.question_type === 2) {
|
||||
const optionKeys = [];
|
||||
question.list.forEach((list) => {
|
||||
list.options.forEach((option) => {
|
||||
optionKeys.push(option.option_key);
|
||||
});
|
||||
});
|
||||
cycle.push({
|
||||
question_index: question.question_index,
|
||||
options: {
|
||||
a: optionKeys.join()
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
loading.value = true;
|
||||
try {
|
||||
if (callbackBeforePage) await callbackBeforePage();
|
||||
const { data } = await AnswerApi.answer({
|
||||
id: proxy.$route.query.sn,
|
||||
data: {
|
||||
cycle,
|
||||
answer: JSON.stringify(questionsAnswer),
|
||||
is_test: proxy.$route.query.is_test
|
||||
}
|
||||
});
|
||||
// 更新答案
|
||||
updateAnswer(data.answer_info_autofill);
|
||||
// 更新分页数组
|
||||
questionsData.value.answer.pages = data.pages;
|
||||
// 京东跳转
|
||||
if (data.action.code === 302) {
|
||||
location.href = data.action.msg;
|
||||
}
|
||||
// 更新问卷状态
|
||||
if (data.action.code === 20014) {
|
||||
const timer = setTimeout(() => {
|
||||
const url = data.action.msg;
|
||||
toUrl(url);
|
||||
clearTimeout(timer);
|
||||
});
|
||||
}
|
||||
// 判断抽奖
|
||||
if (data.action.lottery) {
|
||||
const endAnswerTime = new Date().getTime();
|
||||
const answerTime = endAnswerTime - startAnswerTime;
|
||||
proxy.$router.push({
|
||||
path: '/luck',
|
||||
query: {
|
||||
user_id: proxy.$route.query.sn,
|
||||
id: questionsData.value.answer.sn,
|
||||
time_num: parseInt(answerTime / 1000),
|
||||
is_test: proxy.$route.query.is_test || 0
|
||||
}
|
||||
});
|
||||
}
|
||||
// 选项隐藏
|
||||
hideOptions(data.hide_options);
|
||||
// 更新action
|
||||
questionsData.value.action = data.action;
|
||||
callback(data.jump_to);
|
||||
// 写入关联选项缓存
|
||||
if (props.isAnswer && questionsData.value.survey.is_breakpoint) {
|
||||
if (data.action.code === 20010) {
|
||||
const questionsCache = JSON.parse(localStorage.getItem('questionsCache')) || {};
|
||||
questionsCache[proxy.$route.query.sn] = questionsData.value.questions;
|
||||
localStorage.setItem('questionsCache', JSON.stringify(questionsCache));
|
||||
} else {
|
||||
const questionsCache = JSON.parse(localStorage.getItem('questionsCache'));
|
||||
if (questionsCache?.[proxy.$route.query.sn]) {
|
||||
delete questionsCache[proxy.$route.query.sn];
|
||||
localStorage.setItem('questionsCache', JSON.stringify(questionsCache));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
loading.value = false;
|
||||
} else {
|
||||
console.log(errors);
|
||||
const { error, title, question_index } = errors[0];
|
||||
const lines = (error || '')
|
||||
.split('\n')
|
||||
.filter((i) => !!i)
|
||||
.map((i) => `${title}:${i}`);
|
||||
msg.error(lines.join('\n'));
|
||||
|
||||
// 锚点
|
||||
const anchor = document.querySelector(`#questionIndex${question_index}`);
|
||||
console.log(anchor, scrollbar.value);
|
||||
scrollbar.value.scrollTo(0, anchor.offsetTop - (props.isMobile ? 20 : 40));
|
||||
}
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
qustion.hideOptions = hide.option_key || [];
|
||||
}
|
||||
}
|
||||
|
||||
// 关联引用
|
||||
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 updateAnswer(auto) {
|
||||
if (auto) {
|
||||
auto.forEach((autoItem) => {
|
||||
const question = questionsData.value.questions.find(
|
||||
(question) => question.question_index === autoItem.question_index
|
||||
);
|
||||
question.answer = JSON.parse(autoItem.answer);
|
||||
|
||||
// 隐藏的题目,自动填写时,并被后续题目关联选项,会出现关联选项未正确展示的情况,此暂时解决方法
|
||||
const evt1 = {};
|
||||
|
||||
if ([1].includes(question.question_type)) {
|
||||
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);
|
||||
onRelation(evt1, question);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转链接
|
||||
function toUrl(url) {
|
||||
// 判断是否小程序路径
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.query,
|
||||
() => {
|
||||
console.log('route.query.sn', route.query);
|
||||
location.reload();
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
page,
|
||||
pages,
|
||||
loading,
|
||||
prevLoading,
|
||||
showPage,
|
||||
styleInfo,
|
||||
questions,
|
||||
scrollbar,
|
||||
questionsData,
|
||||
localPageTimer,
|
||||
answer,
|
||||
previous,
|
||||
next,
|
||||
onRelation
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -139,7 +139,7 @@ function shareLink() {
|
||||
function downLoadImg() {
|
||||
const { title, url } = publishInfo.value.download_url;
|
||||
if (utils.getCookie('xToken')) {
|
||||
appBridge.save2Album(url, (result: any) => {
|
||||
appBridge.save2Album(url, () => {
|
||||
showSuccessToast('下载成功');
|
||||
});
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user