feat(component): 优化 contenteditable组件功能
- 添加 showAction 控制编辑按钮显示 - 实现文本域聚焦和失焦时的编辑按钮显示和隐藏 -优化键盘弹出和收起时的编辑按钮显示逻辑 -修复文档中描述的产品问卷配置- 优化问卷设计页面的题目编辑功能
This commit is contained in:
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -23,6 +23,7 @@ declare module 'vue' {
|
||||
VanDivider: typeof import('vant/es')['Divider']
|
||||
VanField: typeof import('vant/es')['Field']
|
||||
VanIcon: typeof import('vant/es')['Icon']
|
||||
VanNavBar: typeof import('vant/es')['NavBar']
|
||||
VanPopup: typeof import('vant/es')['Popup']
|
||||
VanRadio: typeof import('vant/es')['Radio']
|
||||
VanRadioGroup: typeof import('vant/es')['RadioGroup']
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
<template>
|
||||
<div
|
||||
ref="editor"
|
||||
:contenteditable="active"
|
||||
class="van-field"
|
||||
v-html="modelValue"
|
||||
></div>
|
||||
<div ref="editor" :contenteditable="active" class="van-field" v-html="modelValue"></div>
|
||||
<div v-if="showAction && active" ref="editorAction" class="editor-action">
|
||||
<button v-for="item in actions" :key="item.name" @click="funEvent(item,$event)">{{ item.label }}</button>
|
||||
<button v-for="item in actions" :key="item.name" @click="funEvent(item, $event)">
|
||||
{{ item.label }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -52,7 +49,7 @@ watch(
|
||||
() => props.active,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
showAction.value = true;
|
||||
// showAction.value = true;
|
||||
} else {
|
||||
save(editor.value);
|
||||
setTimeout(() => {
|
||||
@@ -105,7 +102,7 @@ onMounted(() => {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 3000;
|
||||
z-index: 2008;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
|
||||
177
src/utils/QuestionJsons/checkbox.js
Normal file
177
src/utils/QuestionJsons/checkbox.js
Normal file
@@ -0,0 +1,177 @@
|
||||
export default {
|
||||
title: '',
|
||||
stem: '<p>请输入题目(多选)</p>',
|
||||
other: '',
|
||||
question_index: '',
|
||||
question_type: 2,
|
||||
config: {
|
||||
placeholder: '',
|
||||
version: '',
|
||||
scene: null,
|
||||
shelf: null,
|
||||
ware: null,
|
||||
option_groups: null,
|
||||
is_required: 1,
|
||||
select_random: 0,
|
||||
each_number: 1,
|
||||
is_three_dimensions: 0,
|
||||
material_sn: '',
|
||||
scene_information: null,
|
||||
simple_scene_information: null,
|
||||
is_behavior: 0,
|
||||
is_price_tag: 0,
|
||||
is_brand: 0,
|
||||
is_initialize: 0,
|
||||
is_default_perspective: 0,
|
||||
is_binding_goods: 0,
|
||||
float_window: 0,
|
||||
float_window_content: '',
|
||||
popup_window: 0,
|
||||
popup_window_content: '',
|
||||
is_show: [],
|
||||
quick_type: 0,
|
||||
option_group_random_inside: null,
|
||||
option_group_random_outside: null
|
||||
},
|
||||
created_at: '',
|
||||
created_user_id: '',
|
||||
updated_user_id: null,
|
||||
survey_id: '',
|
||||
logic_config: {
|
||||
expect: '',
|
||||
order: 0,
|
||||
type: 0,
|
||||
stay_time: ''
|
||||
},
|
||||
options: [
|
||||
[
|
||||
{
|
||||
option: '<span >选项1</span>',
|
||||
id: '',
|
||||
type: 0,
|
||||
is_other: 0,
|
||||
is_fixed: 0,
|
||||
is_remove_other: 0,
|
||||
created_at: null,
|
||||
created_user_id: null,
|
||||
parent_id: null,
|
||||
option_index: 1,
|
||||
option_code: '',
|
||||
option_config: {
|
||||
title: '',
|
||||
instructions: [],
|
||||
price: 0,
|
||||
gradient: '',
|
||||
image_url: null,
|
||||
option_type: 0,
|
||||
type: 0,
|
||||
limit_right_content: '',
|
||||
child_area: null,
|
||||
binding_goods_id: ''
|
||||
},
|
||||
disable_option_update: 1,
|
||||
cascade: []
|
||||
},
|
||||
{
|
||||
option: '<span >选项2</span>',
|
||||
id: '',
|
||||
type: 0,
|
||||
is_other: 0,
|
||||
is_fixed: 0,
|
||||
is_remove_other: 0,
|
||||
created_at: null,
|
||||
created_user_id: null,
|
||||
parent_id: null,
|
||||
option_index: 2,
|
||||
option_code: '',
|
||||
option_config: {
|
||||
title: '',
|
||||
instructions: [],
|
||||
price: 0,
|
||||
gradient: '',
|
||||
image_url: null,
|
||||
option_type: 0,
|
||||
type: 0,
|
||||
limit_right_content: '',
|
||||
child_area: null,
|
||||
binding_goods_id: ''
|
||||
},
|
||||
disable_option_update: 1,
|
||||
cascade: []
|
||||
},
|
||||
{
|
||||
option: '<span >选项3</span>',
|
||||
id: '',
|
||||
type: 0,
|
||||
is_other: 0,
|
||||
is_fixed: 0,
|
||||
is_remove_other: 0,
|
||||
created_at: null,
|
||||
created_user_id: null,
|
||||
parent_id: null,
|
||||
option_index: 3,
|
||||
option_code: '',
|
||||
option_config: {
|
||||
title: '',
|
||||
instructions: [],
|
||||
price: 0,
|
||||
gradient: '',
|
||||
image_url: null,
|
||||
option_type: 0,
|
||||
type: 0,
|
||||
limit_right_content: '',
|
||||
child_area: null,
|
||||
binding_goods_id: ''
|
||||
},
|
||||
disable_option_update: 1,
|
||||
cascade: []
|
||||
},
|
||||
{
|
||||
option: '<span >选项4</span>',
|
||||
id: '',
|
||||
type: 0,
|
||||
is_other: 0,
|
||||
is_fixed: 0,
|
||||
is_remove_other: 0,
|
||||
created_at: null,
|
||||
created_user_id: null,
|
||||
parent_id: null,
|
||||
option_index: 4,
|
||||
option_code: '',
|
||||
option_config: {
|
||||
title: '',
|
||||
instructions: [],
|
||||
price: 0,
|
||||
gradient: '',
|
||||
image_url: null,
|
||||
option_type: 0,
|
||||
type: 0,
|
||||
limit_right_content: '',
|
||||
child_area: null,
|
||||
binding_goods_id: ''
|
||||
},
|
||||
disable_option_update: 1,
|
||||
cascade: []
|
||||
}
|
||||
]
|
||||
],
|
||||
associate: [],
|
||||
logics_has: false,
|
||||
last_option_index: 4,
|
||||
question_code: '',
|
||||
question_value: '',
|
||||
question_tag: '',
|
||||
planet_id: '',
|
||||
permissions: {
|
||||
id: 9032,
|
||||
question_id: 17835006,
|
||||
parent_question_id: 0,
|
||||
disable_update: 1,
|
||||
disable_option_update: 1,
|
||||
disable_copy: 1,
|
||||
disable_delete: 1,
|
||||
delete_group_keep_one: 1,
|
||||
disable_copy_delete: 1,
|
||||
disable_copy_update: 1
|
||||
}
|
||||
};
|
||||
177
src/utils/QuestionJsons/radio.js
Normal file
177
src/utils/QuestionJsons/radio.js
Normal file
@@ -0,0 +1,177 @@
|
||||
export default {
|
||||
title: '',
|
||||
stem: '<p>请输入题目(单选)</p>',
|
||||
other: '',
|
||||
question_index: '',
|
||||
question_type: 1,
|
||||
config: {
|
||||
placeholder: '',
|
||||
version: '',
|
||||
scene: null,
|
||||
shelf: null,
|
||||
ware: null,
|
||||
option_groups: null,
|
||||
is_required: 1,
|
||||
select_random: 0,
|
||||
each_number: 1,
|
||||
is_three_dimensions: 0,
|
||||
material_sn: '',
|
||||
scene_information: null,
|
||||
simple_scene_information: null,
|
||||
is_behavior: 0,
|
||||
is_price_tag: 0,
|
||||
is_brand: 0,
|
||||
is_initialize: 0,
|
||||
is_default_perspective: 0,
|
||||
is_binding_goods: 0,
|
||||
float_window: 0,
|
||||
float_window_content: '',
|
||||
popup_window: 0,
|
||||
popup_window_content: '',
|
||||
is_show: [],
|
||||
quick_type: 0,
|
||||
option_group_random_inside: null,
|
||||
option_group_random_outside: null
|
||||
},
|
||||
created_at: '',
|
||||
created_user_id: '',
|
||||
updated_user_id: null,
|
||||
survey_id: '',
|
||||
logic_config: {
|
||||
expect: '',
|
||||
order: 0,
|
||||
type: 0,
|
||||
stay_time: ''
|
||||
},
|
||||
options: [
|
||||
[
|
||||
{
|
||||
option: '<span >选项1</span>',
|
||||
id: '',
|
||||
type: 0,
|
||||
is_other: 0,
|
||||
is_fixed: 0,
|
||||
is_remove_other: 0,
|
||||
created_at: null,
|
||||
created_user_id: null,
|
||||
parent_id: null,
|
||||
option_index: 1,
|
||||
option_code: '',
|
||||
option_config: {
|
||||
title: '',
|
||||
instructions: [],
|
||||
price: 0,
|
||||
gradient: '',
|
||||
image_url: null,
|
||||
option_type: 0,
|
||||
type: 0,
|
||||
limit_right_content: '',
|
||||
child_area: null,
|
||||
binding_goods_id: ''
|
||||
},
|
||||
disable_option_update: 1,
|
||||
cascade: []
|
||||
},
|
||||
{
|
||||
option: '<span >选项2</span>',
|
||||
id: '',
|
||||
type: 0,
|
||||
is_other: 0,
|
||||
is_fixed: 0,
|
||||
is_remove_other: 0,
|
||||
created_at: null,
|
||||
created_user_id: null,
|
||||
parent_id: null,
|
||||
option_index: 2,
|
||||
option_code: '',
|
||||
option_config: {
|
||||
title: '',
|
||||
instructions: [],
|
||||
price: 0,
|
||||
gradient: '',
|
||||
image_url: null,
|
||||
option_type: 0,
|
||||
type: 0,
|
||||
limit_right_content: '',
|
||||
child_area: null,
|
||||
binding_goods_id: ''
|
||||
},
|
||||
disable_option_update: 1,
|
||||
cascade: []
|
||||
},
|
||||
{
|
||||
option: '<span >选项3</span>',
|
||||
id: '',
|
||||
type: 0,
|
||||
is_other: 0,
|
||||
is_fixed: 0,
|
||||
is_remove_other: 0,
|
||||
created_at: null,
|
||||
created_user_id: null,
|
||||
parent_id: null,
|
||||
option_index: 3,
|
||||
option_code: '',
|
||||
option_config: {
|
||||
title: '',
|
||||
instructions: [],
|
||||
price: 0,
|
||||
gradient: '',
|
||||
image_url: null,
|
||||
option_type: 0,
|
||||
type: 0,
|
||||
limit_right_content: '',
|
||||
child_area: null,
|
||||
binding_goods_id: ''
|
||||
},
|
||||
disable_option_update: 1,
|
||||
cascade: []
|
||||
},
|
||||
{
|
||||
option: '<span >选项4</span>',
|
||||
id: '',
|
||||
type: 0,
|
||||
is_other: 0,
|
||||
is_fixed: 0,
|
||||
is_remove_other: 0,
|
||||
created_at: null,
|
||||
created_user_id: null,
|
||||
parent_id: null,
|
||||
option_index: 4,
|
||||
option_code: '',
|
||||
option_config: {
|
||||
title: '',
|
||||
instructions: [],
|
||||
price: 0,
|
||||
gradient: '',
|
||||
image_url: null,
|
||||
option_type: 0,
|
||||
type: 0,
|
||||
limit_right_content: '',
|
||||
child_area: null,
|
||||
binding_goods_id: ''
|
||||
},
|
||||
disable_option_update: 1,
|
||||
cascade: []
|
||||
}
|
||||
]
|
||||
],
|
||||
associate: [],
|
||||
logics_has: false,
|
||||
last_option_index: 4,
|
||||
question_code: '',
|
||||
question_value: '',
|
||||
question_tag: '',
|
||||
planet_id: '',
|
||||
permissions: {
|
||||
id: 9032,
|
||||
question_id: 17835006,
|
||||
parent_question_id: 0,
|
||||
disable_update: 1,
|
||||
disable_option_update: 1,
|
||||
disable_copy: 1,
|
||||
disable_delete: 1,
|
||||
delete_group_keep_one: 1,
|
||||
disable_copy_delete: 1,
|
||||
disable_copy_update: 1
|
||||
}
|
||||
};
|
||||
331
src/utils/common.js
Normal file
331
src/utils/common.js
Normal file
@@ -0,0 +1,331 @@
|
||||
const basicQuesTypeList = [
|
||||
{
|
||||
name: '选择题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
childTypes: [1, 2]
|
||||
},
|
||||
{
|
||||
name: '级联题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
childTypes: [3]
|
||||
},
|
||||
{
|
||||
name: '填空题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
childTypes: [4]
|
||||
},
|
||||
{
|
||||
name: '多项填空题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 27,
|
||||
childTypes: [27]
|
||||
},
|
||||
{
|
||||
name: '打分题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 5,
|
||||
childTypes: [5]
|
||||
},
|
||||
{
|
||||
name: '矩阵题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 9,
|
||||
childTypes: [8, 9, 10, 11]
|
||||
},
|
||||
{
|
||||
name: '图片题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 13,
|
||||
childTypes: [12, 13, 14]
|
||||
},
|
||||
{
|
||||
name: '分类题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 15,
|
||||
childTypes: [15]
|
||||
},
|
||||
{
|
||||
name: '排序题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 16,
|
||||
childTypes: [16]
|
||||
},
|
||||
{
|
||||
name: '图文说明题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 6,
|
||||
childTypes: [6]
|
||||
},
|
||||
{
|
||||
name: '日期/时间',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 7,
|
||||
childTypes: [7]
|
||||
},
|
||||
{
|
||||
name: '恒定总和题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 17,
|
||||
childTypes: [17]
|
||||
},
|
||||
{
|
||||
name: '文件上传题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 18,
|
||||
childTypes: [18]
|
||||
},
|
||||
{
|
||||
name: '热区题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 25,
|
||||
childTypes: [25, 56]
|
||||
},
|
||||
{
|
||||
name: 'NPS',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 106,
|
||||
childTypes: [106]
|
||||
}
|
||||
];
|
||||
const quickQuesTypeList = [
|
||||
{
|
||||
name: '姓名',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
quickType: 6
|
||||
},
|
||||
{
|
||||
name: '性别',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
quickType: 7
|
||||
},
|
||||
{
|
||||
name: '手机号',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 20
|
||||
},
|
||||
{
|
||||
name: '身份证号',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
quickType: 2
|
||||
},
|
||||
{
|
||||
name: '邮箱',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
quickType: 8
|
||||
},
|
||||
{
|
||||
name: '年龄',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
quickType: 9
|
||||
},
|
||||
{
|
||||
name: '年龄段',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
quickType: 10
|
||||
},
|
||||
{
|
||||
name: '生日',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 7,
|
||||
quickType: 11
|
||||
},
|
||||
{
|
||||
name: '学历',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
quickType: 12
|
||||
},
|
||||
{
|
||||
name: '院校',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 13
|
||||
},
|
||||
{
|
||||
name: '专业',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 14
|
||||
},
|
||||
{
|
||||
name: '行业',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
quickType: 15
|
||||
},
|
||||
{
|
||||
name: '地理位置',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 19,
|
||||
quickType: 16
|
||||
},
|
||||
{
|
||||
name: '省份',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 3
|
||||
},
|
||||
{
|
||||
name: '省市',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 4
|
||||
},
|
||||
{
|
||||
name: '省市区/县',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 5
|
||||
},
|
||||
// {
|
||||
// type: 21,
|
||||
// quickType: 21,
|
||||
// name: "密码",
|
||||
// icon: "",
|
||||
// check: false,
|
||||
// },
|
||||
{
|
||||
type: 22,
|
||||
name: '签名题',
|
||||
icon: '',
|
||||
check: false
|
||||
},
|
||||
{
|
||||
type: 23,
|
||||
name: '知情同意书',
|
||||
icon: '',
|
||||
check: false
|
||||
}
|
||||
];
|
||||
const advancedQuesTypeList = [
|
||||
{
|
||||
name: 'Maxdiff',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 105
|
||||
},
|
||||
// {
|
||||
// name: "CBC",
|
||||
// icon: "",
|
||||
// check: false,
|
||||
// type: 103,
|
||||
// },
|
||||
// // {
|
||||
// // name: "BPTO",
|
||||
// // icon: "",
|
||||
// // check: false,
|
||||
// // type: 104,
|
||||
// // },
|
||||
{
|
||||
name: 'PSM',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 101
|
||||
},
|
||||
{
|
||||
name: 'KANO',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 102
|
||||
}
|
||||
];
|
||||
const d3QuestypeList = [
|
||||
{
|
||||
name: 'ID test',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 200
|
||||
},
|
||||
{
|
||||
name: 'Experiment',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 201
|
||||
}
|
||||
];
|
||||
const uxSimuatorQuestypeList = [
|
||||
{
|
||||
name: 'UI',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 202
|
||||
},
|
||||
{
|
||||
name: 'Prototype',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 203
|
||||
},
|
||||
{
|
||||
name: 'UE',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 204
|
||||
}
|
||||
];
|
||||
|
||||
function 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;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
basicQuesTypeList,
|
||||
quickQuesTypeList,
|
||||
advancedQuesTypeList,
|
||||
d3QuestypeList,
|
||||
uxSimuatorQuestypeList,
|
||||
getIcon
|
||||
};
|
||||
3
src/utils/importJsons.js
Normal file
3
src/utils/importJsons.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// 引入QuestionJsons 下的radio 并导出
|
||||
export { default as radio } from './QuestionJsons/radio.js';
|
||||
export { default as checkbox } from './QuestionJsons/checkbox.js';
|
||||
@@ -1,22 +1,32 @@
|
||||
<template>
|
||||
<div class="design-create">
|
||||
<draggable
|
||||
v-model:data="questionInfo.questions" item-key="id" handle=".moverQues" chosenClass="chosen"
|
||||
animation="300" :scroll="true"
|
||||
v-model:data="questionInfo.questions"
|
||||
item-key="id"
|
||||
handle=".moverQues"
|
||||
chosenClass="chosen"
|
||||
animation="300"
|
||||
:scroll="true"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<choose-question
|
||||
:element="element" :questions="questionInfo.questions" :index="index"
|
||||
:chooseQuestionId="chooseQuestionId" @get-choose-question-id="getChooseQuestionId"
|
||||
:element="element"
|
||||
:questions="questionInfo.questions"
|
||||
:index="index"
|
||||
:chooseQuestionId="chooseQuestionId"
|
||||
@get-choose-question-id="getChooseQuestionId"
|
||||
>
|
||||
<!-- 选择题 -->
|
||||
<Choice
|
||||
v-if="element.question_type === 1 || element.question_type === 2" :element="element"
|
||||
v-if="element.question_type === 1 || element.question_type === 2"
|
||||
:element="element"
|
||||
:active="chooseQuestionId === element.id"
|
||||
></Choice>
|
||||
<!-- 填空题 -->
|
||||
<Completion
|
||||
v-if="element.question_type === 4" :element="element" :active="chooseQuestionId === element.id"
|
||||
v-if="element.question_type === 4"
|
||||
:element="element"
|
||||
:active="chooseQuestionId === element.id"
|
||||
sn="lXEBBpE2"
|
||||
></Completion>
|
||||
|
||||
@@ -26,37 +36,54 @@
|
||||
element.question_type === 8 ||
|
||||
element.question_type === 9 ||
|
||||
element.question_type === 10
|
||||
" :element="element" :active="chooseQuestionId === element.id"
|
||||
"
|
||||
:element="element"
|
||||
:active="chooseQuestionId === element.id"
|
||||
/>
|
||||
|
||||
<!-- 签名题 -->
|
||||
<sign-question
|
||||
v-if="element.question_type === 22" :element="element"
|
||||
v-if="[9, 10, 22].includes(element.question_type)"
|
||||
:element="element"
|
||||
:active="chooseQuestionId === element.id"
|
||||
></sign-question>
|
||||
/>
|
||||
|
||||
<!-- 文件上传题 -->
|
||||
<file-upload
|
||||
v-if="element.question_type === 18" :element="element" :active="chooseQuestionId === element.id"
|
||||
v-if="element.question_type === 18"
|
||||
:element="element"
|
||||
:active="chooseQuestionId === element.id"
|
||||
></file-upload>
|
||||
|
||||
<!-- 打分题 -->
|
||||
<Rate
|
||||
v-if="element.question_type === 5" :element="element" :active="chooseQuestionId === element.id"
|
||||
v-if="element.question_type === 5"
|
||||
:element="element"
|
||||
:active="chooseQuestionId === element.id"
|
||||
sn="lXEBBpE2"
|
||||
/>
|
||||
|
||||
<!--图文-->
|
||||
<TextWithImages
|
||||
v-if="element.question_type === 6" :element="element"
|
||||
v-if="element.question_type === 6"
|
||||
:element="element"
|
||||
:active="chooseQuestionId === element.id"
|
||||
/>
|
||||
<!--图文-->
|
||||
<NPS
|
||||
v-if="element.question_type === 106"
|
||||
:element="element"
|
||||
:active="chooseQuestionId === element.id"
|
||||
/>
|
||||
|
||||
<!--组件底部左侧操作-->
|
||||
<template #action="{ element: el }">
|
||||
<div class="flex slot-actions">
|
||||
<template v-for="(item, optionIndex) in actionOptions">
|
||||
<div v-if="item.question_type.includes(el.question_type)" :key="optionIndex" class="flex">
|
||||
<div
|
||||
v-if="item.question_type.includes(el.question_type)"
|
||||
:key="optionIndex"
|
||||
class="flex"
|
||||
>
|
||||
<template v-for="(act, actIndex) in item.actions" :key="actIndex">
|
||||
<div class="flex align-center action-item" @click="actionEvent(act, el)">
|
||||
<van-icon :name="act.icon"></van-icon>
|
||||
@@ -72,8 +99,11 @@
|
||||
<!-- {{ element.question_type }}-->
|
||||
<!-- {{questionInfo.survey.pages.length}}-->
|
||||
<paging
|
||||
v-if="!element.question_type && questionInfo.survey.pages.length > 1" :info="element" :index="index"
|
||||
:active="pageIsActive(activeIndex, questionInfo.questions, element.page)" @click.stop=""
|
||||
v-if="!element.question_type && questionInfo.survey.pages.length > 1"
|
||||
:info="element"
|
||||
:index="index"
|
||||
:active="pageIsActive(activeIndex, questionInfo.questions, element.page)"
|
||||
@click.stop=""
|
||||
/>
|
||||
</template>
|
||||
</draggable>
|
||||
@@ -94,6 +124,8 @@ import Rate from './components/Questions/Rate.vue';
|
||||
import TextWithImages from '@/views/Design/components/Questions/TextWithImages.vue';
|
||||
import SignQuestion from './components/Questions/SignQuestion.vue';
|
||||
import FileUpload from './components/Questions/FileUpload.vue';
|
||||
import NPS from '@/views/Design/components/Questions/NPS.vue';
|
||||
|
||||
const activeIndex = ref(-1);
|
||||
/**
|
||||
* 工具函数
|
||||
@@ -148,9 +180,12 @@ const store = storeToRefs(counterStore);
|
||||
const chooseQuestionId = ref('');
|
||||
const questionInfo = ref(store.questionsInfo.value);
|
||||
|
||||
const emit = defineEmits(['getActiveQuestion']);
|
||||
// 获取选中的题目的ID
|
||||
const getChooseQuestionId = (questionItem) => {
|
||||
chooseQuestionId.value = questionItem.id;
|
||||
// 向外传出选中的题目
|
||||
emit('getActiveQuestion', questionItem);
|
||||
};
|
||||
// 组件对应的操作
|
||||
const actionOptions = [
|
||||
@@ -238,12 +273,12 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.design-create {
|
||||
min-height: calc(100vh);
|
||||
//min-height: calc(100vh);
|
||||
background-color: #e9eef3;
|
||||
color: #333;
|
||||
|
||||
.slot-actions {
|
||||
& .action-item+.action-item {
|
||||
& .action-item + .action-item {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,11 @@
|
||||
></van-switch>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="选项随机" :border="false">
|
||||
<van-cell
|
||||
v-if="[1, 2].includes(activeQuestion.question_type)"
|
||||
title="选项随机"
|
||||
:border="false"
|
||||
>
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="activeQuestion.config.select_random"
|
||||
@@ -95,13 +99,13 @@
|
||||
</van-popup>
|
||||
</template>
|
||||
<script setup>
|
||||
import checkboxQuestionAction from './components/QuestionItemAction/checkboxQuestionAction.vue';
|
||||
import checkboxQuestionAction from './components/QuestionItemAction/CheckboxQuestionAction.vue';
|
||||
import { showConfirmDialog } from 'vant';
|
||||
import { ref } from 'vue';
|
||||
import { useCounterStore } from '@/stores/counter';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import QuestionBefore from '@/views/Design/components/ActionCompoents/components/QuestionItemAction/QuestionBefore.vue';
|
||||
import RateAction from './components/RateAction.vue';
|
||||
import RateAction from './components/OptionItemAction/RateAction.vue';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
const store = useCounterStore();
|
||||
const { questionsInfo } = storeToRefs(store);
|
||||
|
||||
77
src/views/Design/components/Questions/NPS.vue
Normal file
77
src/views/Design/components/Questions/NPS.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<van-field
|
||||
v-model="element.stem"
|
||||
:label="element.stem"
|
||||
:required="element.config.is_required === 1"
|
||||
label-align="top"
|
||||
>
|
||||
<template #label>
|
||||
<div
|
||||
:contenteditable="active"
|
||||
class="van-field"
|
||||
@blur="saveStem($event, element)"
|
||||
v-html="element.stem"
|
||||
></div>
|
||||
</template>
|
||||
<template #input>
|
||||
<div v-for="(optionItem, optionItemIndex) in element.options" :key="optionItemIndex">
|
||||
<div v-for="(item, index) in optionItem" :key="index" @click="chooseOption(item)">
|
||||
<!-- <div-->
|
||||
<!-- :contenteditable="item.id === chooseId"-->
|
||||
<!-- class="van-field"-->
|
||||
<!-- v-html="item.option"-->
|
||||
<!-- ></div>-->
|
||||
<RateCharacter :config="element.config"></RateCharacter>
|
||||
<div class="tips">
|
||||
<p>{{ element.config.prompt_left }}</p>
|
||||
<p>{{ element.config.prompt_center }}</p>
|
||||
<p>{{ element.config.prompt_right }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import RateCharacter from './RateCharacter.vue';
|
||||
|
||||
const props = defineProps({
|
||||
element: {
|
||||
type: Object
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
sn: { type: String, default: '' },
|
||||
questionType: { type: [String, Number], default: 4 }
|
||||
});
|
||||
const element = ref(props.element);
|
||||
const chooseId = ref('');
|
||||
// 创建一个本地副本以保存更改
|
||||
const localElement = ref({ ...props.element });
|
||||
|
||||
const saveStem = (e) => {
|
||||
localElement.value.stem = e.target.innerHTML;
|
||||
};
|
||||
|
||||
const chooseOption = (item) => {
|
||||
chooseId.value = item.id;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.content {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.tips {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
</style>
|
||||
@@ -15,27 +15,32 @@
|
||||
</div>
|
||||
|
||||
<button @click="show = true">添加题目</button>
|
||||
<div class="ques">
|
||||
<van-popup v-model:show="show" round position="bottom" :style="{ height: '50%' }">
|
||||
<van-row class="ques_title">
|
||||
<van-col :span="6">添加题目</van-col>
|
||||
<van-col :span="6" :offset="12" @click="show = false">
|
||||
<van-icon name="close" />
|
||||
</van-col>
|
||||
</van-row>
|
||||
<ul>
|
||||
<li v-for="item in quesList" :key="item.type">
|
||||
<div>
|
||||
<van-icon :name="item.icon" size="20" />
|
||||
<p>{{ item.name }}</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</van-popup>
|
||||
</div>
|
||||
</div>
|
||||
</van-cell-group>
|
||||
|
||||
<div class="ques">
|
||||
<!-- 题目-->
|
||||
<Design @get-active-question="getActiveQuestion"></Design>
|
||||
<van-button @click="show = true">添加题目</van-button>
|
||||
<!-- 弹出的新增题目弹窗-->
|
||||
<van-popup v-model:show="show" round position="bottom" :style="{ height: '50%' }">
|
||||
<van-row class="ques_title">
|
||||
<van-col :span="6">添加题目</van-col>
|
||||
<van-col :span="6" :offset="12" @click="show = false">
|
||||
<van-icon name="close" />
|
||||
</van-col>
|
||||
</van-row>
|
||||
<ul>
|
||||
<li v-for="item in quesList" :key="item.type" @click="questionEvent(item)">
|
||||
<div>
|
||||
<van-icon :name="item.icon" size="20" />
|
||||
<p>{{ item.name }}</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
<van-cell-group inset class="thanks-cell">
|
||||
<div>您已完成本次调研,感谢您的参与!</div>
|
||||
</van-cell-group>
|
||||
@@ -57,71 +62,127 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import Design from '@/views/Design/Index.vue';
|
||||
import { useCounterStore } from '@/stores/counter';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { radio, checkbox } from '@/utils/importJsons';
|
||||
import { useRoute } from 'vue-router';
|
||||
// 获取 Store 实例
|
||||
const counterStore = useCounterStore();
|
||||
const store = storeToRefs(counterStore);
|
||||
|
||||
const chooseQuestionId = ref('');
|
||||
const questionInfo = ref(store.questionsInfo.value);
|
||||
|
||||
const activeQuestionIndex = ref(-1);
|
||||
|
||||
const route = useRoute();
|
||||
const surveyTitle = route.meta.title as string;
|
||||
const show = ref(false);
|
||||
|
||||
// 获取选中的题目
|
||||
const getActiveQuestion = (activeQues) => {
|
||||
chooseQuestionId.value = activeQues.id;
|
||||
// 在questions 里 查找index 给 activeQuestionIndex
|
||||
questionInfo.value.questions.forEach((item, index) => {
|
||||
if (item.id === activeQues.id) {
|
||||
activeQuestionIndex.value = index;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const quesList = ref([
|
||||
{
|
||||
icon: 'location-o',
|
||||
name: '单选题',
|
||||
type: '1'
|
||||
question_type: '1',
|
||||
json: radio
|
||||
},
|
||||
{
|
||||
icon: 'like-o',
|
||||
name: '多选题',
|
||||
type: '2'
|
||||
question_type: '2',
|
||||
json: checkbox
|
||||
},
|
||||
{
|
||||
icon: 'star-o',
|
||||
name: '填空题',
|
||||
type: '3'
|
||||
question_type: '3'
|
||||
},
|
||||
{
|
||||
icon: 'phone-o',
|
||||
name: '图形打分',
|
||||
type: '4'
|
||||
question_type: '4'
|
||||
},
|
||||
{
|
||||
icon: 'cart-o',
|
||||
name: '数值打分',
|
||||
type: '5'
|
||||
question_type: '5'
|
||||
},
|
||||
{
|
||||
icon: 'comment-o',
|
||||
name: '矩阵单选',
|
||||
type: '6'
|
||||
question_type: '6'
|
||||
},
|
||||
{
|
||||
icon: 'bag-o',
|
||||
name: '矩阵多选',
|
||||
type: '7'
|
||||
question_type: '7'
|
||||
},
|
||||
{
|
||||
icon: 'gift-o',
|
||||
name: '矩阵填空',
|
||||
type: '8'
|
||||
question_type: '8'
|
||||
},
|
||||
{
|
||||
icon: 'bag-o',
|
||||
name: '文件上传',
|
||||
type: '9'
|
||||
question_type: '9'
|
||||
}
|
||||
]);
|
||||
|
||||
const questionEvent = (item) => {
|
||||
let questionJson = {};
|
||||
switch (item.question_type) {
|
||||
// 单选
|
||||
case '1':
|
||||
case '2':
|
||||
questionJson = JSON.parse(
|
||||
JSON.stringify({
|
||||
...item.json,
|
||||
id: uuidv4(),
|
||||
question_index: questionInfo.value.survey.last_question_index + 1,
|
||||
options: item.json.options.map((item) => {
|
||||
return item.map((it) => {
|
||||
return {
|
||||
...it,
|
||||
// 主键生成
|
||||
id: uuidv4()
|
||||
};
|
||||
});
|
||||
})
|
||||
})
|
||||
);
|
||||
|
||||
questionInfo.value.questions.splice(activeQuestionIndex.value + 1, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
// 给store 装数据 判断是 push 还是 splice
|
||||
if (activeQuestionIndex.value === -1) {
|
||||
questionInfo.value.questions.push(questionJson);
|
||||
} else {
|
||||
questionInfo.value.questions.splice(activeQuestionIndex.value + 1, 0, questionJson);
|
||||
}
|
||||
// 更新题目索引
|
||||
questionInfo.value.survey.last_question_index += 1;
|
||||
};
|
||||
|
||||
const init = () => {
|
||||
// event.detail 为当前输入的值
|
||||
show.value = true;
|
||||
};
|
||||
|
||||
// const handleInputIntro = (e: Event) => {
|
||||
// // event.detail 为当前输入的值
|
||||
// console.log(e);
|
||||
// surveyIntro.value = e.target.value;
|
||||
// };
|
||||
|
||||
defineExpose({ init });
|
||||
</script>
|
||||
|
||||
@@ -137,7 +198,7 @@ defineExpose({ init });
|
||||
}
|
||||
|
||||
* {
|
||||
font-size: 12px;
|
||||
//font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,13 +206,13 @@ defineExpose({ init });
|
||||
margin: 10px 5px;
|
||||
padding: 5px;
|
||||
|
||||
&>div {
|
||||
& > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
&>button {
|
||||
& > button {
|
||||
margin: 20px;
|
||||
border-radius: 10px;
|
||||
background-color: lightgreen;
|
||||
|
||||
Reference in New Issue
Block a user