feat(component): 优化 contenteditable组件功能

- 添加 showAction 控制编辑按钮显示
- 实现文本域聚焦和失焦时的编辑按钮显示和隐藏
-优化键盘弹出和收起时的编辑按钮显示逻辑
-修复文档中描述的产品问卷配置- 优化问卷设计页面的题目编辑功能
This commit is contained in:
陈昱达
2025-03-07 18:12:53 +08:00
parent 8f903e3869
commit 529c0ff940
14 changed files with 291 additions and 6274 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -162,16 +162,5 @@ export default {
question_value: '', question_value: '',
question_tag: '', question_tag: '',
planet_id: '', planet_id: '',
permissions: { 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
}
}; };

View File

@@ -0,0 +1,60 @@
export default {
id: '',
title: '',
stem: '<p>请输入题目(填空)</p>',
other: '',
question_index: '',
question_type: 4,
config: {
min: '',
max: '',
version: '',
scene: null,
shelf: null,
ware: null,
placeholder: '',
is_required: 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,
left_prompt: '',
right_prompt: '',
text_type: 0,
include_mark: 0,
line_type: 1,
line_height: 3,
decimal_few: 1,
type_name: '',
float_window: 0,
float_window_content: '',
popup_window: 0,
popup_window_content: '',
is_show: [],
quick_type: 0
},
created_at: '',
created_user_id: 1180,
updated_user_id: null,
survey_id: 8721,
logic_config: {
expect: '',
order: 0,
type: 0,
stay_time: ''
},
options: [],
associate: [],
logics_has: false,
last_option_index: 0,
question_code: '',
question_value: '',
question_tag: '',
planet_id: '',
permissions: {}
};

View File

@@ -0,0 +1,54 @@
export default {
// JSON 需要修改
id: '',
question_type: 5,
question_index: '',
stem: '请完成打分',
title: '',
options: [
[
{
id: 'e46f51b1-bfd8-4d9c-becc-4fb7d175a6f4',
is_fixed: 0,
is_other: 0,
is_remove_other: 0,
option: '<p>选项1</p>',
option_config: {
image_url: [],
title: '',
instructions: [],
option_type: 0,
limit_right_content: ''
},
option_index: 1,
parent_id: 0,
type: 0,
cascade: [],
config: []
}
]
],
last_option_index: 1,
config: {
is_required: 1,
quick_type: 0,
is_show: [],
max: 5,
min: 1,
prompt_center: '',
prompt_left: '',
prompt_right: '',
score_interval: 1,
score_type: 0,
score_way: 0,
prompt_score: 2
},
associate: [],
question_code: '',
logic_config: {
order: 0,
type: 0,
expect: '',
stay_time: ''
}
};

View File

@@ -162,16 +162,5 @@ export default {
question_value: '', question_value: '',
question_tag: '', question_tag: '',
planet_id: '', planet_id: '',
permissions: { 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
}
}; };

View File

@@ -0,0 +1,53 @@
export default {
id: '',
question_type: 5,
question_index: '',
stem: '请完成打分',
title: '',
options: [
[
{
id: 'e46f51b1-bfd8-4d9c-becc-4fb7d175a6f4',
is_fixed: 0,
is_other: 0,
is_remove_other: 0,
option: '<p>选项1</p>',
option_config: {
image_url: [],
title: '',
instructions: [],
option_type: 0,
limit_right_content: ''
},
option_index: 1,
parent_id: 0,
type: 0,
cascade: [],
config: []
}
]
],
last_option_index: 1,
config: {
is_required: 1,
quick_type: 0,
is_show: [],
max: 5,
min: 1,
prompt_center: '',
prompt_left: '',
prompt_right: '',
score_interval: 1,
score_type: 0,
score_way: 0,
prompt_score: 2
},
associate: [],
question_code: '',
logic_config: {
order: 0,
type: 0,
expect: '',
stay_time: ''
}
};

View File

@@ -1,3 +1,6 @@
// 引入QuestionJsons 下的radio 并导出 // 引入QuestionJsons 下的radio 并导出
export { default as radio } from './QuestionJsons/radio.js'; export { default as radio } from './QuestionJsons/Radio.js';
export { default as checkbox } from './QuestionJsons/checkbox.js'; export { default as checkbox } from './QuestionJsons/Checkbox.js';
export { default as completion } from './QuestionJsons/Completion.js';
export { default as rate } from './QuestionJsons/Rate.js';
export { default as martrixQuestion } from './QuestionJsons/MartrixQuestion.js';

View File

