艾特某人代码实现_Vue@某人,At某人,仿新浪微博@某人,@user,艾特,艾特某人...

atuser.vue

  • 展开更多群成员

import getCaretCoordinates from 'textarea-caret'

export default {

props: {

value: { //输入框初始值

type: String,

default: null

},

suffix: { //插入字符链接

type: String,

default: ' '

},

loop: { //上下箭头循环

type: Boolean,

default: true

},

avoidEmail: { //@前不能是字符

type: Boolean,

default: true

},

hoverSelect: { //悬浮选中

type: Boolean,

default: true

},

members: { //选择框选项列表

type: Array,

default: () => []

},

nameKey: {

type: String,

default: ''

}

},

data() {

return {

atItems: ['@'],

bindsValue: this.value != null,

atwho: null

}

},

computed: {

style() {

if(this.atwho) {

const {

list,

cur,

x,

y

} = this.atwho

const {

wrap

} = this.$refs

const el = this.$el.querySelector('textarea')

if(wrap) {

const left = x + el.offsetLeft - el.scrollLeft + 'px'

const top = y + el.offsetTop - el.scrollTop + 25 + 'px'

return {

left,

top

}

}

}

return null

}

},

watch: {

members() {

this.handleInput(true)

},

value(value, oldValue) {

if(this.bindsValue) {

this.handleValueUpdate(value)

}

}

},

mounted() {

if(this.bindsValue) {

this.handleValueUpdate(this.value)

}

},

methods: {

getAtAndIndex(text, ats) {

return ats.map((at) => {

return {

at,

index: text.lastIndexOf(at)

}

}).reduce((a, b) => {

return a.index > b.index ? a : b

})

},

isCur(index) {

return index === this.atwho.cur

},

handleValueUpdate(value) { //更新textarea的值

const el = this.$el.querySelector('textarea')

if(value !== el.value) {

el.value = value

}

},

handleItemHover(e) {

if(this.hoverSelect) {

this.selectByMouse(e)

}

},

handleItemClick(e) {

this.selectByMouse(e)

this.insertItem()

},

handleKeyDown(e) {

const {

atwho

} = this

if(atwho) {

if(e.keyCode === 38 || e.keyCode === 40) { // ↑/↓

if(!(e.metaKey || e.ctrlKey)) {

e.preventDefault()

e.stopPropagation()

this.selectByKeyboard(e)

}

return

}

if(e.keyCode === 13) { // enter

this.insertItem()

e.preventDefault()

e.stopPropagation()

return

}

if(e.keyCode === 27) { // esc

this.closePanel()

return

}

}

// 为了兼容ie ie9~11 editable无input事件 只能靠keydown触发 textarea正常

// 另 ie9 textarea的delete不触发input

const isValid = e.keyCode >= 48 && e.keyCode <= 90 || e.keyCode === 8

if(isValid) {

setTimeout(() => {

this.handleInput()

}, 50)

}

if(e.keyCode === 8) { //删除

//this.handleDelete(e)

}

if(e.keyCode === 13) { //删除

this.$emit("enterSend",e)

}

},

handleInput(event) {

const el = this.$el.querySelector('textarea')

this.$emit('input', el.value) //更新父组件

const text = el.value.slice(0, el.selectionEnd)

if(text) {

const {

atItems,

avoidEmail

} = this

let show = true

const {

at,

index

} = this.getAtAndIndex(text, atItems)

if(index < 0) show = false

const prev = text[index - 1] //上一个字符

const chunk = text.slice(index + at.length, text.length)

if(avoidEmail) { //上一个字符不能为字母数字 避免与邮箱冲突,微信则是避免 所有字母数字及半角符号

if(/^[a-z0-9]$/i.test(prev)) show = false

}

if(/^\s/.test(chunk)) show = false //chunk以空白字符开头不匹配 避免`@ `也匹配

if(!show) {

this.closePanel()

} else {

const {

members,

filterMatch

} = this

if(!event) { // fixme: should be consistent with At.vue

this.$emit('at', chunk)

}

const matched = members.filter(v => {

return v.toString().indexOf(chunk) > -1

})

if(matched.length) {

this.openPanel(matched, chunk, index, at)

} else {

this.closePanel()

}

}

} else {

this.closePanel()

}

},

closePanel() {

if(this.atwho) {

this.atwho = null

}

},

openPanel(list, chunk, offset, at) { //打开Atuser列表 matched, chunk, index, at 过滤数组,匹配项,匹配项index,'@'

const fn = () => {

const el = this.$el.querySelector('textarea')

const atEnd = offset + at.length // 从@后第一位开始

const rect = getCaretCoordinates(el, atEnd)

this.atwho = {

chunk,

offset,

list,

atEnd,

x: rect.left,

y: rect.top - 4,

cur: 0, // todo: 尽可能记录

}

}

if(this.atwho) {

fn()

} else { // 焦点超出了显示区域 需要提供延时以移动指针 再计算位置

setTimeout(fn, 10)

}

},

selectByMouse(e) {

function closest(el, predicate) { //遍历直到有data-index为止

do {

if(predicate(el)) return el;

} while (el = el && el.parentNode);

}

const el = closest(e.target, d => {

return d.getAttribute('data-index')

})

const cur = +el.getAttribute('data-index')

this.atwho = {

...this.atwho,

cur

}

},

selectByKeyboard(e) {

const offset = e.keyCode === 38 ? -1 : 1

const {

cur,

list

} = this.atwho

const nextCur = this.loop ?

(cur + offset + list.length) % list.length :

Math.max(0, Math.min(cur + offset, list.length - 1))

this.atwho = {

...this.atwho,

cur: nextCur

}

},

// todo: 抽离成库并测试

insertText(text, el) {

const start = el.selectionStart

const end = el.selectionEnd

el.value = el.value.slice(0, start) +

text + el.value.slice(end)

const newEnd = start + text.length

el.selectionStart = newEnd

el.selectionEnd = newEnd

},

insertItem() {

const {

chunk,

offset,

list,

cur,

atEnd

} = this.atwho

const {

suffix,

atItems

} = this

const el = this.$el.querySelector('textarea')

const text = el.value.slice(0, atEnd)

const {

at,

index

} = this.getAtAndIndex(text, atItems)

const start = index + at.length // 从@后第一位开始

el.selectionStart = start

el.focus() // textarea必须focus回来

const curItem = list[cur]

const t = '' + curItem + suffix

this.insertText(t, el)

this.$emit('insert', curItem) //插入字符

this.handleInput()

}

}

}

