feat(contenteditable): 添加占位符功能并优化编辑器
- 实现了 contenteditable 组件的占位符功能 - 优化了编辑器的初始化和变更处理逻辑 - 调整了编辑器样式,增加了最小高度 - 移除了不必要的模板代码
This commit is contained in:
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -16,6 +16,8 @@ declare module 'vue' {
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSpace: typeof import('element-plus/es')['ElSpace']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElText: typeof import('element-plus/es')['ElText']
|
||||
RichText: typeof import('./src/components/RichText.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
||||
@@ -255,3 +255,15 @@ input {
|
||||
color: #4b4b59 !important;
|
||||
font-weight: unset !important;
|
||||
}
|
||||
|
||||
.editor-placeholder {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: attr(data-placeholder);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none; // 确保占位符不会干扰用户输入
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
<template>
|
||||
<div class="flex contenteditable align-center space-between" :class="className">
|
||||
<p
|
||||
v-if="active"
|
||||
:id="'editor' + id"
|
||||
ref="editor"
|
||||
:contenteditable="active"
|
||||
class="van-field contenteditable-content"
|
||||
:data-placeholder="placeholder"
|
||||
@focus="onFocus"
|
||||
@input="onChange($event.target, $event)"
|
||||
v-html="modelValue"
|
||||
></p>
|
||||
<p v-else ref="editor" class="van-field contenteditable-content" v-html="modelValue"></p>
|
||||
<!-- <p-->
|
||||
<!-- v-else-->
|
||||
<!-- ref="editor"-->
|
||||
<!-- class="van-field contenteditable-content"-->
|
||||
<!-- v-html="modelValue"-->
|
||||
<!-- :placeholder="placeholder"-->
|
||||
<!-- ></p>-->
|
||||
<div class="right-icon ml10">
|
||||
<slot name="right-icon"></slot>
|
||||
</div>
|
||||
@@ -31,16 +38,22 @@ defineProps({
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入'
|
||||
}
|
||||
});
|
||||
const modelValue = defineModel('modelValue', {
|
||||
type: String,
|
||||
default: ''
|
||||
});
|
||||
|
||||
const active = defineModel('active', {
|
||||
type: Boolean,
|
||||
default: false
|
||||
});
|
||||
|
||||
const id = ref(uuidv4());
|
||||
|
||||
const editor = ref(null);
|
||||
@@ -62,7 +75,7 @@ const functions = {
|
||||
document.execCommand('italic', false, null);
|
||||
},
|
||||
|
||||
uploadImage: async() => {
|
||||
uploadImage: async () => {
|
||||
// 保存当前光标位置
|
||||
savedRange.value = saveSelection();
|
||||
|
||||
@@ -71,7 +84,7 @@ const functions = {
|
||||
|
||||
fileInput.click();
|
||||
|
||||
fileInput.onchange = async(e) => {
|
||||
fileInput.onchange = async (e) => {
|
||||
const [file] = e.target.files;
|
||||
if (!file) return;
|
||||
if (file.size > 2 * 1024 * 1024) {
|
||||
@@ -117,6 +130,39 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => modelValue.value,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
setTimeout(() => {
|
||||
onChange(editor.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const isEmptyContent = (html) => {
|
||||
// 移除所有空白字符
|
||||
const trimmedHtml = html.trim();
|
||||
// 检查是否为空字符串、仅包含 <br> 或仅包含 <p><br></p>
|
||||
return (
|
||||
trimmedHtml === '' ||
|
||||
trimmedHtml === '<br>' ||
|
||||
trimmedHtml === '<p><br></p>' ||
|
||||
trimmedHtml === '<p></p>'
|
||||
);
|
||||
};
|
||||
|
||||
const onChange = (target) => {
|
||||
console.log(target.innerHTML.trim(), 123);
|
||||
if (isEmptyContent(target.innerHTML)) {
|
||||
editor.value.classList.add('editor-placeholder');
|
||||
//删除br
|
||||
editor.value.innerHTML = editor.value.innerHTML.replace(/<br>/g, '');
|
||||
} else {
|
||||
editor.value.classList.remove('editor-placeholder');
|
||||
}
|
||||
};
|
||||
const showAction = ref(false);
|
||||
const actions = [
|
||||
{
|
||||
@@ -149,6 +195,7 @@ const checkContains = (element, target) => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onChange(editor.value);
|
||||
editor.value.addEventListener('focus', () => {
|
||||
showAction.value = true;
|
||||
});
|
||||
@@ -224,6 +271,7 @@ const insertImageAtCaret = (html) => {
|
||||
.contenteditable-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 25px;
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
|
||||
@@ -8,399 +8,8 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
export const useCommonStore = defineStore('common', {
|
||||
state: () => ({
|
||||
questionsInfo: {
|
||||
survey: {
|
||||
id: 9616,
|
||||
introduction:
|
||||
'<p><b>为优化活动服务品质,烦请完成问卷,感谢配合!您的反馈至关重要!(此提示语为默认提示语,您可选择自行输入本问卷的提示语)</b></p>',
|
||||
pages: [[7], [23], [24], [25], [26], [9], [13], [12], [11], [10]],
|
||||
sn: 'qxj894Ww',
|
||||
status: 0,
|
||||
title: '<b>报名签到问卷 </b>',
|
||||
detail_pages: [[7], [23], [24], [25], [26], [9], [13], [12], [11], [10]],
|
||||
group_pages: [],
|
||||
is_one_page_one_question: 1,
|
||||
last_question_index: 26,
|
||||
is_three_d_permissions: 0,
|
||||
is_dept: 1,
|
||||
last_title: 'Q4',
|
||||
project_name: '报名签到问卷 ',
|
||||
quota_end_content:
|
||||
'<p style="text-align:center"><img style="width:220px;margin-top:30px;margin-bottom: 40px;" src="https://cxp-pubcos.yili.com/prod-yls/theme/XxgQ98WN/1693807609602_962_error.png"></p>\n<p style="text-align:center;font-size: 16px;font-weight: 500;padding-bottom: 12px;/* margin-bottom: 10px; */">很抱歉,本次调研不太适合您的情况,感谢您的参与!</p>',
|
||||
quota_end_url: '',
|
||||
quota_end_url_select: 0,
|
||||
quota_standing_time: 0,
|
||||
screening_end_content:
|
||||
'<p style="text-align:center"><img style="width:220px;margin-top:30px;margin-bottom: 40px;" src="https://cxp-pubcos.yili.com/prod-yls/theme/XxgQ98WN/1693807609602_962_error.png"></p>\n<p style="text-align:center;font-size: 16px;font-weight: 500;padding-bottom: 12px;/* margin-bottom: 10px; */">很抱歉,本次调研不太适合您的情况,感谢您的参与!</p>',
|
||||
screening_end_url: '',
|
||||
screening_end_url_select: 0,
|
||||
screening_standing_time: 0,
|
||||
end_jump_url: '',
|
||||
end_jump_status: 0,
|
||||
end_jump_standing_time: 0,
|
||||
success_end_content:
|
||||
'<p style="text-align:center"><img style="width:220px;margin-top:30px;margin-bottom: 40px;" src="https://cxp-pubcos.yili.com/prod-yls/theme/XxgQ98WN/1693807609607_514_success.png"></p>\n<p style="text-align:center;font-size: 16px;font-weight: 500;padding-bottom: 12px;/* margin-bottom: 10px; */">您已完成本次调研,感谢您的参与!</p>\n<p style="text-align:center;color: #85b43a;font-size: 16px;font-weight: 550;">【成功完成】</p>',
|
||||
success_end_url: '',
|
||||
success_end_url_select: 0,
|
||||
success_standing_time: 0,
|
||||
template_type: 0,
|
||||
local_pages: [
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
},
|
||||
{
|
||||
pages: [],
|
||||
is_short_time: 0,
|
||||
short_time: '',
|
||||
is_show: 0,
|
||||
use_type: 0
|
||||
}
|
||||
]
|
||||
},
|
||||
logics: [
|
||||
{
|
||||
id: 472233,
|
||||
question_index: 10,
|
||||
sample_number: 0,
|
||||
skip_question_index: -1,
|
||||
skip_type: 0,
|
||||
question_id: '17853822',
|
||||
logic: [
|
||||
{
|
||||
value: '',
|
||||
location: 0,
|
||||
date: '',
|
||||
time: '',
|
||||
type: 0,
|
||||
row_type: 0,
|
||||
cell_type: 0,
|
||||
logic: 'if',
|
||||
operator: '=',
|
||||
is_answer: 1,
|
||||
is_select: 0,
|
||||
row_index: 0,
|
||||
cell_index: 0,
|
||||
question_type: 1,
|
||||
question_index: 7,
|
||||
relation_question_index: 0,
|
||||
relation_question_row_index: 0,
|
||||
relation_question_cell_index: 0,
|
||||
is_option_group: 0,
|
||||
option_index: 1,
|
||||
skip_type: null,
|
||||
question_id: null
|
||||
}
|
||||
],
|
||||
autofill: {
|
||||
value: '',
|
||||
date: '',
|
||||
time: '',
|
||||
question_type: 1,
|
||||
option_indexs: [],
|
||||
row_indexs: [],
|
||||
cell_indexs: []
|
||||
},
|
||||
hide_option_index: []
|
||||
},
|
||||
{
|
||||
id: 472236,
|
||||
question_index: 10,
|
||||
sample_number: 0,
|
||||
skip_question_index: 7,
|
||||
skip_type: 0,
|
||||
question_id: '17853822',
|
||||
logic: [
|
||||
{
|
||||
value: '',
|
||||
location: 0,
|
||||
date: '',
|
||||
time: '',
|
||||
type: 0,
|
||||
row_type: 0,
|
||||
cell_type: 0,
|
||||
logic: 'if',
|
||||
operator: '=',
|
||||
is_answer: 1,
|
||||
is_select: 1,
|
||||
row_index: 0,
|
||||
cell_index: 0,
|
||||
question_type: 2,
|
||||
question_index: 7,
|
||||
relation_question_index: 0,
|
||||
relation_question_row_index: 0,
|
||||
relation_question_cell_index: 0,
|
||||
is_option_group: 0,
|
||||
option_index: 1,
|
||||
skip_type: null,
|
||||
question_id: null
|
||||
}
|
||||
],
|
||||
autofill: {
|
||||
value: '',
|
||||
date: '',
|
||||
time: '',
|
||||
question_type: 1,
|
||||
option_indexs: [],
|
||||
row_indexs: [],
|
||||
cell_indexs: []
|
||||
},
|
||||
hide_option_index: []
|
||||
},
|
||||
{
|
||||
id: 472237,
|
||||
question_index: 10,
|
||||
sample_number: 0,
|
||||
skip_question_index: 13,
|
||||
skip_type: 0,
|
||||
question_id: '17853822',
|
||||
logic: [
|
||||
{
|
||||
value: '',
|
||||
location: 0,
|
||||
date: '',
|
||||
time: '',
|
||||
type: 0,
|
||||
row_type: 1,
|
||||
cell_type: 2,
|
||||
logic: 'if',
|
||||
operator: '=',
|
||||
is_answer: 1,
|
||||
is_select: 0,
|
||||
row_index: 2,
|
||||
cell_index: 3,
|
||||
question_type: 9,
|
||||
question_index: 24,
|
||||
relation_question_index: 0,
|
||||
relation_question_row_index: 0,
|
||||
relation_question_cell_index: 0,
|
||||
is_option_group: 0,
|
||||
option_index: 0,
|
||||
skip_type: null,
|
||||
question_id: null
|
||||
}
|
||||
],
|
||||
autofill: {
|
||||
value: '',
|
||||
date: '',
|
||||
time: '',
|
||||
question_type: 1,
|
||||
option_indexs: [],
|
||||
row_indexs: [],
|
||||
cell_indexs: []
|
||||
},
|
||||
hide_option_index: []
|
||||
},
|
||||
{
|
||||
id: 472240,
|
||||
question_index: 10,
|
||||
sample_number: 0,
|
||||
skip_question_index: 0,
|
||||
skip_type: 0,
|
||||
question_id: '17853822',
|
||||
logic: [
|
||||
{
|
||||
value: '8',
|
||||
location: 0,
|
||||
date: '',
|
||||
time: '',
|
||||
type: 0,
|
||||
row_type: 0,
|
||||
cell_type: 0,
|
||||
logic: 'if',
|
||||
operator: '=',
|
||||
is_answer: 1,
|
||||
is_select: 0,
|
||||
row_index: 0,
|
||||
cell_index: 0,
|
||||
question_type: 106,
|
||||
question_index: 26,
|
||||
relation_question_index: 0,
|
||||
relation_question_row_index: 0,
|
||||
relation_question_cell_index: 0,
|
||||
is_option_group: 0,
|
||||
option_index: 1,
|
||||
skip_type: null,
|
||||
question_id: null
|
||||
}
|
||||
],
|
||||
autofill: {
|
||||
value: '',
|
||||
date: '',
|
||||
time: '',
|
||||
question_type: 1,
|
||||
option_indexs: [],
|
||||
row_indexs: [],
|
||||
cell_indexs: []
|
||||
},
|
||||
hide_option_index: []
|
||||
},
|
||||
{
|
||||
id: 472241,
|
||||
question_index: 10,
|
||||
sample_number: 0,
|
||||
skip_question_index: 0,
|
||||
skip_type: 0,
|
||||
question_id: '17853822',
|
||||
logic: [
|
||||
{
|
||||
value: '213',
|
||||
location: 0,
|
||||
date: '',
|
||||
time: '',
|
||||
type: 0,
|
||||
row_type: 0,
|
||||
cell_type: 0,
|
||||
logic: 'if',
|
||||
operator: '=',
|
||||
is_answer: 1,
|
||||
is_select: 0,
|
||||
row_index: 0,
|
||||
cell_index: 0,
|
||||
question_type: 5,
|
||||
question_index: 25,
|
||||
relation_question_index: 0,
|
||||
relation_question_row_index: 0,
|
||||
relation_question_cell_index: 0,
|
||||
is_option_group: 0,
|
||||
option_index: 1,
|
||||
skip_type: null,
|
||||
question_id: null
|
||||
}
|
||||
],
|
||||
autofill: {
|
||||
value: '',
|
||||
date: '',
|
||||
time: '',
|
||||
question_type: 1,
|
||||
option_indexs: [],
|
||||
row_indexs: [],
|
||||
cell_indexs: []
|
||||
},
|
||||
hide_option_index: []
|
||||
}
|
||||
],
|
||||
survey: {},
|
||||
logics: [],
|
||||
questions: [],
|
||||
cycle_pages: null
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@search="blurs"
|
||||
></van-search>
|
||||
</div>
|
||||
<div class="new-survey-container container">
|
||||
<div class="new-survey-container">
|
||||
<van-list
|
||||
v-model:loading="loading"
|
||||
:finished="finished"
|
||||
|
||||
Reference in New Issue
Block a user