feat(contenteditable): 添加占位符功能并优化编辑器

- 实现了 contenteditable 组件的占位符功能
- 优化了编辑器的初始化和变更处理逻辑
- 调整了编辑器样式,增加了最小高度
- 移除了不必要的模板代码
This commit is contained in:
陈昱达
2025-03-19 18:56:28 +08:00
parent 0301e092ea
commit 0a6c2e75a7
5 changed files with 69 additions and 398 deletions

2
components.d.ts vendored
View File

@@ -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']

View File

@@ -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; // 确保占位符不会干扰用户输入
}
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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"