日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

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

發布時間:2023/12/2 vue 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 艾特某人代码实现_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;

}

}

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的艾特某人代码实现_Vue@某人,At某人,仿新浪微博@某人,@user,艾特,艾特某人...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。