fix:投放

This commit is contained in:
LHY\18810
2025-03-15 19:25:11 +08:00
parent 4e75b40af9
commit 3f27ad1847
4 changed files with 907 additions and 25 deletions

424
src/assets/js/logic.js Normal file
View File

@@ -0,0 +1,424 @@
/**
* 问卷设计中涉及到的一些 【逻辑】 中公共方法
*/
import config from '@/config';
import { Modal } from 'ant-design-vue';
import { advancedQuesTypeList } from './quesInfoList';
const advancedTypes = advancedQuesTypeList.map((i) => i.type); // 高级题型的 question_type
const performance = {
start(label) {
if (config.currentMode === 'dev') {
console.time(`${label} Performance`);
}
},
end(label) {
if (config.currentMode === 'dev') {
console.timeEnd(`${label} Performance`);
}
},
log(...rest) {
if (config.currentMode === 'dev') {
console.log(...rest);
}
}
};
function isNullish(value) {
return value === undefined || value === null;
}
/**
* 修改(添加、移动、删除)问卷 题目、分页,编辑(添加、删除)逻辑 时,判断循环逻辑是否满足
* 的异步方法,感觉题多了这个方法执行会很慢。。。
* @param rest
* @return {Promise<boolean>}
*/
export async function loopingAvailableAsync(...rest) {
return Promise.resolve(loopingAvailable(...rest));
}
/**
* 修改(添加、移动、删除)问卷 题目、分页,编辑(添加、删除)逻辑 时,判断循环逻辑是否满足
* @param cycles 循环逻辑列表
* @param questions 被操作后的问题列表
* @param logics 逻辑列表
* @param isPerPage 是否是每页一道题
* @param reason 提示信息
* @return {boolean} 是否满足循环逻辑
*/
export function loopingAvailable({ cycles, questions, logics, isPerPage, reason } = {}) {
const performanceName = 'CheckLoopingAvailable';
performance.start(performanceName);
let result = true;
let prompt = reason || '';
if (!cycles.length) {
// 没有配置循环逻辑
performance.end(performanceName);
return result;
}
const quizList = generateQuestionPages({ questions, logics, isPerPage });
const cycleList = generateCyclePages(cycles, quizList);
const logicList = generateLogicPages(logics, quizList);
const lastPage = quizList.reduce((prev, curr) => Math.max(prev, curr.$page || 0), 0); // 最后一页的页码
// console.log('questions', questions, quizList);
// console.log('cycles', cycles, cycleList);
// console.log('logics', logics, logicList);
// console.log('last page', lastPage);
for (let i = 0; i < cycleList.length; i += 1) {
const { index, page, startPage, endPage } = cycleList[i] || {};
// 循环关联问题被删除时,才会出现这种情况
if (!quizList.find((j) => j.question_index === index)) {
result = false;
prompt = '问题被循环关联,不能删除';
break;
}
if ((startPage && startPage <= page) || (endPage && endPage <= page)) {
result = false;
prompt = '问题被循环关联,不能位于循环题组后';
break;
}
// 循环关联问题位于自己或其它循环的循环题组分页中间
if (page && cycleList.some((j) => j.startPage < page && page < j.endPage)) {
result = false;
prompt = '问题被循环关联,不能位于循环题组中';
break;
}
// 循环的循环题组,修改问题或分页的时候应该不会出现这种情况
if (startPage && endPage && startPage > endPage) {
result = false;
prompt = '循环题组的起始分页应该小于结束分页';
break;
}
// 删除分页,或者删除自带分页的题,导致分页变化,校验分页是否包含了所有的循环题组分页
if (endPage > lastPage) {
result = false;
prompt = '请勿在循环题组中编辑分页,请调整循环题组后再试';
break;
}
// 循环题组分页和跳转逻辑有冲突
if (
logicList.some((j) => {
// 跳转到这几个的时候,循环不校验,只校验带题的
// 正常完成、提前终止、配额超限
if ([-1, -2, -3].includes(Math.min(...j))) {
return false;
}
return isCross(j, [startPage, endPage]);
})
) {
result = false;
prompt = '循环题组分页区间与跳转逻辑冲突,请修改后再试';
break;
}
}
if (!result && (reason || prompt)) {
Modal.warning({
class: 'custom-modal custom-modal-title-notice hide-ant-icon',
title: '无法操作',
width: '450px',
content: reason || prompt
});
}
performance.end(performanceName);
return result;
}
/**
* 给问题列表中的问题添加,问题所在的页码字段($page
* 说明:
* 1. 需要过滤掉没有题的空白页
* 2. 高级题型的题前后需要加分页
* 3. 添加了逻辑的基础题型前后需要加分页
* @param questions
* @param logics
* @param isPerPage
* @param addon 自定义额外操作,是一个方法
* @return {array} 题目列表,包括题和分页
*/
export function generateQuestionPages({ questions, logics, isPerPage, addon }) {
if (!questions?.length) {
return questions || [];
}
let page = 1;
let pageObjectCount = 0; // 计数,在 questions 有几个分页的对象
return questions.map((item, index, arr) => {
const asBlock = [...advancedTypes, 23]; // 需要自动加分页的题型:高级题型及知情同意书
const isPrevAdv = asBlock.includes(arr[index - 1]?.question_type); // 前一道题是高级题型
const isAdv = asBlock.includes(item?.question_type); // 当前题是高级题型
// const isPrevAdv = advancedTypes.includes(arr[index - 1]?.question_type); // 前一道题是高级题型
// const isAdv = advancedTypes.includes(item?.question_type); // 当前题是高级题型
// 前一道题配置了跳转逻辑
const isPrevLogic = !!logics.find((logic) => logic.question_index === arr[index - 1]?.question_index);
// 当前题配置了跳转逻辑
const isLogic = !!logics.find((logic) => logic.question_index === item.question_index);
const isSurroundedByPage = isAdv || isLogic; // 需要在该题前后添加分页
const isPrevPage = !!arr[index - 1]?.page; // 前一道题是一个分页对象
const prevIsQuestion = !arr[index - 1]?.page; // 前一道题是一道题而不是一个分页对象
if (!index) {
page = 1;
pageObjectCount = 0;
}
if (item.page) {
pageObjectCount += 1;
if (index && prevIsQuestion && !isPrevLogic && !isPrevAdv) {
// 过滤掉没有题的空白页
page += 1;
}
} else {
if (index && !isPrevPage && !isPrevAdv && !isPrevLogic && isSurroundedByPage) {
page += 1;
}
item.$page = isPerPage ? index + 1 - pageObjectCount : page;
if (isSurroundedByPage) {
page += 1;
}
}
if (addon) {
addon(item);
}
return item;
});
}
/**
* 格式化循环列表,便于后续使用
* @param cycles
* @param questions
* @return {array} 格式化后的循环列表
*/
export function generateCyclePages(cycles, questions) {
if (!cycles?.length) {
return cycles || [];
}
return cycles.map((cycle) => ({
index: cycle.question_index,
page: getPageByQuestionIndex(cycle.question_index, questions),
startPage: cycle.first_page,
endPage: cycle.last_page
}));
}
/**
* 逻辑关联的页码,便于后续使用
* @param logics {*[]}
* @param questions {*[]}
* @param fillRangeItem {boolean} 用数字填充满 range[index] 数组
* @return {number[][]} 逻辑关联的页码
*/
export function generateLogicPages(logics, questions, fillRangeItem) {
if (!logics?.length) {
return logics || [];
}
const pages = [];
logics.forEach((logic) => {
if (![0, 2].includes(logic.skip_type)) {
return;
}
let page = [
getPageByQuestionIndex(logic.logic?.[0]?.question_index, questions),
getPageByQuestionIndex(logic.question_index, questions),
getPageByQuestionIndex(logic.skip_question_index, questions) || logic.skip_question_index
];
page = page.filter((i) => !!i);
if (fillRangeItem) {
const min = Math.min(...page.filter((i) => i > 0));
const max = Math.max(...page);
page = generateRange(min, max);
}
pages.push(page);
});
return pages;
}
/**
* 通过问题的 question_index 找到问题在第几页
* @param questionIndex
* @param questions
* @return {number|*|undefined}
*/
export function getPageByQuestionIndex(questionIndex, questions) {
return questions.find((i) => i.question_index === questionIndex)?.$page || undefined;
}
/**
* 生成一个从 start 开始到 end 结束的数组
* @param start {number}
* @param end {number}
* @return {number[]}
*/
export function generateRange(start, end) {
if (isNullish(start) || isNullish(end)) {
return [];
}
if (isNaN(Number(start)) || isNaN(Number(end))) {
return [];
}
let i = start;
let range = [];
while (i <= end) {
range.push(i);
i += 1;
}
return range;
}
/**
* 判断逻辑与循环分组,是否不合理
* @param range1 {array} 跳转逻辑的分页数组
* @param range2 {array} 循环分组的分页数组
* @return {boolean} true 不合理, false 合理
*/
export function isCross(range1, range2) {
if (!range1 || !range2) {
return false;
}
const parsedRange1 = range1.slice(1);
const judge = range1[0];
const start1 = Math.min(...parsedRange1);
const end1 = Math.max(...parsedRange1);
const start2 = range2[0];
const end2 = range2[1];
const isPlainSequence = parsedRange1[0] === start1; // 跳转逻辑的方向true 为从前向后跳转
if (isNullish(start1) || isNullish(end1) || end1 < 0 || (isNullish(start2) && isNullish(end2))) {
return false;
}
// [judge, start1, end1]; // isPlainSequence
// [start1, judge, end1]; // isPlainSequence || !isPlainSequence
//
// [start2, end2];
// 逻辑在循环之前
const isLeft = isNullish(start2) ? end1 < end2 : end1 < start2;
// 逻辑在循环之后
const isRight = isNullish(end2) ? start2 < start1 && start2 < judge : end2 < start1 && end2 < judge;
// 不相交也不包含
const isSibling = isLeft || isRight;
// 逻辑包含循环
const contain = (isPlainSequence
&& (((isNullish(start2) || isSequence(judge, start2, start1))
&& (isNullish(end2) || isSequence(judge, end2, start1)))
|| ((isNullish(start2) || isSequence(start1, start2, end1))
&& (isNullish(end2) || isSequence(start1, end2, end1)))))
|| (!isPlainSequence
&& (judge < start1
? ((isNullish(start2) || isSequence(judge, start2, start1))
&& (isNullish(end2) || isSequence(judge, end2, start1)))
|| ((isNullish(start2) || isSequence(start1, start2, end1))
&& (isNullish(end2) || isSequence(start1, end2, end1)))
: ((isNullish(start2) || isSequence(start1, start2, judge))
&& (isNullish(end2) || isSequence(start1, end2, judge)))
|| ((isNullish(start2) || isSequence(judge, start2, end1))
&& (isNullish(end2) || isSequence(judge, end2, end1)))));
// 循环存在封闭区间,并且循环包含逻辑
const contained = !isNullish(start2)
&& !isNullish(end2)
// [judge, start1, end1];
&& ((isPlainSequence && start2 <= judge && end1 <= end2)
// [judge, start1, end1];
// [start1, judge, end1];
|| (!isPlainSequence && start2 <= start1 && start2 <= judge && end1 <= end2));
// 循环不存在封闭区间
const unCircled = (!isNullish(start2)
&& isNullish(end2)
&& ((isPlainSequence && start2 === judge) || (!isPlainSequence && judge < start1)
? start2 === judge
: start2 === start1))
|| (isNullish(start2) && !isNullish(end2) && end2 === end1);
return !(isSibling || contain || contained || unCircled);
}
function isSequence(s1, s2, s3, equal) {
return equal ? s1 <= s2 && s2 <= s3 : s1 < s2 && s2 < s3;
}
/**
* 调用保存问题接口前,检查是否有问题受到循环影响需要重新保存,如果有则重新保存一下这道题,没有则不需要特殊处理
* bugfix for : 有循环的问卷发布后,再次编辑问卷,将循环题组内的问题移除循环题组,
* 再次发布,后端不处理被移除的问题 title导致该题的 title 仍保持上次发布时的值
* 错误格式一般为B3.1 正确格式一般为B3
* 导致作答出现错误,例如:引用找不到题
* 从 store 里查出修改前和修改后的问题、分页、循环;比较修改前后问题是否被移除了某个循环题组;修改 quesSaveParam.newQuestion
* @param quesSaveParam 将要保存的数据会被此方法修改的字段quesSaveParam.newQuestion
* @param store
*/
export function updateNewQuestionsByLoopingEffect(quesSaveParam, store) {
const { questionInfoBeforeModified = {}, questionInfo = {} } = JSON.parse(JSON.stringify(store.state.common)) || {};
const oldPages = questionInfoBeforeModified.survey.pages;
const newQuestions = questionInfo.questions;
const newPages = questionInfo.survey.is_one_page_one_question
? questionInfo.questions.filter((i) => i.question_index).map((i, idx) => [i.question_index])
: questionInfo.survey.pages;
const cycles = questionInfo.cycle_pages || [];
if (!cycles.length) {
return;
}
const moveOutOfCycleQuestionIndex = [];
const cyclePages = cycles.map((i) => [i.first_page, i.last_page]).filter((i) => i[0] && i[1]);
cyclePages.forEach((i) => {
const start = i[0] - 1;
const end = i[1] - 1;
for (let j = start; j <= end; j += 1) {
if (oldPages[j]?.join(',') !== newPages[j]?.join(',')) {
oldPages[j]?.forEach((k) => {
if (!newPages?.[j]?.includes(k)) {
moveOutOfCycleQuestionIndex.push(k);
}
});
}
}
});
const movedOutOfCycleQuestions = newQuestions.filter((i) => moveOutOfCycleQuestionIndex.includes(i.question_index));
if (movedOutOfCycleQuestions.length) {
if (!quesSaveParam.newQuestion) {
quesSaveParam.newQuestion = [];
}
quesSaveParam.newQuestion.push(...movedOutOfCycleQuestions);
}
}