.atwho-wrap {

width: 100%;

font-size: 12px;

color: #333;

position: relative;

.atwho-panel {

position: absolute;

&.test {

width: 2px;

height: 2px;

background: red;

}

.atwho-inner {

position: relative;

}

}

.atwho-view {

color: black;

z-index: 11110 !important;

border-radius: 2px;

box-shadow: 0 0 10px 0 rgba(101, 111, 122, .5);

position: absolute;

cursor: pointer;

background-color: rgba(255, 255, 255, .94);

width: 170px;

max-height: 312px;

&::-webkit-scrollbar {

width: 11px;

height: 11px;

}

&::-webkit-scrollbar-track {

background-color: #F5F5F5;

}

&::-webkit-scrollbar-thumb {

min-height: 36px;

border: 2px solid transparent;

border-top: 3px solid transparent;

border-bottom: 3px solid transparent;

background-clip: padding-box;

border-radius: 7px;

background-color: #C4C4C4;

}

}

.atwho-ul {

list-style: none;

padding: 0;

margin: 0;

li {

box-sizing: border-box;

display: block;

height: 25px;

padding: 2px 10px;

white-space: nowrap;

display: flex;

align-items: center;

justify-content: space-between;

&.atwho-cur {

background: #f2f2f5;

color: #eb7350;

}

span {

overflow: hidden;

text-overflow: ellipsis;

}

img {

height: 13px;

width: 13px;

}

}

}

}

index.vue

import at from './atuser.vue'

export default {

data() {

return {

members: [123, 12, 1234, 12345, "小花", "小花华", "小三"],

inputcontent: "" //用户输入内容初始值

};

},

components: {

at,

},

methods: {

send(e) { //回车发送

console.log(e)

}

}

}

