Files
ylst-pc/src/views/planetDesign/Design/questions/choice/Choice.vue
2022-10-17 22:57:42 +08:00

449 lines
14 KiB
Vue

<template>
<QuesBaseItem :info="info">
<template v-slot:noMrgOption>
<a-radio-group
class="custom-radio-group"
style="width: 100%"
v-if="copyInfo.question_type === 1"
>
<draggable
v-model="optionList"
item-key="id"
handle=".mover"
chosenClass="chosen"
animation="300"
@end="sortOptionHandle"
>
<template #item="{ element, index }">
<div class="choice-option-ele">
<div
style="height: 40px; line-height: 40px"
:style="{ opacity: element.edit ? 0 : 1 }"
>
<i class="iconfont choice-option-ele-icon mover">&#xe6c3;</i>
</div>
<div class="tinymce-option" style="flex: 1">
<div
class="choice-option-radio"
:class="{ 'choice-option-radio-active': element.edit }"
>
<a-radio :value="element.id" class="choice-option-radio-item">
</a-radio>
<question-tinymce-option
v-model:html="element.option"
:ref="`tinymce_${index}`"
:preventKeyDown="true"
:hasAfter="!element.edit && element.is_other"
@keyDown="addOptionHandle(index)"
@focusChange="editChange($event, element, index)"
>
<template v-slot:content>
<div v-if="element.edit" class="choice-option-radio-btn">
<a-checkbox
:checked="Boolean(element.is_other)"
class="margin"
@change="otherChange($event, element)"
><span style="color: #434343"
>设为其他项</span
></a-checkbox
>
<div class="line" style="margin-right: 30px"></div>
<i
class="iconfont choice-option-radio-btn-remove margin"
@click="deleteOptionHandle(element.id)"
>&#xe6c5;</i
>
</div>
</template>
</question-tinymce-option>
</div>
</div>
</div>
</template>
</draggable>
</a-radio-group>
<a-checkbox-group
class="custom-checkbox-group"
:value="checkGroups"
style="width: 100%"
v-else-if="copyInfo.question_type === 2"
@change="groupChange"
>
<draggable
v-model="optionList"
item-key="id"
handle=".mover"
chosenClass="chosen"
animation="300"
@end="sortOptionHandle"
>
<template #item="{ element, index }">
<div class="choice-option-ele">
<div
style="height: 40px; line-height: 40px"
:style="{ opacity: element.edit ? 0 : 1 }"
>
<i class="iconfont choice-option-ele-icon mover">&#xe6c3;</i>
</div>
<div class="tinymce-option" style="flex: 1">
<div
class="choice-option-radio"
:class="{ 'choice-option-radio-active': element.edit }"
>
<a-checkbox
class="choice-option-radio-item"
style="margin-right: 8px"
:value="element.id"
>
</a-checkbox>
<question-tinymce-option
v-model:html="element.option"
:ref="`tinymce_${index}`"
:preventKeyDown="true"
:hasAfter="!element.edit && element.is_other"
@keyDown="addOptionHandle(index)"
@focusChange="editChange($event, element, index)"
>
<template v-slot:content>
<div v-if="element.edit" class="choice-option-radio-btn">
<a-checkbox-group
v-model:value="element.other"
@change="removeOtherChange(element)"
>
<a-checkbox value="remove" class="margin"
><span style="color: #434343"
>设为排他项</span
></a-checkbox
>
<a-checkbox value="other" class="margin"
><span style="color: #434343"
>设为其他项</span
></a-checkbox
>
</a-checkbox-group>
<div class="line" style="margin-right: 30px"></div>
<i
class="iconfont choice-option-radio-btn-remove margin"
@click="deleteOptionHandle(element.id)"
>&#xe6c5;</i
>
</div>
</template>
</question-tinymce-option>
</div>
</div>
</div>
</template>
</draggable>
</a-checkbox-group>
</template>
<template v-slot:footer>
<a-button
v-if="!disableUpdateBtn"
type="text"
class="custom-button"
@click="addOptionHandle(optionList.length - 1)"
>
<div class="flex-align">
<i class="iconfont">&#xe689;</i>
<span style="margin-left: 6px">添加选项</span>
</div>
</a-button>
<related-option v-if="!disableUpdateBtn" :info="info" />
<option-show v-if="!disableUpdateBtn" :info="info" />
<logical :info="info" />
<BatchManageOptions
v-if="!disableUpdateBtn"
:info="info"
></BatchManageOptions>
</template>
</QuesBaseItem>
</template>
<script>
import draggable from "vuedraggable";
import { computed, ref } from "@vue/reactivity";
import Logical from "../../components/Logical.vue";
import { getCurrentInstance, nextTick, watch } from "@vue/runtime-core";
import Option from "../../mode/option.js";
import QuestionTinymceOption from "../../components/QuestionTinymceOption.vue";
import QuesBaseItem from "../../fragement/QuesBaseItem.vue";
import RelatedOption from "../../components/RelatedOption.vue";
import BatchManageOptions from "../../components/BatchManageOptions.vue";
import { useStore } from "vuex";
import OptionShow from "../../components/OptionShow.vue";
import * as cheerio from "cheerio";
import { message } from "ant-design-vue";
import { getOptionName } from "../../js/util.js";
export default {
name: "Choice",
components: {
QuestionTinymceOption,
draggable,
Logical,
QuesBaseItem,
RelatedOption,
BatchManageOptions,
OptionShow,
},
props: {
info: {
type: Object,
default: () => {},
},
},
setup(props, context) {
const store = useStore();
const copyInfo = computed(() => {
const info = JSON.parse(JSON.stringify(props.info));
if (info.options[0]) {
info.options[0].forEach((x) => {
const other = [];
if (x.is_remove_other) {
other.push("remove");
}
if (x.is_other) {
other.push("other");
}
x.other = other;
x.check = false;
});
}
return info;
});
const disableUpdateBtn = computed(() => {
return props.info.permissions?.disable_update || false;
});
const optionList = ref([]);
const checkGroups = ref([]);
/** 修改选项 */
const editChange = (e, element, index) => {
element.edit = e;
if (!element.edit) {
const curUpdateOpt = props.info.options[0].find(
(x) => x.id === element.id
);
if (
curUpdateOpt.option !== element.option ||
curUpdateOpt.is_other !== element.is_other ||
curUpdateOpt.is_remove_other !== element.is_remove_other
) {
// 选项验证重复
const findIndex = optionSet(
copyInfo.value.options[0],
element,
index
);
if (findIndex !== -1) {
element.option = copyInfo.value.options[0][index].option;
return;
}
optionList.value.forEach((opt) => {
if (opt.id === element.id) {
opt.option = element.option;
}
});
emitInfo();
}
}
};
/** 多选题设为排他,其他 */
const removeOtherChange = (element) => {
element.is_remove_other = 0;
element.is_other = 0;
if (element.other.includes("remove")) {
element.is_remove_other = 1;
}
if (element.other.includes("other")) {
element.is_other = 1;
}
};
/** 单选设为其他项 */
const otherChange = (e, element) => {
element.is_other = Number(e.target.checked);
};
const instance = getCurrentInstance();
/** 新增选项 */
const addOptionHandle = (index) => {
const findIndex = optionSet(
optionList.value,
optionList.value[index],
index
);
if (findIndex !== -1) {
optionList.value[index].option =
copyInfo.value.options[0][index].option;
}
const newOption = new Option();
copyInfo.value.last_option_index++;
newOption.option = `<p>选项${getOptionName(optionList.value)}</p>`;
newOption.option_index = copyInfo.value.last_option_index;
optionList.value.splice(index + 1, 0, newOption);
// 解决在回车插入选项时,需要让新加的选项处于编辑状态,而不是还在之前位置
nextTick(() => {
const refs = instance.refs;
refs[`tinymce_${index}`]?.onClickOutsides();
refs[`tinymce_${index + 1}`]?.clickHandle();
});
emitInfo();
};
/**选项验重 */
const optionSet = (optionList, element, index) => {
const findIndex = optionList.findIndex((opt, fIndex) => {
if (fIndex !== index) {
const $old = cheerio
.load(opt.option)
.text()
.replace(/\s*/g, "")
.replaceAll("\n", "");
const $new = cheerio
.load(element.option)
.text()
.replace(/\s*/g, "")
.replaceAll("\n", "");
return $old === $new;
}
});
if (findIndex !== -1) {
message.error(
`提示:第${index + 1}个选项和第${
findIndex + 1
}个选项重复,无法自动保存!`
);
}
return findIndex;
};
/** 排序选项 */
const sortOptionHandle = () => {
emitInfo();
};
/** 删除选项 */
const deleteOptionHandle = (id) => {
optionList.value = optionList.value.filter((x) => x.id !== id);
emitInfo();
};
const emitInfo = () => {
const copyOptionList = optionList.value.map((opt) => {
const temp = {
...opt,
};
delete temp.check;
delete temp.other;
delete temp.edit;
return temp;
});
const tempCopyInfo = JSON.parse(JSON.stringify(copyInfo.value));
tempCopyInfo.options[0] = copyOptionList;
context.emit("update", tempCopyInfo);
};
const groupChange = (e) => {
if (e.length) {
const last = e[e.length - 1];
const option = optionList.value.find((option) => option.id === last);
if (option.is_remove_other) {
e = [last];
} else {
e = e.filter((value) =>
optionList.value.find(
(option) => option.id === value && !option.is_remove_other
)
);
}
}
checkGroups.value = e;
};
watch(
copyInfo,
(val) => {
optionList.value = JSON.parse(
JSON.stringify(val.options[0] ? copyInfo.value.options[0] : [])
);
},
{ immediate: true }
);
return {
copyInfo,
disableUpdateBtn,
optionList,
checkGroups,
editChange,
otherChange,
removeOtherChange,
addOptionHandle,
deleteOptionHandle,
sortOptionHandle,
groupChange,
};
},
};
</script>
<style lang="scss" scoped>
.choice {
&-option {
&-ele {
display: flex;
margin-bottom: 18px;
&-icon {
font-size: 24px;
cursor: move;
opacity: 0;
height: 40px;
&:hover {
color: $yili-default-color;
}
}
&:hover .iconfont {
opacity: 1;
}
}
&-radio {
display: flex;
min-height: 40px;
width: 100%;
border-radius: 5px;
border: 1px solid transparent;
padding-left: 12px;
&:hover {
border: 1px dashed #8c8c8c;
}
&-item {
height: 40px;
line-height: 40px;
}
&-btn {
display: flex;
align-items: center;
height: 40px;
&-remove {
font-size: 16px;
cursor: pointer;
color: #bfbfbf;
&:hover {
color: $yili-default-color;
}
}
}
&-active {
background: #f5f5f5;
border: 1px solid transparent !important;
}
}
}
.line {
width: 1px;
height: 16px;
background: #d8d8d8;
opacity: 0.2;
border: 1px solid #979797;
}
.margin {
margin-right: 30px;
}
}
.chosen {
background: #ffffff;
box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.2);
}
</style>