View File

@@ -0,0 +1,32 @@
// 问题类型
export const QUESTION_TYPE = {
// 单选
CHOICE: 1,
// 多选
CHOICE_MULT: 2,
// 输入
INPUT_MULT: 4,
// 图文说明
IMG_TEXT_EXPLAIN: 6,
// 热区-开关
HOTAREA: 25,
// 热区-喜欢/不喜欢
HOTAREA_2: 26,
// 矩阵-填空
MATRIX_INPUT: 8,
// 矩阵-单选
MATRIX_RADIO: 9,
contains(item) {
return Object.keys(this).some((key) => this[key] == item);
}
};
export default {};

424
src/layouts/logic.js Normal file
View File

@@ -0,0 +1,424 @@
/**
* 问卷设计中涉及到的一些 【逻辑】 中公共方法
*/
import config from '@/config';
import { Modal } from 'ant-design-vue';
import { advancedQuesTypeList } from './quesInfoList';
const advancedTypes = advancedQuesTypeList.map((i) => i.type); // 高级题型的 question_type
const performance = {
start(label) {
if (config.currentMode === 'dev') {
console.time(`${label} Performance`);
}
},
end(label) {
if (config.currentMode === 'dev') {
console.timeEnd(`${label} Performance`);
}
},
log(...rest) {
if (config.currentMode === 'dev') {
console.log(...rest);
}
}
};
function isNullish(value) {
return value === undefined || value === null;
}
/**
* 修改(添加、移动、删除)问卷 题目、分页,编辑(添加、删除)逻辑 时,判断循环逻辑是否满足
* 的异步方法,感觉题多了这个方法执行会很慢。。。
* @param rest
* @return {Promise<boolean>}
*/
export async function loopingAvailableAsync(...rest) {
return Promise.resolve(loopingAvailable(...rest));
}
/**
* 修改(添加、移动、删除)问卷 题目、分页,编辑(添加、删除)逻辑 时,判断循环逻辑是否满足
* @param cycles 循环逻辑列表
* @param questions 被操作后的问题列表
* @param logics 逻辑列表
* @param isPerPage 是否是每页一道题
* @param reason 提示信息
* @return {boolean} 是否满足循环逻辑
*/
export function loopingAvailable({ cycles, questions, logics, isPerPage, reason } = {}) {
const performanceName = 'CheckLoopingAvailable';
performance.start(performanceName);
let result = true;
let prompt = reason || '';
if (!cycles.length) {
// 没有配置循环逻辑
performance.end(performanceName);
return result;
}
const quizList = generateQuestionPages({ questions, logics, isPerPage });
const cycleList = generateCyclePages(cycles, quizList);
const logicList = generateLogicPages(logics, quizList);
const lastPage = quizList.reduce((prev, curr) => Math.max(prev, curr.$page || 0), 0); // 最后一页的页码
// console.log('questions', questions, quizList);
// console.log('cycles', cycles, cycleList);
// console.log('logics', logics, logicList);
// console.log('last page', lastPage);
for (let i = 0; i < cycleList.length; i += 1) {
const { index, page, startPage, endPage } = cycleList[i] || {};
// 循环关联问题被删除时,才会出现这种情况
if (!quizList.find((j) => j.question_index === index)) {
result = false;
prompt = '问题被循环关联,不能删除';
break;
}
if ((startPage && startPage <= page) || (endPage && endPage <= page)) {
result = false;
prompt = '问题被循环关联,不能位于循环题组后';
break;
}
// 循环关联问题位于自己或其它循环的循环题组分页中间
if (page && cycleList.some((j) => j.startPage < page && page < j.endPage)) {
result = false;
prompt = '问题被循环关联,不能位于循环题组中';
break;
}
// 循环的循环题组,修改问题或分页的时候应该不会出现这种情况
if (startPage && endPage && startPage > endPage) {
result = false;
prompt = '循环题组的起始分页应该小于结束分页';
break;
}
// 删除分页,或者删除自带分页的题,导致分页变化,校验分页是否包含了所有的循环题组分页
if (endPage > lastPage) {
result = false;
prompt = '请勿在循环题组中编辑分页,请调整循环题组后再试';
break;
}
// 循环题组分页和跳转逻辑有冲突
if (
logicList.some((j) => {
// 跳转到这几个的时候,循环不校验,只校验带题的
// 正常完成、提前终止、配额超限
if ([-1, -2, -3].includes(Math.min(...j))) {
return false;
}
return isCross(j, [startPage, endPage]);
})
) {
result = false;
prompt = '循环题组分页区间与跳转逻辑冲突,请修改后再试';
break;
}
}
if (!result && (reason || prompt)) {
Modal.warning({
class: 'custom-modal custom-modal-title-notice hide-ant-icon',
title: '无法操作',
width: '450px',
content: reason || prompt
});
}
performance.end(performanceName);
return result;
}
/**
* 给问题列表中的问题添加,问题所在的页码字段($page
* 说明:
* 1. 需要过滤掉没有题的空白页
* 2. 高级题型的题前后需要加分页
* 3. 添加了逻辑的基础题型前后需要加分页
* @param questions
* @param logics
* @param isPerPage
* @param addon 自定义额外操作,是一个方法
* @return {array} 题目列表,包括题和分页
*/
export function generateQuestionPages({ questions, logics, isPerPage, addon }) {
if (!questions?.length) {
return questions || [];
}
let page = 1;
let pageObjectCount = 0; // 计数,在 questions 有几个分页的对象
return questions.map((item, index, arr) => {
const asBlock = [...advancedTypes, 23]; // 需要自动加分页的题型:高级题型及知情同意书
const isPrevAdv = asBlock.includes(arr[index - 1]?.question_type); // 前一道题是高级题型
const isAdv = asBlock.includes(item?.question_type); // 当前题是高级题型
// const isPrevAdv = advancedTypes.includes(arr[index - 1]?.question_type); // 前一道题是高级题型
// const isAdv = advancedTypes.includes(item?.question_type); // 当前题是高级题型
// 前一道题配置了跳转逻辑
const isPrevLogic = !!logics.find((logic) => logic.question_index === arr[index - 1]?.question_index);
// 当前题配置了跳转逻辑
const isLogic = !!logics.find((logic) => logic.question_index === item.question_index);
const isSurroundedByPage = isAdv || isLogic; // 需要在该题前后添加分页
const isPrevPage = !!arr[index - 1]?.page; // 前一道题是一个分页对象
const prevIsQuestion = !arr[index - 1]?.page; // 前一道题是一道题而不是一个分页对象
if (!index) {
page = 1;
pageObjectCount = 0;
}
if (item.page) {
pageObjectCount += 1;
if (index && prevIsQuestion && !isPrevLogic && !isPrevAdv) {
// 过滤掉没有题的空白页
page += 1;
}
} else {
if (index && !isPrevPage && !isPrevAdv && !isPrevLogic && isSurroundedByPage) {
page += 1;
}
item.$page = isPerPage ? index + 1 - pageObjectCount : page;
if (isSurroundedByPage) {
page += 1;
}
}
if (addon) {
addon(item);
}
return item;
});
}
/**
* 格式化循环列表,便于后续使用
* @param cycles
* @param questions
* @return {array} 格式化后的循环列表
*/
export function generateCyclePages(cycles, questions) {
if (!cycles?.length) {
return cycles || [];
}
return cycles.map((cycle) => ({
index: cycle.question_index,
page: getPageByQuestionIndex(cycle.question_index, questions),
startPage: cycle.first_page,
endPage: cycle.last_page
}));
}
/**
* 逻辑关联的页码,便于后续使用
* @param logics {*[]}
* @param questions {*[]}
* @param fillRangeItem {boolean} 用数字填充满 range[index] 数组
* @return {number[][]} 逻辑关联的页码
*/
export function generateLogicPages(logics, questions, fillRangeItem) {
if (!logics?.length) {
return logics || [];
}
const pages = [];
logics.forEach((logic) => {
if (![0, 2].includes(logic.skip_type)) {
return;
}
let page = [
getPageByQuestionIndex(logic.logic?.[0]?.question_index, questions),
getPageByQuestionIndex(logic.question_index, questions),
getPageByQuestionIndex(logic.skip_question_index, questions) || logic.skip_question_index
];
page = page.filter((i) => !!i);
if (fillRangeItem) {
const min = Math.min(...page.filter((i) => i > 0));
const max = Math.max(...page);
page = generateRange(min, max);
}
pages.push(page);
});
return pages;
}
/**
* 通过问题的 question_index 找到问题在第几页
* @param questionIndex
* @param questions
* @return {number|*|undefined}
*/
export function getPageByQuestionIndex(questionIndex, questions) {
return questions.find((i) => i.question_index === questionIndex)?.$page || undefined;
}
/**
* 生成一个从 start 开始到 end 结束的数组
* @param start {number}
* @param end {number}
* @return {number[]}
*/
export function generateRange(start, end) {
if (isNullish(start) || isNullish(end)) {
return [];
}
if (isNaN(Number(start)) || isNaN(Number(end))) {
return [];
}
let i = start;
let range = [];
while (i <= end) {
range.push(i);
i += 1;
}
return range;
}
/**
* 判断逻辑与循环分组,是否不合理
* @param range1 {array} 跳转逻辑的分页数组
* @param range2 {array} 循环分组的分页数组
* @return {boolean} true 不合理, false 合理
*/
export function isCross(range1, range2) {
if (!range1 || !range2) {
return false;
}
const parsedRange1 = range1.slice(1);
const judge = range1[0];
const start1 = Math.min(...parsedRange1);
const end1 = Math.max(...parsedRange1);
const start2 = range2[0];
const end2 = range2[1];
const isPlainSequence = parsedRange1[0] === start1; // 跳转逻辑的方向true 为从前向后跳转
if (isNullish(start1) || isNullish(end1) || end1 < 0 || (isNullish(start2) && isNullish(end2))) {
return false;
}
// [judge, start1, end1]; // isPlainSequence
// [start1, judge, end1]; // isPlainSequence || !isPlainSequence
//
// [start2, end2];
// 逻辑在循环之前
const isLeft = isNullish(start2) ? end1 < end2 : end1 < start2;
// 逻辑在循环之后
const isRight = isNullish(end2) ? start2 < start1 && start2 < judge : end2 < start1 && end2 < judge;
// 不相交也不包含
const isSibling = isLeft || isRight;
// 逻辑包含循环
const contain = (isPlainSequence
&& (((isNullish(start2) || isSequence(judge, start2, start1))
&& (isNullish(end2) || isSequence(judge, end2, start1)))
|| ((isNullish(start2) || isSequence(start1, start2, end1))
&& (isNullish(end2) || isSequence(start1, end2, end1)))))
|| (!isPlainSequence
&& (judge < start1
? ((isNullish(start2) || isSequence(judge, start2, start1))
&& (isNullish(end2) || isSequence(judge, end2, start1)))
|| ((isNullish(start2) || isSequence(start1, start2, end1))
&& (isNullish(end2) || isSequence(start1, end2, end1)))
: ((isNullish(start2) || isSequence(start1, start2, judge))
&& (isNullish(end2) || isSequence(start1, end2, judge)))
|| ((isNullish(start2) || isSequence(judge, start2, end1))
&& (isNullish(end2) || isSequence(judge, end2, end1)))));
// 循环存在封闭区间,并且循环包含逻辑
const contained = !isNullish(start2)
&& !isNullish(end2)
// [judge, start1, end1];
&& ((isPlainSequence && start2 <= judge && end1 <= end2)
// [judge, start1, end1];
// [start1, judge, end1];
|| (!isPlainSequence && start2 <= start1 && start2 <= judge && end1 <= end2));
// 循环不存在封闭区间
const unCircled = (!isNullish(start2)
&& isNullish(end2)
&& ((isPlainSequence && start2 === judge) || (!isPlainSequence && judge < start1)
? start2 === judge
: start2 === start1))
|| (isNullish(start2) && !isNullish(end2) && end2 === end1);
return !(isSibling || contain || contained || unCircled);
}
function isSequence(s1, s2, s3, equal) {
return equal ? s1 <= s2 && s2 <= s3 : s1 < s2 && s2 < s3;
}
/**
* 调用保存问题接口前,检查是否有问题受到循环影响需要重新保存,如果有则重新保存一下这道题,没有则不需要特殊处理
* bugfix for : 有循环的问卷发布后,再次编辑问卷,将循环题组内的问题移除循环题组,
* 再次发布,后端不处理被移除的问题 title导致该题的 title 仍保持上次发布时的值
* 错误格式一般为B3.1 正确格式一般为B3
* 导致作答出现错误,例如:引用找不到题
* 从 store 里查出修改前和修改后的问题、分页、循环;比较修改前后问题是否被移除了某个循环题组;修改 quesSaveParam.newQuestion
* @param quesSaveParam 将要保存的数据会被此方法修改的字段quesSaveParam.newQuestion
* @param store
*/
export function updateNewQuestionsByLoopingEffect(quesSaveParam, store) {
const { questionInfoBeforeModified = {}, questionInfo = {} } = JSON.parse(JSON.stringify(store.state.common)) || {};
const oldPages = questionInfoBeforeModified.survey.pages;
const newQuestions = questionInfo.questions;
const newPages = questionInfo.survey.is_one_page_one_question
? questionInfo.questions.filter((i) => i.question_index).map((i, idx) => [i.question_index])
: questionInfo.survey.pages;
const cycles = questionInfo.cycle_pages || [];
if (!cycles.length) {
return;
}
const moveOutOfCycleQuestionIndex = [];
const cyclePages = cycles.map((i) => [i.first_page, i.last_page]).filter((i) => i[0] && i[1]);
cyclePages.forEach((i) => {
const start = i[0] - 1;
const end = i[1] - 1;
for (let j = start; j <= end; j += 1) {
if (oldPages[j]?.join(',') !== newPages[j]?.join(',')) {
oldPages[j]?.forEach((k) => {
if (!newPages?.[j]?.includes(k)) {
moveOutOfCycleQuestionIndex.push(k);
}
});
}
}
});
const movedOutOfCycleQuestions = newQuestions.filter((i) => moveOutOfCycleQuestionIndex.includes(i.question_index));
if (movedOutOfCycleQuestions.length) {
if (!quesSaveParam.newQuestion) {
quesSaveParam.newQuestion = [];
}
quesSaveParam.newQuestion.push(...movedOutOfCycleQuestions);
}
}

