feat(component): 优化 contenteditable组件功能
- 添加 showAction 控制编辑按钮显示 - 实现文本域聚焦和失焦时的编辑按钮显示和隐藏 -优化键盘弹出和收起时的编辑按钮显示逻辑 -修复文档中描述的产品问卷配置- 优化问卷设计页面的题目编辑功能
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@@ -14,9 +14,12 @@ declare module 'vue' {
|
||||
VanButton: typeof import('vant/es')['Button']
|
||||
VanCell: typeof import('vant/es')['Cell']
|
||||
VanCellGroup: typeof import('vant/es')['CellGroup']
|
||||
VanCheck: typeof import('vant/es')['Check']
|
||||
VanCheckbo: typeof import('vant/es')['Checkbo']
|
||||
VanCheckbox: typeof import('vant/es')['Checkbox']
|
||||
VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
|
||||
VanCol: typeof import('vant/es')['Col']
|
||||
VanDialog: typeof import('vant/es')['Dialog']
|
||||
VanDivider: typeof import('vant/es')['Divider']
|
||||
VanField: typeof import('vant/es')['Field']
|
||||
VanIcon: typeof import('vant/es')['Icon']
|
||||
|
||||
@@ -1,39 +1,40 @@
|
||||
<template>
|
||||
<div
|
||||
ref="editor"
|
||||
contenteditable="true"
|
||||
:contenteditable="active"
|
||||
class="van-field"
|
||||
@focus="showToolbar"
|
||||
@blur="save"
|
||||
v-html="modelValue"
|
||||
></div>
|
||||
<div ref="editorAction" class="editor-action">
|
||||
<button v-for="item in actions" :key="item.name" @click="funEvent(item)">{{ item.label }}</button>
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineEmits, ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { defineEmits, ref, onMounted, watch } from 'vue';
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const editor = ref(null);
|
||||
const editorAction = ref(null);
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
let lastHeight = window.innerHeight;
|
||||
|
||||
const save = (e) => {
|
||||
emit('update:modelValue', e.target.innerHTML);
|
||||
emit('update:modelValue', e.innerHTML);
|
||||
};
|
||||
|
||||
const functions = {
|
||||
// todo 点击按钮之后 如何判断 才能让按钮再次点击 不消失 获取重新聚焦 再次选中文本?
|
||||
boldModern: () => {
|
||||
document.execCommand('bold', false, null);
|
||||
document.execCommand('bold', true, null);
|
||||
},
|
||||
underLine: () => {
|
||||
document.execCommand('underline', false, null);
|
||||
@@ -47,6 +48,21 @@ const funEvent = (item) => {
|
||||
functions[item.fun]();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.active,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
showAction.value = true;
|
||||
} else {
|
||||
save(editor.value);
|
||||
setTimeout(() => {
|
||||
showAction.value = false;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const showAction = ref(false);
|
||||
const actions = [
|
||||
{
|
||||
label: '加粗',
|
||||
@@ -69,31 +85,19 @@ const actions = [
|
||||
}
|
||||
];
|
||||
|
||||
const showToolbar = () => {
|
||||
editorAction.value.style.display = '';
|
||||
};
|
||||
|
||||
const handleResize = () => {
|
||||
const currentHeight = window.innerHeight;
|
||||
if (currentHeight < lastHeight) {
|
||||
// 键盘弹出
|
||||
editorAction.value.style.display = '';
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
// 键盘收起
|
||||
editorAction.value.style.display = 'none';
|
||||
}, 100);
|
||||
}
|
||||
lastHeight = currentHeight;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
editor.value.addEventListener('focus', () => {
|
||||
showAction.value = true;
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
document.addEventListener('resize', () => {
|
||||
showAction.value = false;
|
||||
});
|
||||
});
|
||||
|
||||
// onBeforeUnmount(() => {
|
||||
// editor.value.removeEventListener('resize', handleResize);
|
||||
// });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -797,7 +797,7 @@ export const useCommonStore = defineStore('common', {
|
||||
stem: '<p>请问,您的实足年龄在以下哪个区间呢?指的是您上一次过生日时的年龄</p>',
|
||||
other: '',
|
||||
question_index: 27,
|
||||
question_type: 1,
|
||||
question_type: 6,
|
||||
config: {
|
||||
placeholder: '',
|
||||
version: '',
|
||||
@@ -1097,7 +1097,7 @@ export const useCommonStore = defineStore('common', {
|
||||
stem: '<p><span style="font-family: \'PingFang SC Regular\';">假设介绍中描述的产品在您平时购物的商店或网站销售的话,以下哪句话最能描述您为自己或家人<span style="text-decoration: underline;"><strong>购买</strong><strong>这款产品A的可能性</strong></span>呢?</span></p>\n<p><img style="height: 50px; max-height: 440px;" src="https://test-cxp-public-web-1302259445.cos.ap-beijing.myqcloud.com/uat-yls/packing/imgs/1725421981418_809_mao-23.jpg" /></p>',
|
||||
other: '概念诊断问卷配置概念诊断1',
|
||||
question_index: 1,
|
||||
question_type: 1,
|
||||
question_type: 6,
|
||||
config: {
|
||||
placeholder: '',
|
||||
version: '',
|
||||
|
||||
@@ -196,8 +196,8 @@ const getSkipTypeText = (skipType) => {
|
||||
const ls = [];
|
||||
logics.map((item) => {
|
||||
if (
|
||||
item.skip_type === skipType &&
|
||||
item.question_index === activeQuestion.value.question_index
|
||||
item.skip_type === skipType
|
||||
&& item.question_index === activeQuestion.value.question_index
|
||||
) {
|
||||
ls.push(item);
|
||||
}
|
||||
|
||||
@@ -108,9 +108,9 @@ function isSurplus() {
|
||||
return false;
|
||||
} else {
|
||||
return (
|
||||
parseFloat(((localConfig.value.max - localConfig.value.min) * 1000).toFixed(4, 10)) %
|
||||
parseFloat((localConfig.value.score_interval * 1000).toFixed(4, 10)) ===
|
||||
0
|
||||
parseFloat(((localConfig.value.max - localConfig.value.min) * 1000).toFixed(4, 10))
|
||||
% parseFloat((localConfig.value.score_interval * 1000).toFixed(4, 10))
|
||||
=== 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,16 @@
|
||||
label-align="top"
|
||||
class="base-select"
|
||||
>
|
||||
<template #label>
|
||||
<template #left-icon>
|
||||
<div
|
||||
class="van-filed"
|
||||
v-html="element.title"
|
||||
:contenteditable="active"
|
||||
class="van-field"
|
||||
@blur="saveStem($event, element)"
|
||||
v-html="element.stem"
|
||||
></div>
|
||||
@blur="saveStem($event, element, 'title')"
|
||||
/>
|
||||
</template>
|
||||
<template #label>
|
||||
<contenteditable v-model="element.stem" :active="active"></contenteditable>
|
||||
</template>
|
||||
<template #input>
|
||||
<template v-for="(item, index) in element.options" :key="index">
|
||||
@@ -76,7 +79,8 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import OptionAction from '@/views/Design/components/ActionCompoents/OptionAction.vue';
|
||||
import { ref } from 'vue';
|
||||
import { ref, defineAsyncComponent } from 'vue';
|
||||
const Contenteditable = defineAsyncComponent(() => import('@/components/contenteditable.vue'));
|
||||
const props = defineProps({
|
||||
element: {
|
||||
type: Object,
|
||||
@@ -96,8 +100,8 @@ const element = ref(props.element);
|
||||
const saveOption = (e, ele) => {
|
||||
ele.option = e.target.innerHTML;
|
||||
};
|
||||
const saveStem = (e, ele) => {
|
||||
ele.stem = e.target.innerHTML;
|
||||
const saveStem = (e, ele, key) => {
|
||||
ele[key] = e.target.innerHTML;
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
class="base-select"
|
||||
>
|
||||
<template #label>
|
||||
<contenteditable v-model="element.stem"></contenteditable>
|
||||
<contenteditable v-model="element.stem" :active="active"></contenteditable>
|
||||
<!-- <div v-html="element.stem" v-else></div>-->
|
||||
</template>
|
||||
</van-field>
|
||||
</div>
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
class="iconfont active-icon"
|
||||
:style="{ marginRight: isLastPage ? '0' : '16px' }"
|
||||
@click="activePage"
|
||||
></i
|
||||
>
|
||||
></i>
|
||||
<template v-if="!isLastPage">
|
||||
<i class="iconfont moverQues" style="margin-right: 16px"></i>
|
||||
<i class="iconfont" @click="deleteHandle"></i>
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
<div v-for="item in 10" :key="item" class="template">
|
||||
<img src="https://picsum.photos/131/128" width="110" height="100" alt="" />
|
||||
<span>报名/签到模板</span>
|
||||
<span style="color: rgb(127, 127, 127)"
|
||||
>报名签到 | 引用 {{ item }} 次 | 创建人: {{ '张三' }}</span
|
||||
>
|
||||
<span style="color: rgb(127, 127, 127)">报名签到 | 引用 {{ item }} 次 | 创建人: {{ '张三' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user