1502 lines
47 KiB
Vue
1502 lines
47 KiB
Vue
<template>
|
||
<div class="catalog">
|
||
<div
|
||
class="catalog-more"
|
||
:style="{
|
||
height: openCheckbox ? '48px' : 0
|
||
}"
|
||
>
|
||
<a-tooltip title="移动" :mouseEnterDelay="1" placement="bottom">
|
||
<i class="iconfont" @click="openBatchModal('move')"></i>
|
||
</a-tooltip>
|
||
<a-tooltip title="复制" :mouseEnterDelay="1" placement="bottom">
|
||
<i class="iconfont" @click="openBatchModal('copy')"></i>
|
||
</a-tooltip>
|
||
<a-tooltip title="删除" :mouseEnterDelay="1" placement="bottom">
|
||
<i class="iconfont" @click="openBatchModal('delete')"></i>
|
||
</a-tooltip>
|
||
<a-tooltip title="题库" :mouseEnterDelay="1" placement="bottom">
|
||
<i class="iconfont" @click="openBatchModal('bank')"></i>
|
||
</a-tooltip>
|
||
</div>
|
||
<div style="padding: 12px">
|
||
<a-input
|
||
class="custom-input"
|
||
style="height: 32px"
|
||
allowClear
|
||
v-model:value="searchName"
|
||
placeholder="请输入题目名"
|
||
>
|
||
<template #prefix>
|
||
<i class="iconfont" style="color: #bfbfbf"></i>
|
||
</template>
|
||
</a-input>
|
||
</div>
|
||
<div class="catalog-main scrollbar">
|
||
<div
|
||
class="catalog-checkall"
|
||
:style="{
|
||
overflow: 'hidden',
|
||
marginTop: openCheckbox ? '24px' : 0,
|
||
width: openCheckbox ? '70px' : 0,
|
||
height: openCheckbox ? '20px' : 0,
|
||
opacity: openCheckbox ? 1 : 0
|
||
}"
|
||
@click="toTitle('introduct')"
|
||
>
|
||
<a-checkbox
|
||
v-model:checked="checkboxAll"
|
||
class="custom-checkbox"
|
||
style="margin-right: 8px"
|
||
@change="checkboxAllHandle"
|
||
></a-checkbox>
|
||
<div style="word-break: keep-all">全选</div>
|
||
</div>
|
||
<div class="catalog-text" @click="toTitle('introduct')">
|
||
<i class="iconfont catalog-text-icon"></i>
|
||
<div v-html="surveyTitle"></div>
|
||
</div>
|
||
<div class="catalog-text" style="padding-bottom: 24px" @click="toTitle('introduct')">
|
||
<i class="iconfont catalog-text-icon"></i>
|
||
<span>介绍语</span>
|
||
</div>
|
||
<div class="catalog-page" v-for="(ques, index) in questionList" :key="index">
|
||
<div class="catalog-line"></div>
|
||
<div class="catalog-page-title">第{{ ques.page }}页 ({{ ques.first_title }}~{{ ques.last_title }})</div>
|
||
<draggable
|
||
v-model="ques.child"
|
||
item-key="id"
|
||
animation="300"
|
||
:scroll="true"
|
||
handle=".moverQues"
|
||
:group="dragGroup"
|
||
@end="dragEndHandle"
|
||
>
|
||
<template #item="{ element }">
|
||
<div class="catalog-text">
|
||
<a-checkbox
|
||
v-model:checked="element.checked"
|
||
class="custom-checkbox"
|
||
:disabled="Boolean(element.permissions?.disable_update || false)"
|
||
:style="{
|
||
marginRight: openCheckbox ? '8px' : 0,
|
||
width: openCheckbox ? '17px' : 0,
|
||
minWidth: openCheckbox ? '17px' : 0
|
||
}"
|
||
@change="checkboxHandle"
|
||
></a-checkbox>
|
||
<div class="flex-align" @click="skipToHandle(element)">
|
||
<i class="iconfont catalog-text-icon" style="margin-right: 8px" v-html="getIcon(element)"></i>
|
||
<div class="catalog-text-content" :style="{ width: `${catalogTextWidth}px` }">
|
||
<span style="word-break: keep-all">{{ element.title }}</span>
|
||
<p v-html="nodeHandle(element.stem)"></p>
|
||
</div>
|
||
<div class="catalog-text-hover" v-if="!openCheckbox && !searchName">
|
||
<i class="iconfont moverQues"></i>
|
||
<i class="iconfont" v-if="!disableCopyBtn(element)" @click.stop="copyHandle(element)"></i>
|
||
<i class="iconfont" v-if="!disableDelBtn(element)" @click.stop="deleteHandle(element)"></i>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</draggable>
|
||
</div>
|
||
<div class="catalog-line"></div>
|
||
<div class="catalog-text" @click="toTitle('endLang')">
|
||
<i class="iconfont catalog-text-icon"></i>
|
||
<div>结束语</div>
|
||
</div>
|
||
</div>
|
||
<a-modal
|
||
v-model:visible="moveVisible"
|
||
title="批量移动"
|
||
class="custom-modal"
|
||
:maskClosable="false"
|
||
:destroyOnClose="true"
|
||
:width="500"
|
||
>
|
||
<div class="catalog-modal-content">
|
||
<span>移动至</span>
|
||
<a-select
|
||
v-model:value="moveQuesIndex"
|
||
placeholder="请选择问题"
|
||
class="custom-select catalog-modal-content-select"
|
||
>
|
||
<a-select-option v-for="ques in allQuesList" :key="ques.id" :value="ques.question_index">
|
||
<div class="catalog-modal-content-select-html" v-html="`${ques.title || ''}${ques.stemText || ''}`"></div
|
||
></a-select-option>
|
||
</a-select>
|
||
<a-select v-model:value="moveLocation" placeholder="请选择位置" class="custom-select" style="width: 120px">
|
||
<a-select-option :key="0" :value="0">之前</a-select-option>
|
||
<a-select-option :key="1" :value="1">之后</a-select-option>
|
||
</a-select>
|
||
</div>
|
||
<template #footer>
|
||
<div>
|
||
<a-button @click="moveVisible = false">取消</a-button>
|
||
<a-button type="primary" :disabled="moveDiabled" @click="batchMoveOk">确定</a-button>
|
||
</div>
|
||
</template>
|
||
</a-modal>
|
||
<a-modal
|
||
v-model:visible="copyVisible"
|
||
title="批量复制"
|
||
class="custom-modal"
|
||
:maskClosable="false"
|
||
:destroyOnClose="true"
|
||
:width="500"
|
||
>
|
||
<div class="catalog-modal-content">
|
||
<span>复制</span>
|
||
<a-input-number
|
||
class="catalog-modal-content-select"
|
||
v-model:value="copyNum"
|
||
:min="0"
|
||
placeholder="请输入复制次数"
|
||
/>
|
||
<span>组</span>
|
||
</div>
|
||
<template #footer>
|
||
<div>
|
||
<a-button @click="copyVisible = false">取消</a-button>
|
||
<a-button type="primary" :disabled="copyDiabled" @click="batchCopyOk">确定</a-button>
|
||
</div>
|
||
</template>
|
||
</a-modal>
|
||
<a-modal
|
||
v-model:visible="delVisible"
|
||
title="批量删除"
|
||
class="custom-modal"
|
||
:maskClosable="false"
|
||
:destroyOnClose="true"
|
||
:width="500"
|
||
>
|
||
<div class="catalog-modal-content">
|
||
<div>
|
||
<div class="catalog-modal-content-item" v-for="ques in checkQues" :key="ques.id">
|
||
{{ ques.title }} {{ ques.stemText }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<template #footer>
|
||
<div>
|
||
<a-button @click="delVisible = false">取消</a-button>
|
||
<a-button type="primary" @click="batchDeleteOk">删除</a-button>
|
||
</div>
|
||
</template>
|
||
</a-modal>
|
||
<a-modal
|
||
v-model:visible="bankVisible"
|
||
title="保存到题库"
|
||
class="custom-modal"
|
||
:maskClosable="false"
|
||
:destroyOnClose="true"
|
||
:width="600"
|
||
>
|
||
<BatchSelectQuesBank
|
||
ref="batchSelectQuesBankRef"
|
||
@addclass="addclass1"
|
||
@checkGroup="
|
||
($event) => {
|
||
checkGroupInfo = $event;
|
||
}
|
||
"
|
||
/>
|
||
<template #footer>
|
||
<div class="modal-footer">
|
||
<div class="modal-footer-flex" @click="addGroupHandle">
|
||
<div
|
||
class="modal-footer-btn"
|
||
:class="{
|
||
'modal-footer-btn-disable': disableBtn
|
||
}"
|
||
>
|
||
<i class="iconfont"></i>
|
||
<span>新增分类</span>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer-flex">
|
||
<a-button @click="bankVisible = false">取消</a-button>
|
||
<a-button type="primary" :loading="saveLoading" @click="batchBankOk">保存</a-button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</a-modal>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { computed, reactive, ref } from '@vue/reactivity';
|
||
import { useStore } from 'vuex';
|
||
import Scroll from '../js/scroll.js';
|
||
import { createVNode, nextTick, watch } from '@vue/runtime-core';
|
||
import { basicQuesTypeList, quickQuesTypeList, advancedQuesTypeList } from '../../../../utils/common';
|
||
import { nodeHandle, getPageQuesByQues, saveQuesApi } from '../js/util.js';
|
||
import { loopingAvailable } from '../js/logic';
|
||
import draggable from 'vuedraggable';
|
||
import { v4 as uuidv4 } from 'uuid';
|
||
import { Modal, message } from 'ant-design-vue';
|
||
import { ExclamationCircleFilled } from '@ant-design/icons-vue';
|
||
import BatchSelectQuesBank from '../components/BatchSelectQuesBank.vue';
|
||
import { getQuesBankList, batchSaveQuesIntoBank } from '../../api/api.js';
|
||
export default {
|
||
name: 'QuestionCatalog',
|
||
components: { draggable, BatchSelectQuesBank },
|
||
props: {
|
||
openCheckbox: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
setup(props, context) {
|
||
const store = useStore();
|
||
const questionList = ref([]);
|
||
const questionGroupKeepOne = ref({});
|
||
const oldQuestionList = ref([]);
|
||
const disableBtn = ref(null);
|
||
const dragGroup = ref(`catalog_${uuidv4()}`);
|
||
const isKeepCheckStatus = ref(true);
|
||
const questionArr = computed(() => {
|
||
let tmp = [];
|
||
questionList.value.map((item) => {
|
||
console.log(123456);
|
||
console.log(item);
|
||
if (item.child) {
|
||
tmp = [...tmp, ...item.child];
|
||
}
|
||
});
|
||
return tmp;
|
||
});
|
||
const addclass1 = (e) => {
|
||
disableBtn.value = e;
|
||
};
|
||
const initList = () => {
|
||
const quesInfo = JSON.parse(JSON.stringify(store.state.common.questionInfo));
|
||
const quesList = quesInfo.questions.map((ques) => {
|
||
if (ques.stem) {
|
||
ques.stemText = nodeHandle(ques.stem);
|
||
ques.checked = false;
|
||
}
|
||
return ques;
|
||
});
|
||
allQuesList.value = quesList.filter((ques) => ques.id);
|
||
const result = [];
|
||
quesInfo.survey.pages.forEach((page, index) => {
|
||
if (page.length > 0) {
|
||
const temp = {
|
||
page: index + 1,
|
||
first_title: quesList.find((ques) => ques.question_index === page[0])?.title || '',
|
||
last_title: quesList.find((ques) => ques.question_index === page[page.length - 1])?.title || '',
|
||
child: reactive([])
|
||
};
|
||
page.forEach((childPage) => {
|
||
const ques = quesList.find((ques) => ques.question_index === childPage);
|
||
// 保留多选框的选中状态
|
||
if (isKeepCheckStatus.value && questionList.value.length > 0) {
|
||
let oldQues = null;
|
||
questionList.value.forEach((q) => {
|
||
if (q.child) {
|
||
q.child.forEach((child) => {
|
||
if (child.question_index === ques.question_index) {
|
||
oldQues = child;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
ques.checked = oldQues?.checked || false;
|
||
}
|
||
if (ques) {
|
||
temp.child.push(ques);
|
||
}
|
||
});
|
||
result.push(temp);
|
||
}
|
||
});
|
||
questionList.value = result;
|
||
oldQuestionList.value = result;
|
||
isKeepCheckStatus.value = true;
|
||
};
|
||
const catalogTextWidth = ref(75);
|
||
watch(
|
||
() => props.openCheckbox,
|
||
(val) => {
|
||
if (window.document.body.clientWidth < 1280) {
|
||
catalogTextWidth.value = val ? 52 : 75;
|
||
} else {
|
||
catalogTextWidth.value = val ? 178 : 180;
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
watch(
|
||
() => questionArr.value,
|
||
() => {
|
||
questionGroupKeepOne.value = {};
|
||
questionArr.value.map((item) => {
|
||
if (item.permissions?.delete_group_keep_one === 1 && item.other) {
|
||
let tmpGroup = questionGroupKeepOne.value[item.other];
|
||
if (!tmpGroup) {
|
||
tmpGroup = 1;
|
||
} else {
|
||
tmpGroup++;
|
||
}
|
||
questionGroupKeepOne.value[item.other] = tmpGroup;
|
||
}
|
||
});
|
||
}
|
||
);
|
||
const disableMoveBtn = (element) => {
|
||
return element.permissions?.disable_update || false;
|
||
};
|
||
const disableCopyBtn = (element) => {
|
||
return element.permissions?.disable_copy || false;
|
||
};
|
||
const disableDelBtn = (element) => {
|
||
if (questionGroupKeepOne.value[element.other] && questionGroupKeepOne.value[element.other] <= 1) {
|
||
return true;
|
||
}
|
||
return element.permissions?.disable_delete || false;
|
||
};
|
||
const {
|
||
allQuesList,
|
||
checkQues,
|
||
checkboxAll,
|
||
searchName,
|
||
skipToHandle,
|
||
toTitle,
|
||
getIcon,
|
||
checkboxHandle,
|
||
checkboxAllHandle,
|
||
openBatchModal,
|
||
|
||
delVisible,
|
||
deleteHandle,
|
||
batchDeleteOk,
|
||
|
||
copyVisible,
|
||
copyNum,
|
||
copyHandle,
|
||
batchCopyOk,
|
||
|
||
moveVisible,
|
||
moveQuesIndex,
|
||
moveLocation,
|
||
dragEndHandle,
|
||
batchMoveOk,
|
||
|
||
bankVisible,
|
||
checkGroupInfo,
|
||
saveLoading,
|
||
batchSelectQuesBankRef,
|
||
batchBankOk,
|
||
addGroupHandle
|
||
} = domEventHandle(store, context, questionList, initList, isKeepCheckStatus);
|
||
watch(
|
||
() => store.state.common.questionInfo,
|
||
(val, oldVal) => {
|
||
initList();
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
watch(
|
||
searchName,
|
||
(val) => {
|
||
if (val.length > 0) {
|
||
questionList.value = JSON.parse(JSON.stringify(questionList.value))
|
||
.map((x) => {
|
||
x.child = x.child.filter((x) => `${x.title || ''}${x.stem || ''}`.includes(val));
|
||
return x;
|
||
})
|
||
.filter((x) => x.child.length > 0);
|
||
} else {
|
||
questionList.value = JSON.parse(JSON.stringify(oldQuestionList.value));
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
const surveyTitle = computed(() => store.state.common.questionInfo.survey?.title || '');
|
||
const copyDiabled = computed(() => {
|
||
return !copyNum.value;
|
||
});
|
||
const moveDiabled = computed(() => {
|
||
return !moveQuesIndex.value;
|
||
});
|
||
return {
|
||
disableBtn,
|
||
addclass1,
|
||
catalogTextWidth,
|
||
dragGroup,
|
||
searchName,
|
||
surveyTitle,
|
||
questionList,
|
||
allQuesList,
|
||
checkQues,
|
||
checkboxAll,
|
||
getIcon,
|
||
disableMoveBtn,
|
||
disableCopyBtn,
|
||
disableDelBtn,
|
||
|
||
delVisible,
|
||
deleteHandle,
|
||
batchDeleteOk,
|
||
|
||
copyVisible,
|
||
copyNum,
|
||
copyDiabled,
|
||
copyHandle,
|
||
batchCopyOk,
|
||
|
||
moveVisible,
|
||
moveQuesIndex,
|
||
moveLocation,
|
||
moveDiabled,
|
||
dragEndHandle,
|
||
batchMoveOk,
|
||
|
||
bankVisible,
|
||
checkGroupInfo,
|
||
saveLoading,
|
||
batchSelectQuesBankRef,
|
||
batchBankOk,
|
||
addGroupHandle,
|
||
|
||
skipToHandle,
|
||
toTitle,
|
||
checkboxHandle,
|
||
checkboxAllHandle,
|
||
nodeHandle,
|
||
openBatchModal,
|
||
|
||
questionGroupKeepOne
|
||
};
|
||
}
|
||
};
|
||
/**
|
||
* 页面事件统一处理
|
||
*/
|
||
function domEventHandle(store, context, questionList, initList, isKeepCheckStatus) {
|
||
const searchName = ref('');
|
||
const checkQues = ref([]);
|
||
const checkboxAll = ref(false);
|
||
const allQuesList = ref([]);
|
||
|
||
const delVisible = ref(false);
|
||
const copyVisible = ref(false);
|
||
const copyNum = ref(1);
|
||
const moveVisible = ref(false);
|
||
const moveQuesIndex = ref(undefined);
|
||
const moveLocation = ref(1);
|
||
|
||
const bankVisible = ref(false);
|
||
const checkGroupInfo = ref(null);
|
||
const saveLoading = ref(false);
|
||
const batchSelectQuesBankRef = ref(null);
|
||
const { deleteQues, batchDeleteQues, copyQues, batchCopyQues, moveQues, batchMoveQues, batchBank } = storeUpdate(
|
||
store,
|
||
context
|
||
);
|
||
const {
|
||
deleteInterceptor,
|
||
batchDeleteInterceptor,
|
||
dragEndInterceptor,
|
||
batchMoveInterceptor,
|
||
batchBankInterceptor
|
||
} = interceptorsHandle(store, checkQues);
|
||
const skipToHandle = (element) => {
|
||
context.emit('catalogCheck', element);
|
||
nextTick(() => {
|
||
new Scroll(
|
||
document.getElementById(element.id),
|
||
undefined,
|
||
undefined,
|
||
!!store.state.common?.questionInfo?.survey?.is_one_page_one_question
|
||
).animate();
|
||
});
|
||
};
|
||
const getIcon = (item) => {
|
||
let icon = '';
|
||
if (item.config.quick_type === 0) {
|
||
icon = basicQuesTypeList?.find((x) => x?.childTypes.includes(item?.question_type))?.icon || '';
|
||
if (!icon) {
|
||
icon = quickQuesTypeList?.find((x) => x?.type === item?.question_type)?.icon || '';
|
||
}
|
||
} else {
|
||
icon = quickQuesTypeList?.find((x) => x?.quickType === item?.config?.quick_type)?.icon || '';
|
||
}
|
||
if (!icon) {
|
||
icon = advancedQuesTypeList?.find((x) => x?.type === item?.question_type)?.icon || '';
|
||
}
|
||
return icon;
|
||
};
|
||
const toTitle = (str) => {
|
||
new Scroll(document.getElementById(str)).animate();
|
||
};
|
||
/**
|
||
* 打开批量弹框
|
||
*/
|
||
const openBatchModal = (flag) => {
|
||
const temp = {
|
||
move: () => {
|
||
return {
|
||
visible: moveVisible,
|
||
msg: '请先选择要移动的节点',
|
||
done: () => {}
|
||
};
|
||
},
|
||
copy: () => {
|
||
return {
|
||
visible: copyVisible,
|
||
msg: '请先选择要复制的节点',
|
||
done: () => {}
|
||
};
|
||
},
|
||
delete: () => {
|
||
return {
|
||
visible: delVisible,
|
||
msg: '请选择要删除的题目',
|
||
done: () => {}
|
||
};
|
||
},
|
||
bank: () => {
|
||
return {
|
||
visible: bankVisible,
|
||
msg: '请选择要添加的题目',
|
||
done: () => {
|
||
nextTick(() => {
|
||
batchSelectQuesBankRef.value.refreshBankList();
|
||
});
|
||
}
|
||
};
|
||
}
|
||
};
|
||
if (checkQues.value.length === 0) {
|
||
message.warning(temp[flag].call().msg);
|
||
return;
|
||
}
|
||
temp[flag].call().visible.value = true;
|
||
temp[flag].call().done();
|
||
};
|
||
/**
|
||
* 全选题目
|
||
*/
|
||
const checkboxAllHandle = () => {
|
||
questionList.value.forEach((ques) => {
|
||
ques.child.forEach((child) => {
|
||
if (!child.permissions?.disable_update) {
|
||
child.checked = checkboxAll.value;
|
||
}
|
||
});
|
||
});
|
||
checkboxHandle();
|
||
};
|
||
/**
|
||
* 选中题目回调
|
||
*/
|
||
const checkboxHandle = () => {
|
||
const checks = [];
|
||
const quesList = [];
|
||
let childLength = 0;
|
||
questionList.value.forEach((ques) => {
|
||
const childFilter = ques.child.filter((child) => !child.permissions?.disable_update);
|
||
childLength = childLength + childFilter.length;
|
||
checks.push(...ques.child.filter((child) => child.checked && !child.permissions?.disable_update));
|
||
quesList.push(...ques.child);
|
||
});
|
||
allQuesList.value = quesList.filter((ques) => !checks.find((check) => check.question_index == ques.question_index));
|
||
checkboxAll.value = childLength === checks.length;
|
||
checkQues.value = checks;
|
||
};
|
||
/**
|
||
* 复制回调
|
||
*/
|
||
const copyHandle = (quesInfo) => {
|
||
let quesInfoCopy = JSON.parse(JSON.stringify(quesInfo));
|
||
if ([105, 201].includes(quesInfoCopy.question_type)) {
|
||
quesInfoCopy.config.design_version = 0;
|
||
quesInfoCopy.config.file_url = '';
|
||
quesInfoCopy.config.file_name = '';
|
||
}
|
||
delete quesInfoCopy.checked;
|
||
const quesSaveParam = copyQues(quesInfoCopy);
|
||
saveQuesApi(quesSaveParam, store);
|
||
};
|
||
/**
|
||
* 批量复制确认回调
|
||
*/
|
||
const batchCopyOk = () => {
|
||
const allCopyQues = [];
|
||
for (let index = 0; index < copyNum.value; index++) {
|
||
allCopyQues.push(
|
||
...checkQues.value.map((check) => {
|
||
delete check.checked;
|
||
return check;
|
||
})
|
||
);
|
||
}
|
||
|
||
try {
|
||
// 请求体超过500k则退出
|
||
var len = JSON.stringify(allCopyQues).length;
|
||
if (len > 1024 * 500) {
|
||
message.warn('复制内容过多,请分批次复制');
|
||
return;
|
||
}
|
||
} catch (e) {}
|
||
|
||
copyVisible.value = false;
|
||
const quesSaveParam = batchCopyQues(
|
||
JSON.parse(JSON.stringify(allCopyQues)),
|
||
checkQues.value[checkQues.value.length - 1]
|
||
);
|
||
checkQues.value = [];
|
||
isKeepCheckStatus.value = false;
|
||
saveQuesApi(quesSaveParam, store);
|
||
};
|
||
/**
|
||
* 删除回调
|
||
*/
|
||
const deleteHandle = (quesInfo) => {
|
||
Modal.confirm({
|
||
class: 'custom-modal',
|
||
title: '确定删除?',
|
||
content: '删除问题后将不能找回',
|
||
icon: () => createVNode(ExclamationCircleFilled),
|
||
okText: '确定',
|
||
cancelText: '取消',
|
||
onOk: () => {
|
||
const status = deleteInterceptor(quesInfo.id);
|
||
if (status) return;
|
||
const quesSaveParam = deleteQues(quesInfo.id);
|
||
saveQuesApi(quesSaveParam, store);
|
||
}
|
||
});
|
||
};
|
||
/**
|
||
* 批量删除确认回调
|
||
*/
|
||
const batchDeleteOk = () => {
|
||
Modal.confirm({
|
||
class: 'custom-modal',
|
||
title: '确定删除?',
|
||
content: '删除问题后将不能找回',
|
||
icon: () => createVNode(ExclamationCircleFilled),
|
||
okText: '确定',
|
||
cancelText: '取消',
|
||
onOk: () => {
|
||
const { canDeleteStatus, delIds } = batchDeleteInterceptor(checkQues);
|
||
if (!canDeleteStatus) {
|
||
return;
|
||
}
|
||
delVisible.value = false;
|
||
const activeQuestion = store.state.common.activeQuestion;
|
||
if (delIds.includes(activeQuestion.id)) {
|
||
context.emit('clearQuesActiveStatus');
|
||
}
|
||
const quesSaveParam = batchDeleteQues(delIds);
|
||
checkQues.value = [];
|
||
isKeepCheckStatus.value = false;
|
||
saveQuesApi(quesSaveParam, store);
|
||
}
|
||
});
|
||
};
|
||
/**
|
||
* 拖拽移动回调
|
||
*/
|
||
const dragEndHandle = (e) => {
|
||
const moveResult = [];
|
||
questionList.value.forEach((ques) => {
|
||
moveResult.push(...ques.child);
|
||
moveResult.push({
|
||
first_title: ques.first_title,
|
||
last_title: ques.last_title,
|
||
page: ques.page,
|
||
total: ques.child.length
|
||
});
|
||
});
|
||
const param = {
|
||
event: e,
|
||
quesList: JSON.parse(
|
||
JSON.stringify(
|
||
moveResult.map((m) => {
|
||
delete m.checked;
|
||
return m;
|
||
})
|
||
)
|
||
)
|
||
};
|
||
const status = dragEndInterceptor(param.event, moveResult);
|
||
if (status) return;
|
||
const quesSaveParam = moveQues(param);
|
||
saveQuesApi(quesSaveParam, store);
|
||
};
|
||
/**
|
||
* 批量移动确认回调
|
||
*/
|
||
const batchMoveOk = () => {
|
||
const { canMoveStatus, moveResultList } = batchMoveInterceptor(checkQues, moveLocation, moveQuesIndex);
|
||
if (!canMoveStatus) {
|
||
return;
|
||
}
|
||
moveVisible.value = false;
|
||
const quesSaveParam = batchMoveQues(moveResultList);
|
||
checkQues.value = [];
|
||
isKeepCheckStatus.value = false;
|
||
saveQuesApi(quesSaveParam, store);
|
||
// 取消全选
|
||
checkboxAll.value = false;
|
||
};
|
||
/**
|
||
* 批量添加题库回调
|
||
*/
|
||
const batchBankOk = () => {
|
||
const { canAddQuesInBankStatus } = batchBankInterceptor(checkQues, bankVisible);
|
||
if (!canAddQuesInBankStatus) {
|
||
return;
|
||
}
|
||
batchBank(bankVisible, saveLoading, checkQues, checkGroupInfo);
|
||
checkQues.value = [];
|
||
checkboxAll.value = false;
|
||
isKeepCheckStatus.value = false;
|
||
initList();
|
||
};
|
||
/**
|
||
* 新增分类
|
||
*/
|
||
const addGroupHandle = () => {
|
||
// console.log(checkGroupInfo.value.child.length==0)
|
||
batchSelectQuesBankRef.value.openInputHandle();
|
||
};
|
||
return {
|
||
allQuesList,
|
||
checkQues,
|
||
checkboxAll,
|
||
searchName,
|
||
skipToHandle,
|
||
toTitle,
|
||
getIcon,
|
||
checkboxHandle,
|
||
checkboxAllHandle,
|
||
openBatchModal,
|
||
|
||
delVisible,
|
||
deleteHandle,
|
||
batchDeleteOk,
|
||
|
||
copyVisible,
|
||
copyNum,
|
||
copyHandle,
|
||
batchCopyOk,
|
||
|
||
moveVisible,
|
||
moveQuesIndex,
|
||
moveLocation,
|
||
dragEndHandle,
|
||
batchMoveOk,
|
||
|
||
bankVisible,
|
||
checkGroupInfo,
|
||
saveLoading,
|
||
batchSelectQuesBankRef,
|
||
batchBankOk,
|
||
addGroupHandle
|
||
};
|
||
}
|
||
/**
|
||
* 处理更新一下store
|
||
*/
|
||
function storeUpdate(store, context) {
|
||
const copyStoreContent = (store) => {
|
||
return JSON.parse(JSON.stringify(store.state.common));
|
||
};
|
||
/** 删除题目 */
|
||
const deleteQues = (id) => {
|
||
const { questionInfo, quesSaveParam, activeQuestion } = copyStoreContent(store);
|
||
if (activeQuestion.id === id) {
|
||
store.commit('common/A_COMMON_SET_ACTIVEQUESTION', JSON.stringify({}));
|
||
context.emit('clearQuesActiveStatus');
|
||
}
|
||
questionInfo.questions = questionInfo.questions.filter((x) => x.id !== id);
|
||
const { page, resultList } = getPageQuesByQues(questionInfo.questions);
|
||
let title = Number(questionInfo.survey.last_title.substring(1));
|
||
questionInfo.questions = resultList;
|
||
questionInfo.survey.pages = page;
|
||
if (questionInfo.questions.filter((ques) => ques.id).length === 0) {
|
||
title = 0;
|
||
} else {
|
||
title--;
|
||
}
|
||
questionInfo.survey.last_title = `Q${title}`;
|
||
store.commit('common/A_COMMON_SET_QUESTIONINFO', JSON.stringify(questionInfo));
|
||
quesSaveParam.newSurvey.pages = page;
|
||
store.commit('common/A_COMMON_SET_QUESSAVEPARAM', JSON.stringify(quesSaveParam));
|
||
return quesSaveParam;
|
||
};
|
||
/** 批量删除题目 */
|
||
const batchDeleteQues = (ids) => {
|
||
const { questionInfo, activeQuestion, quesSaveParam } = copyStoreContent(store);
|
||
if (ids.includes(activeQuestion.id)) {
|
||
store.commit('common/A_COMMON_SET_ACTIVEQUESTION', JSON.stringify({}));
|
||
}
|
||
questionInfo.questions = questionInfo.questions.filter((x) => !ids.includes(x.id));
|
||
const { page, resultList } = getPageQuesByQues(questionInfo.questions);
|
||
let title = Number(questionInfo.survey.last_title.substring(1));
|
||
questionInfo.questions = resultList;
|
||
questionInfo.survey.pages = page;
|
||
if (questionInfo.questions.filter((ques) => ques.id).length === 0) {
|
||
title = 0;
|
||
} else {
|
||
title = title - ids.length;
|
||
}
|
||
questionInfo.survey.last_title = `Q${title}`;
|
||
store.commit('common/A_COMMON_SET_QUESTIONINFO', JSON.stringify(questionInfo));
|
||
quesSaveParam.newSurvey.pages = page;
|
||
store.commit('common/A_COMMON_SET_QUESSAVEPARAM', JSON.stringify(quesSaveParam));
|
||
return quesSaveParam;
|
||
};
|
||
/** 复制题目 */
|
||
const copyQues = (data) => {
|
||
const { questionInfo, quesSaveParam } = copyStoreContent(store);
|
||
const questionList = questionInfo.questions;
|
||
let activeQuestion = data;
|
||
const index = questionList.findIndex((x) => x.id === activeQuestion.id);
|
||
activeQuestion.id = uuidv4();
|
||
activeQuestion.question_index = questionInfo.survey.last_question_index + 1;
|
||
const maxTitle = questionList
|
||
.filter((ques) => ques.id)
|
||
.map((ques) => Number(ques.title.substring(1)))
|
||
.filter((til) => !isNaN(til))
|
||
.sort((a, b) => a - b);
|
||
if (maxTitle.length === 0) {
|
||
let title = Number(questionInfo.survey.last_title.substring(1));
|
||
activeQuestion.title = `Q${++title}`;
|
||
} else {
|
||
activeQuestion.title = `Q${++maxTitle[maxTitle.length - 1]}`;
|
||
}
|
||
questionList.splice(index + 1, 0, activeQuestion);
|
||
const quesData = getPageQuesByQues(questionList);
|
||
const { page, resultList } = quesData;
|
||
|
||
questionInfo.questions = resultList;
|
||
questionInfo.survey.pages = page;
|
||
questionInfo.survey.last_question_index++;
|
||
questionInfo.survey.last_title = activeQuestion.title;
|
||
store.commit('common/A_COMMON_SET_QUESTIONINFO', JSON.stringify(questionInfo));
|
||
store.commit('common/A_COMMON_SET_ACTIVEQUESTION', JSON.stringify(activeQuestion));
|
||
nextTick(() => {
|
||
new Scroll(document.getElementById(activeQuestion.id)).animate();
|
||
});
|
||
quesSaveParam.newQuestion.push(activeQuestion);
|
||
quesSaveParam.newSurvey.pages = page;
|
||
store.commit('common/A_COMMON_SET_QUESSAVEPARAM', JSON.stringify(quesSaveParam));
|
||
return quesSaveParam;
|
||
};
|
||
/** 批量复制题目 */
|
||
const batchCopyQues = (data, targetQues) => {
|
||
const { questionInfo, quesSaveParam } = copyStoreContent(store);
|
||
const questionList = questionInfo.questions;
|
||
const index = questionList.findIndex((x) => x.id === targetQues.id);
|
||
const maxTitle = questionList
|
||
.filter((ques) => ques.id)
|
||
.map((ques) => Number(ques.title.substring(1)))
|
||
.filter((til) => !isNaN(til))
|
||
.sort((a, b) => a - b);
|
||
const newData = data.map((d, index) => {
|
||
d.id = uuidv4();
|
||
d.question_index = questionInfo.survey.last_question_index + index + 1;
|
||
if (maxTitle.length === 0) {
|
||
let title = Number(questionInfo.survey.last_title.substring(1));
|
||
d.title = `Q${title + index + 1}`;
|
||
} else {
|
||
d.title = `Q${maxTitle[maxTitle.length - 1] + index + 1}`;
|
||
}
|
||
return d;
|
||
});
|
||
const activeQuestion = newData[newData.length - 1];
|
||
questionList.splice(index + 1, 0, ...newData);
|
||
const quesData = getPageQuesByQues(questionList);
|
||
const { page, resultList } = quesData;
|
||
|
||
questionInfo.questions = resultList;
|
||
questionInfo.survey.pages = page;
|
||
questionInfo.survey.last_question_index = questionInfo.survey.last_question_index + newData.length;
|
||
questionInfo.survey.last_title = activeQuestion.title;
|
||
store.commit('common/A_COMMON_SET_QUESTIONINFO', JSON.stringify(questionInfo));
|
||
store.commit('common/A_COMMON_SET_ACTIVEQUESTION', JSON.stringify(activeQuestion));
|
||
nextTick(() => {
|
||
new Scroll(document.getElementById(activeQuestion.id)).animate();
|
||
});
|
||
quesSaveParam.newQuestion.push(...newData);
|
||
quesSaveParam.newSurvey.pages = page;
|
||
store.commit('common/A_COMMON_SET_QUESSAVEPARAM', JSON.stringify(quesSaveParam));
|
||
return quesSaveParam;
|
||
};
|
||
/** 移动问题 */
|
||
const moveQues = (param) => {
|
||
const { questionInfo, quesSaveParam } = copyStoreContent(store);
|
||
const moveQues = questionInfo.questions[param.event.oldIndex];
|
||
const { page, resultList } = getPageQuesByQues(param.quesList);
|
||
questionInfo.questions = resultList;
|
||
questionInfo.survey.pages = page;
|
||
store.commit('common/A_COMMON_SET_QUESTIONINFO', JSON.stringify(questionInfo));
|
||
nextTick(() => {
|
||
new Scroll(document.getElementById(moveQues.id)).animate();
|
||
});
|
||
quesSaveParam.newSurvey.pages = page;
|
||
store.commit('common/A_COMMON_SET_QUESSAVEPARAM', JSON.stringify(quesSaveParam));
|
||
return quesSaveParam;
|
||
};
|
||
/** 批量移动 */
|
||
const batchMoveQues = (quesList) => {
|
||
const { questionInfo, quesSaveParam } = copyStoreContent(store);
|
||
const { page, resultList } = getPageQuesByQues(quesList);
|
||
questionInfo.questions = resultList;
|
||
questionInfo.survey.pages = page;
|
||
store.commit('common/A_COMMON_SET_QUESTIONINFO', JSON.stringify(questionInfo));
|
||
quesSaveParam.newSurvey.pages = page;
|
||
store.commit('common/A_COMMON_SET_QUESSAVEPARAM', JSON.stringify(quesSaveParam));
|
||
return quesSaveParam;
|
||
};
|
||
/** 批量添加到题库 */
|
||
const batchBank = async (bankVisible, saveLoading, checkQues, checkGroupInfo) => {
|
||
saveLoading.value = true;
|
||
const param = {
|
||
question_indexs: checkQues.value.map((ques) => ques.question_index),
|
||
group_id: checkGroupInfo.value.id
|
||
};
|
||
try {
|
||
await batchSaveQuesIntoBank(param);
|
||
message.success('保存成功');
|
||
const { data } = await getQuesBankList();
|
||
store.commit('common/A_COMMON_SET_BANK_LIST', data);
|
||
const { questionInfo } = copyStoreContent(store);
|
||
store.commit('common/A_COMMON_SET_QUESTIONINFO', JSON.stringify(questionInfo));
|
||
} catch (error) {
|
||
console.log(error);
|
||
}
|
||
saveLoading.value = false;
|
||
bankVisible.value = false;
|
||
};
|
||
return {
|
||
deleteQues,
|
||
batchDeleteQues,
|
||
copyQues,
|
||
batchCopyQues,
|
||
moveQues,
|
||
batchMoveQues,
|
||
batchBank
|
||
};
|
||
}
|
||
/**
|
||
* 在题目增删改查时拦截处理一些事情
|
||
*/
|
||
function interceptorsHandle(store) {
|
||
/**
|
||
* 删除题目时的特殊处理
|
||
*/
|
||
const deleteInterceptor = (delId) => {
|
||
const { questionInfo } = JSON.parse(JSON.stringify(store.state.common));
|
||
const allAssociate = [];
|
||
let delQues = {};
|
||
questionInfo.questions.forEach((ques) => {
|
||
if (ques.id && ques.id !== delId) {
|
||
allAssociate.push(...ques.associate.map((ass) => ass.question_index));
|
||
}
|
||
if (ques.id === delId) delQues = ques;
|
||
});
|
||
// 如果删除的题目被其他题关联,则不能删除
|
||
if (allAssociate.includes(delQues.question_index)) {
|
||
Modal.warning({
|
||
class: 'custom-modal',
|
||
title: () => '无法操作',
|
||
content: () => '该问题已被其他问题关联,请取消关联后再删除'
|
||
});
|
||
return true;
|
||
}
|
||
// 有循环关联
|
||
const info = store.state.common.questionInfo || {};
|
||
const movedList = JSON.parse(JSON.stringify(info.questions || [])).filter(
|
||
(i) => i.question_index !== delQues.question_index
|
||
);
|
||
const cycleStatus = loopingAvailable({
|
||
cycles: info.cycle_pages || [],
|
||
questions: movedList,
|
||
logics: info.logics || [],
|
||
isPerPage: info.survey?.is_one_page_one_question
|
||
});
|
||
if (!cycleStatus) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
/**
|
||
* 批量删除题目时对于关联的处理
|
||
*/
|
||
const batchDeleteInterceptor = (checkQues) => {
|
||
const delIds = checkQues.value.map((ques) => ques.id);
|
||
const { questionInfo } = JSON.parse(JSON.stringify(store.state.common));
|
||
let canDeleteStatus = true;
|
||
for (let index = 0; index < checkQues.value.length; index++) {
|
||
const delQues = checkQues.value[index];
|
||
const allAssociate = [];
|
||
questionInfo.questions.forEach((ques) => {
|
||
if (ques.id && ques.id !== delQues.id) {
|
||
allAssociate.push(...ques.associate.map((ass) => ass.question_index));
|
||
}
|
||
});
|
||
// 如果删除的题目被其他题关联,则不能删除
|
||
if (allAssociate.includes(delQues.question_index)) {
|
||
canDeleteStatus = false;
|
||
message.warning(`${delQues.title}题目选项已关联其他题目,不能删除,请取消关联后再删除`);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 有循环关联
|
||
const info = JSON.parse(JSON.stringify(store.state.common.questionInfo || {}));
|
||
const movedList = (info.questions || []).filter(
|
||
(i) => !checkQues.value.some((j) => j.question_index === i.question_index)
|
||
);
|
||
if (canDeleteStatus) {
|
||
canDeleteStatus = loopingAvailable({
|
||
cycles: info.cycle_pages || [],
|
||
questions: movedList,
|
||
logics: info.logics || [],
|
||
isPerPage: info.survey?.is_one_page_one_question
|
||
});
|
||
}
|
||
|
||
return {
|
||
canDeleteStatus,
|
||
delIds
|
||
};
|
||
};
|
||
/**
|
||
* 拖拽完成时的特殊处理
|
||
*/
|
||
const dragEndInterceptor = (event, moveResult) => {
|
||
const { questionInfo } = JSON.parse(JSON.stringify(store.state.common));
|
||
const copyQuesList = questionInfo.questions;
|
||
const toIndex = event.newIndex;
|
||
const fromIndex = event.oldIndex;
|
||
const toQuesInfo = copyQuesList[toIndex];
|
||
const fromQuesInfo = copyQuesList[fromIndex];
|
||
let newQues = [];
|
||
if (fromIndex < toIndex) {
|
||
newQues = copyQuesList.slice(fromIndex, toIndex + 1);
|
||
} else {
|
||
newQues = copyQuesList.slice(toIndex, fromIndex + 1);
|
||
}
|
||
|
||
// 1. 关联问题处理
|
||
const associateIndex = [];
|
||
newQues.forEach((ques) => {
|
||
if (ques.associate) {
|
||
associateIndex.push(...ques.associate);
|
||
}
|
||
});
|
||
const status = associateIndex.find(
|
||
(ass) => ass.question_index === (fromIndex < toIndex ? fromQuesInfo : toQuesInfo).question_index
|
||
);
|
||
let statusContent = '';
|
||
if (status) {
|
||
statusContent = '关联题目不能被移至被关联题目之前';
|
||
}
|
||
|
||
// 2. 解决方案处理 解决方案不能操作,不需要处理回调
|
||
|
||
// 3. 循环逻辑处理
|
||
let loopingStatus = true;
|
||
if (!status) {
|
||
loopingStatus = loopingAvailable({
|
||
cycles: questionInfo.cycle_pages || [],
|
||
questions: moveResult,
|
||
logics: questionInfo.logics || [],
|
||
isPerPage: questionInfo.survey?.is_one_page_one_question
|
||
});
|
||
}
|
||
|
||
// 处理结束,不允许排序则弹提示,否则保存
|
||
if (status || !loopingStatus) {
|
||
if (loopingStatus) {
|
||
Modal.warning({
|
||
class: 'custom-modal custom-modal-title-notice show-icon',
|
||
title: () => '无法操作',
|
||
content: () => statusContent || '禁止移动题目'
|
||
});
|
||
}
|
||
store.commit('common/A_COMMON_SET_QUESTIONINFO', JSON.stringify(questionInfo));
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
/**
|
||
* 批量移动时的特殊处理,判断有关联题目不能移动
|
||
*/
|
||
const batchMoveInterceptor = (checkQues, moveLocation, moveQuesIndex) => {
|
||
const { questionInfo } = JSON.parse(JSON.stringify(store.state.common));
|
||
const questions = questionInfo.questions.filter((ques) => ques.id);
|
||
const relatedLineList = [];
|
||
// 获取要移动到的目标位置
|
||
const moveQuesLocation = questions.findIndex((ques) => ques.question_index === moveQuesIndex.value) + 1;
|
||
checkQues.value.forEach((check) => {
|
||
const curCheckInfo = {
|
||
title: check.title,
|
||
questionIndex: check.question_index,
|
||
relateds: [], // 关联线
|
||
beRelateds: [] // 被关联线
|
||
};
|
||
// 递归获取选中题型的关联线
|
||
const getRelated = (quesInfo) => {
|
||
quesInfo.associate.forEach((ass) => {
|
||
const findQues = questions.find((ques) => ques.question_index === ass.question_index);
|
||
const findQuesLocation = questions.findIndex((ques) => ques.question_index === ass.question_index) + 1;
|
||
findQues.location = findQuesLocation;
|
||
curCheckInfo.relateds.push(findQues);
|
||
if (findQues.associate) {
|
||
getRelated(findQues);
|
||
}
|
||
});
|
||
};
|
||
// 递归获取选中题型的被关联线
|
||
const getBeRelated = (quesInfo) => {
|
||
questions.forEach((ques, index) => {
|
||
if (ques.associate && ques.question_index !== quesInfo.question_index) {
|
||
const findAssocicate = ques.associate.find((ass) => ass.question_index === quesInfo.question_index);
|
||
if (findAssocicate) {
|
||
const tempQues = {
|
||
...ques,
|
||
location: index + 1
|
||
};
|
||
curCheckInfo.beRelateds.push(tempQues);
|
||
getBeRelated(tempQues);
|
||
}
|
||
}
|
||
});
|
||
};
|
||
getRelated(check);
|
||
getBeRelated(check);
|
||
relatedLineList.push(curCheckInfo);
|
||
});
|
||
console.log('relatedLineList', relatedLineList);
|
||
// 用来标识是否校验成功,如果已经有不能移动的题目,则改为false,停止循环,减少计算时间
|
||
let canMoveStatus = true;
|
||
// 遍历上一步处理好的数组
|
||
for (let relIndex = 0; relIndex < relatedLineList.length; relIndex++) {
|
||
const rel = relatedLineList[relIndex];
|
||
// 复制一份被关联线数据,防止后面调用splice方法改变原来的数据
|
||
const copyBeRelateds = JSON.parse(JSON.stringify(rel.beRelateds));
|
||
// 获取移动的目标位置,选择之后则加1,之前则不变
|
||
const targetBeRelLocation = moveLocation.value ? moveQuesLocation + 1 : moveQuesLocation;
|
||
// 这个循环是拿到被关联线数组中去掉选中题型的值
|
||
// 比如:q1的被关联数组是[q2,q3,q4,q5,q6],要移动的选中的题目是[q1,q2],则拿到的结果是[q3,q4,q5,q6]
|
||
checkQues.value.forEach((check) => {
|
||
const findBeRelIndex = copyBeRelateds.findIndex((beRel) => beRel.question_index === check.question_index);
|
||
if (findBeRelIndex !== -1) {
|
||
copyBeRelateds.splice(findBeRelIndex, 1);
|
||
}
|
||
});
|
||
copyBeRelateds.sort((a, b) => a.location - b.location);
|
||
// 这个循环是将上一步拿到的未选中题目数组与目标位置进行对比,如果存在小于的位置则不能移动
|
||
// 比如:上一步拿到的结果是[q3,q4,q5,q6],移动的位置是q3之后,他的位置是4,q3的位置是3,小于4则无法移动,如果移动的位置是q3之前,他的位置是3,3不小于3则可以移动
|
||
for (let index = 0; index < copyBeRelateds.length; index++) {
|
||
const beRel = copyBeRelateds[index];
|
||
if (beRel.location < targetBeRelLocation) {
|
||
message.warning(`${rel.title},${beRel.title}之间存在关联,请取消关联后再移动`);
|
||
canMoveStatus = false;
|
||
break;
|
||
}
|
||
}
|
||
if (!canMoveStatus) {
|
||
break;
|
||
}
|
||
// 下一步是处理关联线,和被关联线的处理方式相同,只不过最后的判断不一样
|
||
const copyRelateds = JSON.parse(JSON.stringify(rel.relateds));
|
||
// 获取移动的目标位置,选择之后不变,之前则需要减1
|
||
const targetRelLocation = moveLocation.value ? moveQuesLocation : moveQuesLocation - 1;
|
||
checkQues.value.forEach((check) => {
|
||
const findRelIndex = copyRelateds.findIndex((rel) => rel.question_index === check.question_index);
|
||
if (findRelIndex !== -1) {
|
||
copyRelateds.splice(findRelIndex, 1);
|
||
}
|
||
});
|
||
copyRelateds.sort((a, b) => a.location - b.location);
|
||
for (let index = 0; index < copyRelateds.length; index++) {
|
||
const element = copyRelateds[index];
|
||
if (element.location > targetRelLocation) {
|
||
message.warning(`${rel.title},${element.title}之间存在关联,请取消关联后再移动`);
|
||
canMoveStatus = false;
|
||
break;
|
||
}
|
||
}
|
||
if (!canMoveStatus) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 判断循环逻辑
|
||
const checkedList = JSON.parse(JSON.stringify(checkQues.value || []));
|
||
checkedList.forEach((i) => (i.$isChecked = true));
|
||
const movedList = JSON.parse(JSON.stringify(questionInfo.questions)).filter(
|
||
(i) => !checkedList.some((j) => j.question_index === i.question_index)
|
||
); // 重排序后的列表
|
||
const tempIndex = movedList.findIndex((i) => i.question_index === moveQuesIndex.value);
|
||
const endIndex = tempIndex + (moveLocation.value ? 1 : 0);
|
||
movedList.splice(endIndex, 0, ...checkedList);
|
||
const loopingStatus = loopingAvailable({
|
||
cycles: questionInfo.cycle_pages || [],
|
||
questions: movedList,
|
||
logics: questionInfo.logics || [],
|
||
isPerPage: questionInfo.survey?.is_one_page_one_question
|
||
});
|
||
if (!loopingStatus) {
|
||
canMoveStatus = false;
|
||
}
|
||
|
||
if (canMoveStatus) {
|
||
questionInfo.questions = questionInfo.questions.filter(
|
||
(ques) => !checkQues.value.find((check) => check.question_index === ques.question_index)
|
||
);
|
||
const moveIndex = questionInfo.questions.findIndex((ques) => ques.question_index === moveQuesIndex.value);
|
||
const targetIndex = moveLocation.value ? moveIndex + 1 : moveIndex;
|
||
questionInfo.questions.splice(targetIndex, 0, ...checkQues.value);
|
||
}
|
||
return {
|
||
canMoveStatus,
|
||
moveResultList: JSON.parse(JSON.stringify(questionInfo.questions))
|
||
};
|
||
};
|
||
/**
|
||
* 批量添加到题库时的特殊处理,判断选中题目存在逻辑,关联,引用则不能添加
|
||
*/
|
||
const batchBankInterceptor = (checkQues, bankVisible) => {
|
||
const { questionInfo } = JSON.parse(JSON.stringify(store.state.common));
|
||
const errorQues = [];
|
||
for (let index = 0; index < checkQues.value.length; index++) {
|
||
let canAddQuesInBankStatus = true;
|
||
const quesInfo = checkQues.value[index];
|
||
const matchArr = quesInfo.stem.match(/(\[%cite\(.*?\)%\])/g) || [];
|
||
const logicArr = questionInfo.logics.filter((log) => log.question_index === quesInfo.question_index);
|
||
if (quesInfo.associate.length > 0) {
|
||
canAddQuesInBankStatus = false;
|
||
} else if (matchArr.length > 0) {
|
||
canAddQuesInBankStatus = false;
|
||
} else if (logicArr.length > 0) {
|
||
canAddQuesInBankStatus = false;
|
||
}
|
||
if (!canAddQuesInBankStatus) {
|
||
errorQues.push(quesInfo);
|
||
}
|
||
}
|
||
if (errorQues.length > 0) {
|
||
Modal.confirm({
|
||
class: 'custom-modal',
|
||
title: () => '保存失败',
|
||
icon: () => createVNode(ExclamationCircleFilled),
|
||
okText: '去修改',
|
||
cancelText: '取消',
|
||
content: () =>
|
||
`您保存的题目${errorQues.map((q) => q.title).join(',')}包含逻辑/关联/引用/随机关系,请解除关系后再试。`,
|
||
onOk: () => {
|
||
bankVisible.value = false;
|
||
nextTick(() => {
|
||
store.commit('common/A_COMMON_SET_ACTIVEQUESTION', JSON.stringify(errorQues[0]));
|
||
new Scroll(document.getElementById(errorQues[0].id)).animate();
|
||
});
|
||
}
|
||
});
|
||
}
|
||
return {
|
||
canAddQuesInBankStatus: errorQues.length === 0
|
||
};
|
||
};
|
||
return {
|
||
deleteInterceptor,
|
||
batchDeleteInterceptor,
|
||
dragEndInterceptor,
|
||
batchMoveInterceptor,
|
||
batchBankInterceptor
|
||
};
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.catalog {
|
||
padding-bottom: 24px;
|
||
&-main {
|
||
max-height: calc(100vh - 313px);
|
||
overflow-y: auto;
|
||
}
|
||
&-page {
|
||
padding-bottom: 24px;
|
||
&-title {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
padding-left: 12px;
|
||
}
|
||
}
|
||
&-line {
|
||
height: 1px;
|
||
background: #f5f5f5;
|
||
margin: 0 12px 24px 0;
|
||
}
|
||
&-checkall {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 24px;
|
||
font-size: 13px;
|
||
font-weight: 400;
|
||
color: #434343;
|
||
cursor: pointer;
|
||
padding-left: 12px;
|
||
transition: all 0.3s;
|
||
}
|
||
&-text {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 24px;
|
||
min-height: 37px;
|
||
font-size: 13px;
|
||
font-weight: 400;
|
||
color: #434343;
|
||
cursor: pointer;
|
||
padding-left: 12px;
|
||
&-icon {
|
||
margin-right: 10px;
|
||
}
|
||
&:hover {
|
||
background: #f5f5f5;
|
||
.catalog-text-hover {
|
||
width: 90px;
|
||
}
|
||
}
|
||
&-content {
|
||
display: flex;
|
||
align-items: center;
|
||
&::v-deep p {
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-align: center;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
}
|
||
&-hover {
|
||
position: absolute;
|
||
right: 0;
|
||
background: #f5f5f5;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-around;
|
||
width: 0;
|
||
height: 100%;
|
||
transition: width 0.3s;
|
||
overflow: hidden;
|
||
.iconfont {
|
||
cursor: pointer;
|
||
color: #dadada;
|
||
&:hover {
|
||
color: $yili-default-color;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
&-more {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
padding-top: 16px;
|
||
height: 0;
|
||
overflow: hidden;
|
||
color: #dadada;
|
||
transition: all 0.5s;
|
||
.iconfont {
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
&:hover {
|
||
color: $yili-default-color;
|
||
}
|
||
}
|
||
}
|
||
&-modal-content {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 11px;
|
||
font-weight: 400;
|
||
color: #434343;
|
||
text-align: left;
|
||
&-select {
|
||
margin-left: 10px;
|
||
margin-right: 10px;
|
||
flex: 1;
|
||
&-html {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
max-width: 200px;
|
||
}
|
||
}
|
||
&-item {
|
||
&:not(:last-child) {
|
||
margin-bottom: 24px;
|
||
}
|
||
}
|
||
}
|
||
.loading {
|
||
display: flex;
|
||
justify-content: center;
|
||
padding-top: 10px;
|
||
font-size: 13px;
|
||
font-family: PingFangSC-Regular, PingFang SC;
|
||
font-weight: 400;
|
||
}
|
||
.flex-align {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.custom-checkbox {
|
||
display: inline-block;
|
||
transition: all 0.3s;
|
||
height: 22px;
|
||
overflow: hidden;
|
||
}
|
||
}
|
||
.modal-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
font-size: 11px;
|
||
font-weight: 400;
|
||
color: #434343;
|
||
text-align: left;
|
||
&-flex {
|
||
display: flex;
|
||
align-items: center;
|
||
&-select {
|
||
margin-left: 10px;
|
||
margin-right: 10px;
|
||
&-html {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
}
|
||
}
|
||
&-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
color: $yili-default-color;
|
||
cursor: pointer;
|
||
&-disable {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
.iconfont {
|
||
height: 27px;
|
||
}
|
||
}
|
||
}
|
||
</style>
|