@@ -20,11 +20,13 @@
<Choice <Choice
v-if="element.question_type === 1 || element.question_type === 2" v-if="element.question_type === 1 || element.question_type === 2"
:element="element" :element="element"
:index="index"
:active="chooseQuestionId === element.id" :active="chooseQuestionId === element.id"
></Choice> ></Choice>
<!-- 填空题 --> <!-- 填空题 -->
<Completion <Completion
v-if="element.question_type === 4" v-if="element.question_type === 4"
:index="index"
:element="element" :element="element"
:active="chooseQuestionId === element.id" :active="chooseQuestionId === element.id"
sn="lXEBBpE2" sn="lXEBBpE2"
@@ -38,13 +40,15 @@
element.question_type === 10 element.question_type === 10
" "
:element="element" :element="element"
:index="index"
:active="chooseQuestionId === element.id" :active="chooseQuestionId === element.id"
/> />
<!-- 签名题 --> <!-- 签名题 -->
<sign-question <sign-question
v-if="[9, 10, 22].includes(element.question_type)" v-if="[22].includes(element.question_type)"
:element="element" :element="element"
:index="index"
:active="chooseQuestionId === element.id" :active="chooseQuestionId === element.id"
/> />
@@ -52,6 +56,7 @@
<file-upload <file-upload
v-if="element.question_type === 18" v-if="element.question_type === 18"
:element="element" :element="element"
:index="index"
:active="chooseQuestionId === element.id" :active="chooseQuestionId === element.id"
></file-upload> ></file-upload>
@@ -59,6 +64,7 @@
<Rate <Rate
v-if="element.question_type === 5" v-if="element.question_type === 5"
:element="element" :element="element"
:index="index"
:active="chooseQuestionId === element.id" :active="chooseQuestionId === element.id"
sn="lXEBBpE2" sn="lXEBBpE2"
/> />
@@ -67,12 +73,14 @@
<TextWithImages <TextWithImages
v-if="element.question_type === 6" v-if="element.question_type === 6"
:element="element" :element="element"
:index="index"
:active="chooseQuestionId === element.id" :active="chooseQuestionId === element.id"
/> />
<!--图文--> <!--图文-->
<NPS <NPS
v-if="element.question_type === 106" v-if="element.question_type === 106"
:element="element" :element="element"
:index="index"
:active="chooseQuestionId === element.id" :active="chooseQuestionId === element.id"
/> />
<!--组件底部左侧操作--> <!--组件底部左侧操作-->

View File

@@ -7,12 +7,7 @@
class="base-select" class="base-select"
> >
<template #left-icon> <template #left-icon>
<div {{ index + 1 }}
class="van-filed"
:contenteditable="active"
@blur="saveStem($event, element, 'title')"
v-html="element.title"
/>
</template> </template>
<template #label> <template #label>
<contenteditable v-model="element.stem" :active="active"></contenteditable> <contenteditable v-model="element.stem" :active="active"></contenteditable>
@@ -93,6 +88,10 @@ const props = defineProps({
active: { active: {
type: Boolean, type: Boolean,
default: false default: false
},
index: {
type: Number,
default: 0
} }
}); });
@@ -100,9 +99,6 @@ const element = ref(props.element);
const saveOption = (e, ele) => { const saveOption = (e, ele) => {
ele.option = e.target.innerHTML; ele.option = e.target.innerHTML;
}; };
const saveStem = (e, ele, key) => {
ele[key] = e.target.innerHTML;
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.choice-html { .choice-html {

View File

@@ -6,6 +6,9 @@
:required="element.config.is_required === 1" :required="element.config.is_required === 1"
label-align="top" label-align="top"
> >
<template #left-icon>
{{ index + 1 }}
</template>
<template #label> <template #label>
<div <div
:contenteditable="active" :contenteditable="active"
@@ -30,6 +33,10 @@ const props = defineProps({
// 补充 // 补充
} }
}, },
index: {
type: Number,
default: 0
},
active: { active: {
type: Boolean, type: Boolean,
default: false default: false

View File

@@ -6,6 +6,9 @@
:required="element.config.is_required === 1" :required="element.config.is_required === 1"
label-align="top" label-align="top"
> >
<template #left-icon>
{{ index + 1 }}
</template>
<template #label> <template #label>
<div <div
:contenteditable="active" :contenteditable="active"
@@ -43,6 +46,10 @@ const props = defineProps({
element: { element: {
type: Object type: Object
}, },
index: {
type: Number,
default: 0
},
active: { active: {
type: Boolean, type: Boolean,
default: false default: false

View File

@@ -6,6 +6,9 @@
:required="element.config.is_required === 1" :required="element.config.is_required === 1"
label-align="top" label-align="top"
> >
<template #left-icon>
{{ index + 1 }}
</template>
<template #label> <template #label>
<div <div
:contenteditable="active" :contenteditable="active"
@@ -47,6 +50,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false default: false
}, },
index: {
type: Number,
default: 0
},
sn: { type: String, default: '' }, sn: { type: String, default: '' },
questionType: { type: [String, Number], default: 4 } questionType: { type: [String, Number], default: 4 }
}); });

View File

@@ -7,6 +7,9 @@
label-align="top" label-align="top"
class="base-select" class="base-select"
> >
<template #left-icon>
{{ index + 1 }}
</template>
<template #label> <template #label>
<contenteditable v-model="element.stem" :active="active"></contenteditable> <contenteditable v-model="element.stem" :active="active"></contenteditable>
<!-- <div v-html="element.stem" v-else></div>--> <!-- <div v-html="element.stem" v-else></div>-->
@@ -29,11 +32,13 @@ const props = defineProps({
active: { active: {
type: Boolean, type: Boolean,
default: false default: false
},
index: {
type: Number,
default: 0
} }
}); });
const element = ref(props.element); const element = ref(props.element);
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss"></style>
</style>

