feat(components): 为 contenteditable组件添加错误消息显示

- 在 contenteditable.vue 中添加了 errorMessage属性和对应的样式
- 在多个组件中为 contenteditable 添加了 errorMessage 属性
- 优化了部分代码格式,如空格、换行等
This commit is contained in:
陈昱达
2025-03-20 18:28:39 +08:00
parent 289b865c51
commit bfa163925e
9 changed files with 109 additions and 50 deletions

View File

@@ -1,5 +1,6 @@
<template> <template>
<div class="flex contenteditable align-center space-between" :class="className"> <div :class="className">
<div class="flex contenteditable align-center space-between">
<p <p
:id="'editor' + id" :id="'editor' + id"
ref="editor" ref="editor"
@@ -21,12 +22,16 @@
<slot name="right-icon"></slot> <slot name="right-icon"></slot>
</div> </div>
</div> </div>
<div v-if="showAction && active" ref="editorAction" class="editor-action"> <div v-if="showAction && active" ref="editorAction" class="editor-action">
<button v-for="item in actions" :key="item.name" @click="funEvent(item, $event)"> <button v-for="item in actions" :key="item.name" @click="funEvent(item, $event)">
<van-icon class-prefix="mobilefont" :name="item.icon"></van-icon> <van-icon class-prefix="mobilefont" :name="item.icon"></van-icon>
</button> </button>
</div> </div>
<div class="error-message">
{{ errorMessage }}
<!-- <slot name="error"></slot>-->
</div>
</div>
</template> </template>
<script setup> <script setup>
@@ -49,6 +54,10 @@ const modelValue = defineModel('modelValue', {
default: '' default: ''
}); });
const errorMessage = defineModel('errorMessage', {
type: String,
default: ''
});
const active = defineModel('active', { const active = defineModel('active', {
type: Boolean, type: Boolean,
default: false default: false
@@ -75,7 +84,7 @@ const functions = {
document.execCommand('italic', false, null); document.execCommand('italic', false, null);
}, },
uploadImage: async() => { uploadImage: async () => {
// 保存当前光标位置 // 保存当前光标位置
savedRange.value = saveSelection(); savedRange.value = saveSelection();
@@ -84,7 +93,7 @@ const functions = {
fileInput.click(); fileInput.click();
fileInput.onchange = async(e) => { fileInput.onchange = async (e) => {
const [file] = e.target.files; const [file] = e.target.files;
if (!file) return; if (!file) return;
if (file.size > 2 * 1024 * 1024) { if (file.size > 2 * 1024 * 1024) {
@@ -169,10 +178,10 @@ const isEmptyContent = (html) => {
const trimmedHtml = html.trim(); const trimmedHtml = html.trim();
// 检查是否为空字符串、仅包含 <br> 或仅包含 <p><br></p> // 检查是否为空字符串、仅包含 <br> 或仅包含 <p><br></p>
return ( return (
trimmedHtml === '' trimmedHtml === '' ||
|| trimmedHtml === '<br>' trimmedHtml === '<br>' ||
|| trimmedHtml === '<p><br></p>' trimmedHtml === '<p><br></p>' ||
|| trimmedHtml === '<p></p>' trimmedHtml === '<p></p>'
); );
}; };
@@ -299,7 +308,11 @@ const insertImageAtCaret = (html) => {
.right-icon { .right-icon {
color: #c4c9d4; color: #c4c9d4;
} }
.error-message {
color: red;
flex: none;
font-size: 12px;
}
.editor-action { .editor-action {
position: fixed; position: fixed;
bottom: 0; bottom: 0;

View File

@@ -15,6 +15,7 @@
className="contenteditable-label" className="contenteditable-label"
:active="active" :active="active"
@blur="emitValue" @blur="emitValue"
:errorMessage="errorMessage"
> >
</contenteditable> </contenteditable>
</template> </template>
@@ -108,7 +109,7 @@ import { defineAsyncComponent } from 'vue';
const isPreview = defineModel('isPreview', { default: false, type: Boolean }); const isPreview = defineModel('isPreview', { default: false, type: Boolean });
const choiceValue = defineModel('answer', { default: '1', type: String }); const choiceValue = defineModel('answer', { default: '1', type: String });
const value = defineModel('checkboxAnswer', { default: [1, 2], type: Array }); const value = defineModel('checkboxAnswer', { default: [1, 2], type: Array });
const errorMessage = defineModel('errorMessage', { default: '', type: String });
const Contenteditable = defineAsyncComponent(() => import('@/components/contenteditable.vue')); const Contenteditable = defineAsyncComponent(() => import('@/components/contenteditable.vue'));
defineProps({ defineProps({
active: { active: {

View File

@@ -15,6 +15,7 @@
v-model="element.stem" v-model="element.stem"
:active="active" :active="active"
@blur="emitValue" @blur="emitValue"
:errorMessage="errorMessage"
></contenteditable> ></contenteditable>
</template> </template>
<template #input> <template #input>
@@ -50,6 +51,10 @@ const props = defineProps({
}, },
questionType: { type: [String, Number], default: 4 } questionType: { type: [String, Number], default: 4 }
}); });
const errorMessage = defineModel('errorMessage', {
type: String,
default: ''
});
// 创建一个本地副本以保存更改 // 创建一个本地副本以保存更改
const emit = defineEmits(['update:element']); const emit = defineEmits(['update:element']);
const { element } = toRefs(props); const { element } = toRefs(props);

View File

@@ -51,6 +51,11 @@ const { element } = toRefs(props);
// } // }
// } // }
const errorMessage = defineModel('errorMessage', {
type: String,
default: ''
});
const emit = defineEmits(['update:element']); const emit = defineEmits(['update:element']);
const emitValue = () => { const emitValue = () => {
emit('update:element', element.value); emit('update:element', element.value);
@@ -76,6 +81,7 @@ const emitValue = () => {
v-model="element.stem" v-model="element.stem"
:active="active" :active="active"
@blur="emitValue" @blur="emitValue"
:errorMessage="errorMessage"
></contenteditable> ></contenteditable>
</template> </template>

View File

@@ -49,6 +49,10 @@ const emitValue = () => {
// console.log(question.value); // console.log(question.value);
emit('update:element', question.value); emit('update:element', question.value);
}; };
const errorMessage = defineModel('errorMessage', {
type: String,
default: ''
});
</script> </script>
<template> <template>
@@ -64,7 +68,12 @@ const emitValue = () => {
</template> </template>
<!-- 使用 title 插槽来自定义标题 --> <!-- 使用 title 插槽来自定义标题 -->
<template #label> <template #label>
<contenteditable v-model="question.stem" :active="active" @blur="emitValue" /> <contenteditable
v-model="question.stem"
:active="active"
@blur="emitValue"
:errorMessage="errorMessage"
/>
</template> </template>
<template #input> <template #input>

View File

@@ -11,7 +11,12 @@
{{ index + 1 }} {{ index + 1 }}
</template> </template>
<template #label> <template #label>
<contenteditable v-model="element.stem" :active="active" @blur="saveStem"></contenteditable> <contenteditable
v-model="element.stem"
:active="active"
@blur="saveStem"
:errorMessage="errorMessage"
></contenteditable>
</template> </template>
<template #input> <template #input>
<div <div
@@ -43,7 +48,7 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'; import { defineModel, ref } from 'vue';
import RateCharacter from './RateCharacter.vue'; import RateCharacter from './RateCharacter.vue';
const value = defineModel('value', { default: -1, type: Number }); const value = defineModel('value', { default: -1, type: Number });
@@ -60,7 +65,10 @@ const isPreview = defineModel('isPreview', { default: false, type: Boolean });
sn: { type: String, default: '' }, sn: { type: String, default: '' },
questionType: { type: [String, Number], default: 4 } questionType: { type: [String, Number], default: 4 }
}); });
const errorMessage = defineModel('errorMessage', {
type: String,
default: ''
});
/** /**
* element === question * element === question
* @type {ModelRef<Object, string, Object, Object>} * @type {ModelRef<Object, string, Object, Object>}

View File

@@ -15,6 +15,7 @@
v-model="element.stem" v-model="element.stem"
:active="active" :active="active"
@blur="emitValue" @blur="emitValue"
:errorMessage="errorMessage"
></contenteditable> ></contenteditable>
</template> </template>
<template #input> <template #input>
@@ -49,7 +50,7 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'; import { defineModel, ref } from 'vue';
import RateCharacter from './RateCharacter.vue'; import RateCharacter from './RateCharacter.vue';
const isPreview = defineModel('isPreview', { default: false, type: Boolean }); const isPreview = defineModel('isPreview', { default: false, type: Boolean });
@@ -65,7 +66,10 @@ defineProps({
sn: { type: String, default: '' }, sn: { type: String, default: '' },
questionType: { type: [String, Number], default: 4 } questionType: { type: [String, Number], default: 4 }
}); });
const errorMessage = defineModel('errorMessage', {
type: String,
default: ''
});
const emit = defineEmits(['update:element']); const emit = defineEmits(['update:element']);
const chooseId = ref(''); const chooseId = ref('');

View File

@@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref, useTemplateRef, toRefs } from 'vue'; import { computed, onMounted, ref, useTemplateRef, toRefs } from 'vue';
import { defineModel } from 'vue/dist/vue';
const props = defineProps<{ const props = defineProps<{
element: any; element: any;
@@ -55,7 +56,10 @@ const togglePen = () => {
isEraser.value = !isEraser.value; isEraser.value = !isEraser.value;
setPenStyle(); setPenStyle();
}; };
const errorMessage = defineModel('errorMessage', {
type: String,
default: ''
});
onMounted(() => { onMounted(() => {
if (!signatureCanvas.value) return; if (!signatureCanvas.value) return;
// 将 canvas 宽度和窗口的宽度保持一致 // 将 canvas 宽度和窗口的宽度保持一致
@@ -181,7 +185,12 @@ const emitValue = () => {
{{ index + 1 }} {{ index + 1 }}
</template> </template>
<template #label> <template #label>
<contenteditable v-model="element.stem" :active="active" @blur="emitValue"></contenteditable> <contenteditable
v-model="element.stem"
:active="active"
@blur="emitValue"
:errorMessage="errorMessage"
></contenteditable>
</template> </template>
<template #input> <template #input>
<div class="sign-question"> <div class="sign-question">

View File

@@ -15,6 +15,7 @@
v-model="element.stem" v-model="element.stem"
:active="active" :active="active"
@blur="emitValue" @blur="emitValue"
:errorMessage="errorMessage"
></contenteditable> ></contenteditable>
</template> </template>
</van-field> </van-field>
@@ -22,7 +23,7 @@
</template> </template>
<script setup> <script setup>
import contenteditable from '@/components/contenteditable.vue'; import contenteditable from '@/components/contenteditable.vue';
import { toRefs } from 'vue'; import { defineModel, toRefs } from 'vue';
const props = defineProps({ const props = defineProps({
element: { element: {
type: Object, type: Object,
@@ -41,7 +42,10 @@ const props = defineProps({
default: 0 default: 0
} }
}); });
const errorMessage = defineModel('errorMessage', {
type: String,
default: ''
});
const { element } = toRefs(props); const { element } = toRefs(props);
const emit = defineEmits(['update:element']); const emit = defineEmits(['update:element']);