319 lines
8.9 KiB
Vue
319 lines
8.9 KiB
Vue
<template>
|
||
<div class="design-create">
|
||
<draggable
|
||
v-model:data="questionInfo.questions"
|
||
item-key="id"
|
||
handle=".moverQues"
|
||
chosenClass="chosen"
|
||
animation="300"
|
||
:scroll="true"
|
||
>
|
||
<template #item="{ element, index }">
|
||
<choose-question
|
||
:element="element"
|
||
:questions="questionInfo.questions"
|
||
:index="index"
|
||
:chooseQuestionId="chooseQuestionId"
|
||
@get-choose-question-id="getChooseQuestionId"
|
||
>
|
||
<!-- 选择题 -->
|
||
<Choice
|
||
v-if="element.question_type === 1 || element.question_type === 2"
|
||
:element="element"
|
||
:index="index"
|
||
:active="chooseQuestionId === element.id"
|
||
></Choice>
|
||
<!-- 填空题 -->
|
||
<Completion
|
||
v-if="element.question_type === 4"
|
||
:index="index"
|
||
:element="element"
|
||
:active="chooseQuestionId === element.id"
|
||
sn="lXEBBpE2"
|
||
></Completion>
|
||
|
||
<!-- 矩阵题 -->
|
||
<martrix-question
|
||
v-if="
|
||
element.question_type === 8 ||
|
||
element.question_type === 9 ||
|
||
element.question_type === 10
|
||
"
|
||
:element="element"
|
||
:index="index"
|
||
:active="chooseQuestionId === element.id"
|
||
/>
|
||
|
||
<!-- 签名题 -->
|
||
<sign-question
|
||
v-if="[22].includes(element.question_type)"
|
||
:element="element"
|
||
:index="index"
|
||
:active="chooseQuestionId === element.id"
|
||
/>
|
||
|
||
<!-- 文件上传题 -->
|
||
<file-upload
|
||
v-if="element.question_type === 18"
|
||
:element="element"
|
||
:index="index"
|
||
:active="chooseQuestionId === element.id"
|
||
></file-upload>
|
||
|
||
<!-- 打分题 -->
|
||
<Rate
|
||
v-if="element.question_type === 5"
|
||
:element="element"
|
||
:index="index"
|
||
:active="chooseQuestionId === element.id"
|
||
sn="lXEBBpE2"
|
||
/>
|
||
|
||
<!--图文-->
|
||
<TextWithImages
|
||
v-if="element.question_type === 6"
|
||
:element="element"
|
||
:index="index"
|
||
:active="chooseQuestionId === element.id"
|
||
/>
|
||
<!--图文-->
|
||
<NPS
|
||
v-if="element.question_type === 106"
|
||
:element="element"
|
||
:index="index"
|
||
:active="chooseQuestionId === element.id"
|
||
/>
|
||
<!--组件底部左侧操作-->
|
||
<template #action="{ element: el }">
|
||
<div class="flex slot-actions">
|
||
<template v-for="(item, optionIndex) in actionOptions">
|
||
<div
|
||
v-if="item.question_type.includes(el.question_type)"
|
||
:key="optionIndex"
|
||
class="flex"
|
||
>
|
||
<template v-for="(act, actIndex) in item.actions" :key="actIndex">
|
||
<div class="flex align-center action-item" @click="actionEvent(act, el)">
|
||
<van-icon :name="act.icon"></van-icon>
|
||
<span class="ml10">{{ act.label }}</span>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</template>
|
||
</choose-question>
|
||
|
||
<!-- {{ element.question_type }}-->
|
||
<!-- {{questionInfo.survey.pages.length}}-->
|
||
<div v-if="!filterGap">
|
||
<paging
|
||
v-if="!element.question_type && questionInfo.survey.pages.length > 1"
|
||
:info="element"
|
||
:index="index"
|
||
:active="pageIsActive(activeIndex, questionInfo.questions, element.page)"
|
||
@click.stop=""
|
||
/>
|
||
</div>
|
||
</template>
|
||
</draggable>
|
||
</div>
|
||
</template>
|
||
<script setup>
|
||
import { v4 as uuidv4 } from 'uuid';
|
||
import { ref, onMounted, watch, computed } from 'vue';
|
||
import { useCounterStore } from '@/stores/counter';
|
||
import { storeToRefs } from 'pinia';
|
||
import Draggable from './components/Draggable.vue';
|
||
import Choice from './components/Questions/Choice.vue';
|
||
import ChooseQuestion from './components/ChooseQuestion.vue';
|
||
import Paging from './components/Questions/paging/Paging.vue';
|
||
import Completion from './components/Questions/Completion.vue';
|
||
import MartrixQuestion from './components/Questions/MartrixQuestion.vue';
|
||
import Rate from './components/Questions/Rate.vue';
|
||
import TextWithImages from '@/views/Design/components/Questions/TextWithImages.vue';
|
||
import SignQuestion from './components/Questions/SignQuestion.vue';
|
||
import FileUpload from './components/Questions/FileUpload.vue';
|
||
import NPS from '@/views/Design/components/Questions/NPS.vue';
|
||
|
||
const activeIndex = ref(-1);
|
||
|
||
// 获取所有的 question 列表内容
|
||
const { filterGap, activeId } = defineProps({
|
||
filterGap: {
|
||
type: Boolean,
|
||
required: false,
|
||
default: false
|
||
},
|
||
activeId: {
|
||
type: String,
|
||
default: ''
|
||
}
|
||
});
|
||
|
||
watch(
|
||
() => activeId,
|
||
(newVal) => {
|
||
chooseQuestionId.value = newVal;
|
||
}
|
||
);
|
||
|
||
/**
|
||
* 工具函数
|
||
*/
|
||
function util() {
|
||
/** 通过id找到数组中对应的下标 */
|
||
const getIndexById = (arr, id) => arr.findIndex((i) => i.id === id);
|
||
/** 通过分页找到数组中对应的下标 */
|
||
const getIndexByPage = (arr, page) => arr.findIndex((i) => i.page === page);
|
||
const quesIsActive = (activeIndex, questionList, eleId) => {
|
||
return activeIndex === getIndexById(questionList, eleId);
|
||
};
|
||
const pageIsActive = (activeIndex, questionList, elePage) => {
|
||
return activeIndex === getIndexByPage(questionList, elePage);
|
||
};
|
||
const quesIsDisabled = (questionIndex, disabledQuestionIndex) => {
|
||
return (disabledQuestionIndex || []).includes(questionIndex);
|
||
};
|
||
const pagingDisabled = (index, question, disabledQuestionIndex) => {
|
||
if (disabledQuestionIndex.includes(question?.[index - 1]?.questionIndex)) {
|
||
return true;
|
||
}
|
||
for (let i = 1; i < question.length - 1; i++) {
|
||
if (question?.[index + i]?.page) {
|
||
continue;
|
||
}
|
||
if (disabledQuestionIndex.includes(question?.[index + i]?.questionIndex)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
/** 工具函数,复制store中的数据,避免直接更改store */
|
||
const copyStoreContent = (store) => {
|
||
return JSON.parse(JSON.stringify(store.state.common));
|
||
};
|
||
return {
|
||
getIndexById,
|
||
getIndexByPage,
|
||
quesIsActive,
|
||
pageIsActive,
|
||
quesIsDisabled,
|
||
pagingDisabled,
|
||
copyStoreContent
|
||
};
|
||
}
|
||
|
||
const { pageIsActive } = util();
|
||
// 获取 Store 实例
|
||
const counterStore = useCounterStore();
|
||
const store = storeToRefs(counterStore);
|
||
|
||
const chooseQuestionId = ref('');
|
||
const questionInfo = computed(() => store.questionsInfo.value);
|
||
|
||
const emit = defineEmits(['getActiveQuestion']);
|
||
// 获取选中的题目的ID
|
||
const getChooseQuestionId = (questionItem) => {
|
||
chooseQuestionId.value = questionItem.id;
|
||
// 向外传出选中的题目
|
||
emit('getActiveQuestion', questionItem);
|
||
};
|
||
// 组件对应的操作
|
||
const actionOptions = [
|
||
{
|
||
question_type: [1, 2, 5],
|
||
actions: [
|
||
{
|
||
label: '添加选项',
|
||
icon: 'add',
|
||
fun: 'radioAddOption'
|
||
}
|
||
]
|
||
},
|
||
{
|
||
question_type: [8, 9, 10],
|
||
// 矩阵问卷逻辑处理
|
||
actions: [
|
||
{
|
||
label: '添加行标签',
|
||
icon: 'add',
|
||
fun: 'addMatrixRowOption'
|
||
},
|
||
{
|
||
label: '添加列标签',
|
||
icon: 'add',
|
||
fun: 'addMatrixColumnOption'
|
||
}
|
||
]
|
||
}
|
||
];
|
||
// 事件分发
|
||
const actionEvent = (item, el) => {
|
||
actionFun[item.fun](el);
|
||
};
|
||
// 总事件注册
|
||
const actionFun = {
|
||
// 单选事件 添加选项
|
||
radioAddOption: (element) => {
|
||
element.options.map((item) => {
|
||
item.push({
|
||
id: uuidv4(),
|
||
option: `选项${item.length + 1}`,
|
||
option_config: {
|
||
image_url: [],
|
||
title: '',
|
||
instructions: [],
|
||
option_type: 0,
|
||
limit_right_content: ''
|
||
},
|
||
option_index: element.last_option_index + 1,
|
||
parent_id: 0,
|
||
type: 0,
|
||
cascade: [],
|
||
config: []
|
||
});
|
||
});
|
||
element.last_option_index += 1;
|
||
},
|
||
|
||
/**
|
||
* martrix 矩阵行数增加
|
||
* @param element {import('./components/Questions/types/martrix.js').MatrixSurveyQuestion}
|
||
*/
|
||
addMatrixRowOption: (element) => {
|
||
element.options[0].push({
|
||
option: '新增行'
|
||
});
|
||
},
|
||
/**
|
||
* martrix 矩阵列数增加
|
||
* @param element {import('./components/Questions/types/martrix.js').MatrixSurveyQuestion}
|
||
*/
|
||
addMatrixColumnOption: (element) => {
|
||
element.options[1].push({ option: '新增列' });
|
||
}
|
||
};
|
||
|
||
onMounted(() => {
|
||
questionInfo.value = store.questionsInfo.value;
|
||
});
|
||
</script>
|
||
<style scoped lang="scss">
|
||
.ml10 {
|
||
margin-left: 5px;
|
||
}
|
||
|
||
.design-create {
|
||
//min-height: calc(100vh);
|
||
background-color: #e9eef3;
|
||
color: #333;
|
||
|
||
.slot-actions {
|
||
& .action-item + .action-item {
|
||
margin-left: 10px;
|
||
}
|
||
}
|
||
}
|
||
</style>
|