View File

@@ -66,8 +66,9 @@ import Design from '@/views/Design/Index.vue';
import { useCounterStore } from '@/stores/counter'; import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { radio, checkbox } from '@/utils/importJsons'; import { radio, checkbox, completion, rate, martrixQuestion } from '@/utils/importJsons';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
// 获取 Store 实例 // 获取 Store 实例
const counterStore = useCounterStore(); const counterStore = useCounterStore();
const store = storeToRefs(counterStore); const store = storeToRefs(counterStore);
@@ -108,52 +109,91 @@ const quesList = ref([
{ {
icon: 'star-o', icon: 'star-o',
name: '填空题', name: '填空题',
question_type: '3' question_type: '3',
}, json: completion
{
icon: 'phone-o',
name: '图形打分',
question_type: '4'
}, },
// {
// icon: 'phone-o',
// name: '图形打分',
// question_type: '4',
// json: rate
// },
{ {
icon: 'cart-o', icon: 'cart-o',
name: '数值打分', name: '数值打分',
question_type: '5' question_type: '4',
json: rate
}, },
{ {
icon: 'comment-o', icon: 'comment-o',
name: '矩阵单选', name: '矩阵单选',
question_type: '6' question_type: '8',
json: martrixQuestion
}, },
{ {
icon: 'bag-o', icon: 'bag-o',
name: '矩阵多选', name: '矩阵多选',
question_type: '7' question_type: '9',
json: martrixQuestion
}, },
{ {
icon: 'gift-o', icon: 'gift-o',
name: '矩阵填空', name: '矩阵填空',
question_type: '8' question_type: '10',
json: martrixQuestion
}, },
{ {
icon: 'bag-o', icon: 'bag-o',
name: '文件上传', name: '文件上传',
question_type: '9' question_type: '9'
},
{
icon: 'bag-o',
name: 'NPS',
question_type: '106'
} }
]); ]);
const questionEvent = (item) => { const questionEvent = (item) => {
let questionJson = {}; let questionJson = {};
switch (item.question_type) { // switch (item.question_type) {
// 单选 // // 单选 多选
case '1': // case '1':
case '2': // case '2':
//
//
// break;
// // 填空
// case '3':
// questionJson = JSON.parse(
// JSON.stringify({
// ...item.json,
// id: uuidv4(),
// question_index: questionInfo.value.survey.last_question_index + 1
// })
// );
// break;
// // 图形打分
// case '4':
// questionJson = JSON.parse(
// JSON.stringify({
// ...item.json,
// id: uuidv4(),
// question_index: questionInfo.value.survey.last_question_index + 1
// })
// );
// break
// }
questionJson = JSON.parse( questionJson = JSON.parse(
JSON.stringify({ JSON.stringify({
...item.json, ...item.json,
id: uuidv4(), id: uuidv4(),
question_type: Number(item.question_type),
question_index: questionInfo.value.survey.last_question_index + 1, question_index: questionInfo.value.survey.last_question_index + 1,
options: item.json.options.map((item) => { options:
item.json.options.length > 0
? item.json.options.map((item) => {
return item.map((it) => { return item.map((it) => {
return { return {
...it, ...it,
@@ -162,13 +202,9 @@ const questionEvent = (item) => {
}; };
}); });
}) })
: []
}) })
); );
questionInfo.value.questions.splice(activeQuestionIndex.value + 1, 0);
break;
}
// 给store 装数据 判断是 push 还是 splice // 给store 装数据 判断是 push 还是 splice
if (activeQuestionIndex.value === -1) { if (activeQuestionIndex.value === -1) {
questionInfo.value.questions.push(questionJson); questionInfo.value.questions.push(questionJson);