View File

@@ -1,7 +1,9 @@
import { showConfirmDialog } from 'vant'; import { showConfirmDialog } from 'vant';
import { getQuestionList, getCheckSurvey } from '@/api/survey'; import { getQuestionList, getCheckSurvey } from '@/api/survey';
import appBridge from '@/assets/js/appBridge'; import appBridge from '@/assets/js/appBridge';
import { QUESTION_TYPE } from '@/layouts/config3d.constant.js';
import { loopingAvailable } from '@/layouts/logic.js';
import { getDomText } from '@/utils/utils';
// /** // /**
// * 统一的弹窗 // * 统一的弹窗
// * @param options // * @param options
@@ -46,8 +48,8 @@ const canPlanetPublishPSM = function(data) {
let message = ''; let message = '';
let title = '题目设置未完成'; let title = '题目设置未完成';
const incompleteQuestionList = []; const incompleteQuestionList = [];
data.questions data.questions &&
&& data.questions.forEach((s) => { data.questions.forEach((s) => {
if (s.question_type === 101 && s.config.price_gradient.length <= 0) { if (s.question_type === 101 && s.config.price_gradient.length <= 0) {
isFb = false; isFb = false;
message = 'psm题目未完成设置请设置价格区间后投放'; message = 'psm题目未完成设置请设置价格区间后投放';
@@ -79,8 +81,8 @@ const canPlanetPublishMxdAndHotArea = function(data) {
const incompleteQuestionList = []; const incompleteQuestionList = [];
let type = 0; let type = 0;
let title = '题目设置未完成'; let title = '题目设置未完成';
data.questions data.questions &&
&& data.questions.forEach((s) => { data.questions.forEach((s) => {
if (s.question_type === 105 && s.config.design_version <= 0) { if (s.question_type === 105 && s.config.design_version <= 0) {
isFb = false; isFb = false;
message = 'maxdiff题目未完成设置请生成设计后投放'; message = 'maxdiff题目未完成设置请生成设计后投放';
@@ -125,8 +127,8 @@ const canPlanetPublish3D = function(data) {
let message = ''; let message = '';
const qSteams = []; const qSteams = [];
let title = ''; let title = '';
data.questions data.questions &&
&& data.questions.forEach((s) => { data.questions.forEach((s) => {
if (QUESTION_TYPE.contains(s.question_type)) { if (QUESTION_TYPE.contains(s.question_type)) {
try { try {
if (s.config.is_three_dimensions && !s.config.scene) { if (s.config.is_three_dimensions && !s.config.scene) {
@@ -156,15 +158,15 @@ const canPlanetPublish3D = function(data) {
let message = ''; let message = '';
const qSteams = []; const qSteams = [];
let title = ''; let title = '';
data.questions data.questions &&
&& data.questions.forEach((s) => { data.questions.forEach((s) => {
if (QUESTION_TYPE.contains(s.question_type)) { if (QUESTION_TYPE.contains(s.question_type)) {
try { try {
if (s.config.is_three_dimensions && s.config.is_binding_goods) { if (s.config.is_three_dimensions && s.config.is_binding_goods) {
const wares = []; const wares = [];
const _sceneInformation = s.config.scene_information; const _sceneInformation = s.config.scene_information;
const sceneInformation const sceneInformation =
= typeof _sceneInformation === 'string' typeof _sceneInformation === 'string'
? JSON.parse(_sceneInformation) ? JSON.parse(_sceneInformation)
: _sceneInformation; : _sceneInformation;
sceneInformation.shelves.forEach((shelf) => { sceneInformation.shelves.forEach((shelf) => {
@@ -219,8 +221,8 @@ const canPlanetPublishImage = function(data) {
let message = ''; let message = '';
const qSteams = []; const qSteams = [];
let title = ''; let title = '';
data.questions data.questions &&
&& data.questions.forEach((s) => { data.questions.forEach((s) => {
if (s.question_type === 13) { if (s.question_type === 13) {
try { try {
if (s.options.length <= 0 || s.options.some((y) => y.length <= 0)) { if (s.options.length <= 0 || s.options.some((y) => y.length <= 0)) {
@@ -314,8 +316,8 @@ function canPublishRandom(data, publishType) {
if (!isValidated) { if (!isValidated) {
errors.push({ errors.push({
message: message:
field.message field.message ||
|| `请填写"${random.title}"中第${index + 1}组"随机题组"的"${field.name}"` `请填写"${random.title}"中第${index + 1}组"随机题组"的"${field.name}"`
}); });
} }
}); });
@@ -375,11 +377,11 @@ function isLoopingLogicValid(data, publishType) {
if ( if (
(data?.cycle_pages || []).every((i) => { (data?.cycle_pages || []).every((i) => {
return ( return (
i.question_index i.question_index &&
&& i.relation_type !== undefined i.relation_type !== undefined &&
&& i.relation_type !== null i.relation_type !== null &&
&& i.first_page i.first_page &&
&& i.last_page i.last_page
); );
}) })
) { ) {