Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 多级联动优化 #460

Merged
merged 2 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/src/enums/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ export enum QUESTION_TYPE {
/**
* 多级联动
*/
MULTILEVEL = 'multilevel',
CASCADER = 'cascader',
}
10 changes: 5 additions & 5 deletions server/src/interfaces/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ export interface NPS {
rightText: string;
}

export interface MultilevelItem {
export interface CascaderItem {
hash: string;
text: string;
children?: MultilevelItem[];
children?: CascaderItem[];
}

export interface MultilevelData {
export interface CascaderDate {
placeholder: Array<{
hash: string;
text: string;
}>;
children: Array<MultilevelItem>;
children: Array<CascaderItem>;
}

export interface TextRange {
Expand Down Expand Up @@ -74,7 +74,7 @@ export interface DataItem {
rangeConfig?: any;
starStyle?: string;
innerType?: string;
multilevelData: MultilevelData;
cascaderData: CascaderDate;
}

export interface Option {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class DataStatisticController {
QUESTION_TYPE.RADIO_STAR,
QUESTION_TYPE.RADIO_NPS,
QUESTION_TYPE.VOTE,
QUESTION_TYPE.MULTILEVEL,
QUESTION_TYPE.CASCADER,
];
const fieldList = responseSchema.code.dataConf.dataList
.filter((item) => allowQuestionType.includes(item.type as QUESTION_TYPE))
Expand Down
6 changes: 3 additions & 3 deletions server/src/modules/survey/services/dataStatistic.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@
}
// 将多级联动id还原成选项文案
if (
itemConfig.multilevelData &&
itemConfig.type === QUESTION_TYPE.MULTILEVEL
itemConfig.cascaderData &&
itemConfig.type === QUESTION_TYPE.CASCADER

Check warning on line 93 in server/src/modules/survey/services/dataStatistic.service.ts

View check run for this annotation

Codecov / codecov/patch

server/src/modules/survey/services/dataStatistic.service.ts#L93

Added line #L93 was not covered by tests
) {
let optionTextMap = keyBy(itemConfig.multilevelData.children, 'hash');
let optionTextMap = keyBy(itemConfig.cascaderData.children, 'hash');

Check warning on line 95 in server/src/modules/survey/services/dataStatistic.service.ts

View check run for this annotation

Codecov / codecov/patch

server/src/modules/survey/services/dataStatistic.service.ts#L95

Added line #L95 was not covered by tests
data[itemKey] = data[itemKey]
?.split(',')
.map((v) => {
Expand Down
4 changes: 2 additions & 2 deletions server/src/modules/survey/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,9 @@ export function handleAggretionData({ dataMap, item }) {
summary,
},
};
} else if (type == QUESTION_TYPE.MULTILEVEL) {
} else if (type == QUESTION_TYPE.CASCADER) {
const aggregation = getTextPaths(
dataMap[item.field].multilevelData.children,
dataMap[item.field].cascaderData.children,
);
return {
...item,
Expand Down
Binary file not shown.
6 changes: 3 additions & 3 deletions web/src/common/typeEnum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export enum QUESTION_TYPE {
RADIO_STAR = 'radio-star',
RADIO_NPS = 'radio-nps',
VOTE = 'vote',
MULTILEVEL = 'multilevel',
CASCADER = 'cascader',
}

// 题目类型标签映射对象
Expand All @@ -21,7 +21,7 @@ export const typeTagLabels: Record<QUESTION_TYPE, string> = {
[QUESTION_TYPE.RADIO_STAR]: '评分',
[QUESTION_TYPE.RADIO_NPS]: 'NPS评分',
[QUESTION_TYPE.VOTE]: '投票',
[QUESTION_TYPE.MULTILEVEL]: '多级联动'
[QUESTION_TYPE.CASCADER]: '多级联动'
}

// 输入类题型
Expand All @@ -42,4 +42,4 @@ export const CHOICES = [
export const RATES = [QUESTION_TYPE.RADIO_STAR, QUESTION_TYPE.RADIO_NPS]

// 高级题型分类
export const ADVANCED = [QUESTION_TYPE.MULTILEVEL]
export const ADVANCED = [QUESTION_TYPE.CASCADER]
12 changes: 6 additions & 6 deletions web/src/management/config/questionMenuConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ export const menuItems = {
icon: 'tixing-toupiao',
title: '投票'
},
multilevel: {
type: 'multilevel',
path: 'MultilevelModule',
snapshot: '/imgs/question-type-snapshot/multilevel.webp',
icon: 'multilevel-select',
cascader: {
type: 'cascader',
path: 'CascaderModule',
snapshot: '/imgs/question-type-snapshot/cascader.webp',
icon: 'cascader-select',
title: '多级联动'
}
}
Expand All @@ -74,7 +74,7 @@ const menuGroup = [
questionList: ['radio', 'checkbox', 'binary-choice', 'radio-star', 'radio-nps', 'vote']
}, {
title: '高级题型',
questionList: ['multilevel']
questionList: ['cascader']
}
]

Expand Down
138 changes: 138 additions & 0 deletions web/src/management/hooks/useCascaderPull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { ElMessageBox } from 'element-plus'
import { ref } from 'vue'
import { cloneDeep } from 'lodash-es'

interface NodeItem {
hash: string,
text: string,
children?: NodeItem[],
}

type CascaderDate = {
placeholder: Array<{
hash: string,
text: string,
}>,
children: Array<NodeItem>,
}

export const useCascaderPull = () => {
const maxCount = 3;
const optionsCount = 50;
const cascaderVal = ref<Array<null | NodeItem>>([]);
const cascaderData = ref<CascaderDate | null>(null)
let hashArr: Array<string> = [];


const extractHash = (obj: CascaderDate): Array<string> => {
const hashes: Array<string> = [];

function recurse(currentObj: any) {
if (Array.isArray(currentObj)) {
currentObj.forEach(item => recurse(item));
} else if (typeof currentObj === 'object' && currentObj !== null) {
if (currentObj.hash) {
hashes.push(currentObj.hash);
}
for (const key in currentObj) {
// eslint-disable-next-line no-prototype-builtins
if (currentObj.hasOwnProperty(key as any)) {
recurse(currentObj[key]);
}
}
}
}

recurse(obj);
return hashes;
}

const getRandom = () => {
return Math.random().toString().slice(-6)
}

const getNewHash = () => {
let random = getRandom()
while (random in hashArr) {
random = getRandom()
}
hashArr.push(random)
return random
}

const addCascaderNode = (key: number) => {
const nodeItem: NodeItem = (key == 0 ? cascaderData.value : cascaderVal.value[key - 1]) as NodeItem
if (nodeItem.children && nodeItem.children.length > optionsCount) {
ElMessageBox.alert(`当前最多添加${optionsCount}个选项`, '提示', {
confirmButtonText: '确定',
type: 'warning'
})
return
}
const optionStr = `选项${nodeItem?.children ? nodeItem?.children?.length + 1 : 1}`
nodeItem.children?.push({
hash: getNewHash(),
text: optionStr,
children: []
})
}

const resetCascaderVal = (index: number) => {
for (let i = cascaderVal.value.length; index < i; i--) {
cascaderVal.value[i - 1] = null;
}
}

const removeCascaderNode = (nodeItem: NodeItem, index: number, key: number) => {
try {
if (key == 0 && cascaderData.value?.children && cascaderData.value?.children?.length<=1) {
ElMessageBox.alert('至少保留一个选项', '提示', {
confirmButtonText: '确定',
type: 'warning'
})
return
}
if (nodeItem.children) {
nodeItem.children[index].children = [];
}
nodeItem.children?.splice(index, 1)
resetCascaderVal(key)
} catch (error) {
console.log(error)
}
}

const editCascaderNode = (nodeItem: NodeItem, index: number, text: string) => {
nodeItem.children && (nodeItem.children[index].text = text)
}


const setCascaderVal = (data: NodeItem, index: number) => {
if (cascaderVal.value[index]?.hash == data.hash) return
resetCascaderVal(index)
cascaderVal.value[index] = data
}


const loadInitData = (data: CascaderDate) => {
cascaderData.value = cloneDeep(data);
cascaderVal.value = [];
for (let index = 0; index < maxCount; index++) {
cascaderVal.value.push(null)
}
hashArr = extractHash(cascaderData.value);
}



return {
addCascaderNode,
removeCascaderNode,
editCascaderNode,
loadInitData,
setCascaderVal,

cascaderVal,
cascaderData
}
}
58 changes: 29 additions & 29 deletions web/src/management/hooks/useMultilevelPull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ interface NodeItem {
children?: NodeItem[],
}

type MultilevelDat = {
type CascaderDate = {
placeholder: Array<{
hash: string,
text: string,
}>,
children: Array<NodeItem>,
}

export const useMultilevelPull = () => {
export const useCascaderPull = () => {
const maxCount = 3;
const optionsCount = 50;
const multilevelVal = ref<Array<null | NodeItem>>([]);
const multilevelData = ref<MultilevelDat | null>(null)
const cascaderVal = ref<Array<null | NodeItem>>([]);
const cascaderData = ref<CascaderDate | null>(null)
let hashArr: Array<string> = [];


const extractHash = (obj: MultilevelDat): Array<string> => {
const extractHash = (obj: CascaderDate): Array<string> => {
const hashes: Array<string> = [];

function recurse(currentObj: any) {
Expand Down Expand Up @@ -60,8 +60,8 @@ export const useMultilevelPull = () => {
return random
}

const addMultilevelNode = (key: number) => {
const nodeItem: NodeItem = (key == 0 ? multilevelData.value : multilevelVal.value[key - 1]) as NodeItem
const addCascaderNode = (key: number) => {
const nodeItem: NodeItem = (key == 0 ? cascaderData.value : cascaderVal.value[key - 1]) as NodeItem
if (nodeItem.children && nodeItem.children.length > optionsCount) {
ElMessageBox.alert(`当前最多添加${optionsCount}个选项`, '提示', {
confirmButtonText: '确定',
Expand All @@ -77,15 +77,15 @@ export const useMultilevelPull = () => {
})
}

const resetMultilevelVal = (index: number) => {
for (let i = multilevelVal.value.length; index < i; i--) {
multilevelVal.value[i - 1] = null;
const resetCascaderVal = (index: number) => {
for (let i = cascaderVal.value.length; index < i; i--) {
cascaderVal.value[i - 1] = null;
}
}

const removeMultilevelNode = (nodeItem: NodeItem, index: number, key: number) => {
const removeCascaderNode = (nodeItem: NodeItem, index: number, key: number) => {
try {
if (key == 0 && multilevelData.value?.children && multilevelData.value?.children?.length<=1) {
if (key == 0 && cascaderData.value?.children && cascaderData.value?.children?.length<=1) {
ElMessageBox.alert('至少保留一个选项', '提示', {
confirmButtonText: '确定',
type: 'warning'
Expand All @@ -96,43 +96,43 @@ export const useMultilevelPull = () => {
nodeItem.children[index].children = [];
}
nodeItem.children?.splice(index, 1)
resetMultilevelVal(key)
resetCascaderVal(key)
} catch (error) {
console.log(error)
}
}

const editMultilevelNode = (nodeItem: NodeItem, index: number, text: string) => {
const editCascaderNode = (nodeItem: NodeItem, index: number, text: string) => {
nodeItem.children && (nodeItem.children[index].text = text)
}


const setMultilevelVal = (data: NodeItem, index: number) => {
if (multilevelVal.value[index]?.hash == data.hash) return
resetMultilevelVal(index)
multilevelVal.value[index] = data
const setCascaderVal = (data: NodeItem, index: number) => {
if (cascaderVal.value[index]?.hash == data.hash) return
resetCascaderVal(index)
cascaderVal.value[index] = data
}


const loadInitData = (data: MultilevelDat) => {
multilevelData.value = cloneDeep(data);
multilevelVal.value = [];
const loadInitData = (data: CascaderDate) => {
cascaderData.value = cloneDeep(data);
cascaderVal.value = [];
for (let index = 0; index < maxCount; index++) {
multilevelVal.value.push(null)
cascaderVal.value.push(null)
}
hashArr = extractHash(multilevelData.value);
hashArr = extractHash(cascaderData.value);
}



return {
addMultilevelNode,
removeMultilevelNode,
editMultilevelNode,
addCascaderNode,
removeCascaderNode,
editCascaderNode,
loadInitData,
setMultilevelVal,
setCascaderVal,

multilevelVal,
multilevelData
cascaderVal,
cascaderData
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const itemClass = computed(() => {
}
})
const showHover = computed(() => {
return isHover.value || props.isSelected
return isHover.value
})
const showUp = computed(() => {
return !props.isFirst
Expand Down
Loading
Loading