.atuser {

width: 700px;

height: 160px;

border: 1px solid red;

.editor{

width: 700px;

height: 160px;

overflow: hidden;

border: 0px;

outline: none;

resize: none;

-webkit-appearance: none;

}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/340937.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring batch_Spring Batch作为Wildfly模块

spring batch长期以来&#xff0c;Java EE规范缺少批处理API。 今天&#xff0c;这对于企业应用程序来说是必不可少的。 这是最后固定与JSR-352批处理应用程序的Java平台现在的Java EE提供7 JSR-352得到了它的的灵感Spring Batch的对手。 两者涵盖相同的概念&#xff0c;尽管生…

h3c 链路聚合测试_良无磐石固,虚名复何益?- 链路聚合协议互通测试

一、描述链路聚合是将多条链路聚合在一起形成一个汇聚组&#xff0c;以实现负荷在各成员链路中的分担&#xff0c;同时也提供了更高的连接可靠性。Cisco的链路聚合有几种方式&#xff1a;手工、LACP(包括active和passive两种)、PAgP(Port-aggregation Protocol)&#xff0c;其中…

Spring@懒惰注释

介绍&#xff1a; 默认情况下&#xff0c; Spring框架在应用程序启动时加载并热切初始化所有bean。 在我们的应用程序中&#xff0c;我们可能有一些非常消耗资源的bean。 我们宁愿根据需要加载此类bean。 我们可以使用Spring Lazy批注实现此目的 。 在本教程中&#xff0c;我们…

变压器符号_行输出变压器的结构、符号及电路分析

行输出变压器又称逆程变压器、回扫变压器&#xff0c;俗称行输出&#xff0c;它是电视机、显示器中的一个重要变压器。1.行输出变压器结构行输出变压器的全部绕组和高压整流管均密封在其中&#xff0c;底部引出各个绕组的引脚&#xff0c;高压输出采用高压引线直接送至显像管的…

西南医院微服务咋查得到_#全国最好医院排行榜#发布:成都这4家牛了!四川31个专科排全国前十(总榜)...

病急不能乱投医&#xff01;近日&#xff0c;一年一度、令人期待的——复旦版《2018年度中国医院排行榜&#xff08;总榜&#xff09;》全新揭晓了~这份榜单不仅是是中国医院学科建设的一个标杆也是大众疑难杂症寻找好医院的重要参考四川4家医院入百强&#xff01;华西全国第二…

jit 和 jvm_关于JVM和JIT的一点点

jit 和 jvm如您所知&#xff0c;JVM&#xff08;Java Virtusal Machine&#xff09;使Java能够遵循“一次写入&#xff0c;随处运行”的范例。 JVM的核心包括以下组件&#xff1a; 堆 叠放 PermGen和方法区域 JIT编译器 代码缓存 堆是在应用程序代码开发阶段为您使用的每…

voxelnet_ue4商城资源Voxel Sandbox Toolkit体素沙盒工具箱

Unreal Engine虚幻游戏引擎素材资源 Unreal Engine Marketplace –Voxel Sandbox Toolkit体素沙盒工具箱体素沙盒工具箱是一个新的改进版本的体素生成从2016年适应4.22的需要。它用不同类型的立方体、树和拾取网格生成无限世界。因为额外的代码实现比以前的版本快得多。理想的创…

Pub / Sub本地模拟器

发布/订阅是GCP提供的不错的工具。 它非常方便&#xff0c;可以帮助您解决应用程序可能面临的消息传递难题。 实际上&#xff0c;如果您使用GCP&#xff0c;则可以使用托管消息解决方案。 如预期的那样&#xff0c;使用实际的发布/订阅解决方案需要一定的配额&#xff0c;因此…

mysql 写入随机字中文符_MySQL产生随机字符

MySQL产生随机字符UUID简介UUID含义是通用唯一识别码 (Universally Unique Identifier)&#xff0c;这是一个软件建构的标准&#xff0c;也是被开源软件基金会 (Open Software Foundation, OSF) 的组织在分布式计算环境 (Distributed Computing Environment, DCE) 领域的一部份…

mysql建表时外检怎么创建_MySQL创建表时加入的约束以及外键约束的的意义

1&#xff0c;创建表时加入的约束a) 非空约束&#xff0c;not nullb) 唯一约束&#xff0c;uniquec) 主键约束&#xff0c;primary keyd) 外键约束&#xff0c;foreign key1&#xff0c;非空约束&#xff0c;针对某个字段设置其值不为空&#xff0c;如&#xff1a;学生的姓名不…

minecraft_MineCraft和堆外内存

minecraft总览 MineCraft是一个很好的例子&#xff0c;说明何时使用堆外内存确实可以提供帮助。 关键要求是&#xff1a; 保留的数据大部分是一个简单的数据结构&#xff08;在Minecraft的情况下&#xff0c;其很多字节[]&#xff09; 堆外内存的使用可以隐藏在抽象中。 考试…

win2008 mysql_mysql5.7.17在win2008R2的64位系统安装与配置实例

脚本之家已经给大家讲解过MYSQL其他版本在各种环境中的安装过程&#xff0c;大家可以参阅正文下面的相关文章&#xff0c;今天一起来学习下mysql5.7.17的实例安装教学&#xff0c;配置上稍微不同&#xff0c;希望能够帮助到你。安装MySql操作系统&#xff1a;Windows Server 20…

结构性错误

团队成员在使用以下代码时遇到了麻烦&#xff1a; void extractData(String targetUri) { Path tempFile createTempFilePath(); extractDataToPathAndUpload(tempFile, targetUri); cleanUp(tempFile); } void extractDataToPathAndUpload(Path tempFile, String targetUr…

mysql小计_使用SQL实现小计,合计以及排序_MySQL

bitsCN.com--说明&#xff1a;个人学习笔记&#xff0c;实现小计合计显示&#xff0c;分组按BANK_IDOP_DATE升序排序--测试数据CREATE TABLE #TB(ID VARCHAR(10),BANK_ID VARCHAR(10),OP_DATE VARCHAR(10),OPERATOR_NO VARCHAR(20),AMT DECIMAL(10,2))INSERT #TB SELECT 1111,0…

mysql函数封装_PHP访问MYSQL数据库封装类(附函数说明)

/*MYSQL 数据库访问封装类MYSQL 数据访问方式&#xff0c;php4支持以mysql_开头的过程访问方式&#xff0c;php5开始支持以mysqli_开头的过程和mysqli面向对象访问方式&#xff0c;本封装类以mysql_封装数据访问的一般流程&#xff1a;1,连接数据库 mysql_connect or mysql_pco…

mule和activemq_Mule ESB,ActiveMQ和DLQ

mule和activemq在本文中&#xff0c;我将展示一个简单的Mule ESB流程&#xff0c;以了解实际中使用的Active MQ 的DLQ功能 。 我假设您有一个正在运行的Apache ActiveMQ实例&#xff08;如果没有&#xff0c;则可以在此处下载一个版本&#xff09;。 在此示例中&#xff0c;我…

MySQL倒序如何避免filesort_如何避免mysql查询的filesort?

我用不同的参数使用这种查询&#xff1a;EXPLAIN SELECT SQL_NO_CACHE ilan_genel.id , ilan_genel.durum , ilan_genel.kategori , ilan_genel.tip , ilan_genel.ozellik , ilan_genel.m2 , ilan_genel.fiyat , ilan_genel.baslik , ilan_genel.ilce , ilan_genel.parabirimi …

在Java中键入Safe SQL

字符串&#xff0c;字符串&#xff0c;字符串 无论您使用的是JPA &#xff0c; MyBatis还是Spring Data JDBC之类的框架&#xff0c;最终都将SQL语句声明为Java String。 这种方法的问题在于&#xff0c;您必须为每个语句编写测试&#xff0c;以确保它甚至是有效的SQL。 没有编…

springmvc怎么设置更改了界面不用重启_Microsoft Visual Studio 2019 更改语言包

第一次写文章&#xff0c;如有格式等问题&#xff0c;请多多包含与指点&#xff01;很多人在编程时更喜欢英文界面&#xff0c;认为与编程语言具有相同的语境&#xff0c;更顺手。但是&#xff0c;在更改环境显示语言时&#xff0c;可能会遇到一些障碍&#xff0c;例如我之前在…

苹果电脑怎么删除软件_误格式化,删除文件怎么恢复?3款最好用的数据恢复软件推荐...

1、EasyRecovery一款威力非常强大的硬盘数据恢复工具&#xff0c;能够恢复丢失的数据以及重建文件系统。主要体现在可以从被病毒破坏或是已经格式化的硬盘中恢复数据。EasyRecovery在使用过程中不会在原始的驱动器中写入任何东西&#xff0c;其主要是在内存中重建文件分区表使数…