设计背景
最近客户提出要在输入框中实现类似微信@人 和发送截图功能,于是考虑到使用可编辑div来代替input输入框
使用效果
先上图,看效果
发送截图功能暂不展示,可参考实现
代码实现
template模板可编辑div及@部分
<div>
<div id="atInputComponents">
<div
ref="editDivElement"
id="editDivInput"
contenteditable="true"
@focus="focusHandler"
@blur="blurHandler"
@input="inputHandler"
@keydown.shift.exact.50="showAtDom"
@keydown.ctrl.enter="newLine"
@keydown.enter.exact="enterKey"
@paste.prevent="pasteIntercept"
@drop.prevent="onDrop"
></div>
<transition name="fade">
<div
class="atlist_dom"
ref="atlist"
v-show="atDomVisible"
:style="[atDomListPosition]"
>
<ul class="atstyle">
<li
v-for="(item, index) in atUserList"
:key="index"
@click="chooseAtItem(item)"
>
<el-avatar :class="item.class"></el-avatar>
{
{
item[props.label]
}}{
{
item[props.after] ? "(" + item[props.after] + ")" : "" }}
</li>
</ul>
</div>
</transition>
</div>
</div>
<p class="cbgols_btnpar"><el-button @click="send">发送</el-button></p>
首先简要说明一下div的绑定事件:
@focus=“focusHandler” 获取焦点事件
@blur="blurHandler"失去焦点事件
@input="inputHandler"输入事件
@keydown.shift.exact.50="showAtDom"按下键盘上的shift+2事件
shift vue的系统修饰键
exact 修饰符允许你控制由精确的系统修饰符组合触发的事件
50 2的键码
@keydown.ctrl.enter="newLine"换行事件
@keydown.enter.exact="enterKey"回车事件
@paste.prevent="pasteIntercept"粘贴事件
@drop.prevent="onDrop"拖拽至div中出发的事件
这里使用了vue 内置过度组件 transition 实现弹层过度效果
script部分
import Viewer from 'v-viewer'
import 'viewerjs/dist/viewer.css'
import '@/utils/jquery.caret'
import {
imgTurnBase64 } from '@/utils/Base64'
import request from '@/utils/request'
export default {
components: {
Viewer
},
data() {
return {
atDomVisible: false,
editorRange: null,
atDomListPosition: {
},
files: [], // DataTransfer 中的files
imgPreviewDialog: false,
imgBase64List: [],
viewerOptions: {
zIndex: 3000,
title: 0,
toolbar: {
zoomIn: 1,
zoomOut: 1,
oneToOne: 1,
reset: 1,
prev: {
show: 1,
size: 'large'
},
play: 0,
next: {
show: 1,
size: 'large'
},
rotateLeft: 1,
rotateRight: 1,
flipHorizontal: 1,
flipVertical: 1,
}
}
}
},
props: {
value: {
type: [String, Number]
},
placeholder: {
type: String,
default: ''
},
atUserList: {
type: Array,
default: () => {
return [];
}
},
// 是否开启粘贴发送截图功能
usePasteScreenshot: {
type: Boolean,
default: false
},
// 开启粘贴截图功能时的文件服务地址
uploadUrl: {
type: String,
default: ''
},
fileType: {
type: String,
default: 'png, jpg, jpeg'
},
props: {
type: Object,
default: () => {
return {
label: 'userName',
after: ''
}
}
}
},
computed: {
nativeDivValue() {
return this.value === null || this.value === undefined ? '' : String(this.value);
}
},
watch: {
nativeDivValue() {
this.setNativeDivValue