fix: bug
This commit is contained in:
440
src/views/planetDesign/Design/questions/choice/Choice.vue
Normal file
440
src/views/planetDesign/Design/questions/choice/Choice.vue
Normal file
@@ -0,0 +1,440 @@
|
||||
<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"></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)"
|
||||
></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"></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)"
|
||||
></i
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</question-tinymce-option>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</a-checkbox-group>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<a-button
|
||||
type="text"
|
||||
class="custom-button"
|
||||
@click="addOptionHandle(optionList.length - 1)"
|
||||
>
|
||||
<div class="flex-align">
|
||||
<i class="iconfont"></i>
|
||||
<span style="margin-left: 6px">添加选项</span>
|
||||
</div>
|
||||
</a-button>
|
||||
<related-option :info="info" />
|
||||
<option-show :info="info" />
|
||||
<logical :info="info" />
|
||||
<BatchManageOptions :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 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,
|
||||
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>
|
||||
Reference in New Issue
Block a user