<template>
<div class="auto-container" :id="id">
<el-input type="textarea" v-model="text" @input="handleChange" @change="changeVal" class="autoInput"></el-input>
<div class="fix-el" ref="fixEl" :style="styleObj" v-show="show">
<div class="auto-item" :class="{check:item.check}" v-for="item in showOptions"
:key="item.variableCode" @click="itemClick(item)">{{ item.variableCode }}<span>({{item.variableName }})</span></div>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type:String,
default:''
}
},
model: {
prop: 'value',
event:'change'
},
data() {
return {
text: '',
saveText: '',
lastText: '',
styleObj: {
top: '',
left:''
},
str: '',
options: [],
show: false,
showOptions: [],
isEnd: true,
cursorPos: 0,
id:'auto-'+Date.now()
}
},
watch: {
value(nl, ol) {
this.text = nl
}
},
created() {
this.text = this.value
this.getVariable()
document.addEventListener('keydown',this.handleKeybord)
},
methods: {
changeVal(data) {
this.$emit('change', data)
},
handleChange(data) {
let pos = this.getCursorPos(data)
this.styleObj.left = pos.x +'px'
this.styleObj.top = pos.y + 'px'
let str = data
if (!this.isEnd) {
str = data.slice(0,this.cursorPos)
}
this.getLastStr(str)
},
getCursorPos(text) {
let textarea = document.querySelector(`#${this.id} textarea`)
// 1. 光标在末尾
// 2. 光标在中间
this.cursorPos = textarea.selectionStart
this.isEnd = this.cursorPos == this.text.length
if (!this.isEnd) {
text = text.slice(0, this.cursorPos)
}
// 获取textarea内容宽度
let contentHeight = textarea.offsetHeight
let paddingLeft = getComputedStyle(textarea)['paddingLeft'].replace('px', '') / 1
let paddingRight = getComputedStyle(textarea)['paddingRight'].replace('px', '') / 1
let paddingTop = getComputedStyle(textarea)['paddingTop'].replace('px', '') / 1
let lineHeight = getComputedStyle(textarea)['lineHeight'].replace('px', '') / 1
let font = getComputedStyle(textarea)['font']
// 下拉框距离文字间距
let offset = 5
let contentWidth = textarea.offsetWidth - paddingLeft - paddingRight
var canvas = document.createElement('canvas')
var context = canvas.getContext('2d');
context.font = font;
let x = paddingLeft, y = lineHeight+paddingTop;
let total = context.measureText(text).width;
// 文本不超过一行
if (total < contentWidth) {
return {y:lineHeight+offset + paddingTop,x:total+ paddingLeft }
} else {
// 多行文本
var arrText = text.split('');
// 最后一行文本
var line = '';
for (var n = 0; n < arrText.length; n++) {
var testLine = line + arrText[n];
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > contentWidth && n > 0) {
line = arrText[n];
y += lineHeight;
} else {
line = testLine;
}
}
x = context.measureText(line).width
y = y> contentHeight? contentHeight+ offset :y
return {x,y}
}
},
getLastStr(data) {
// 获取最后输入的英文单词
let match = data.match(/[a-zA-Z]+$/)
if (match) {
this.str = match[0]
}
if (!data || !match) {
this.showOptions = []
} else {
this.showOptions = this.options.filter(item => this.str && item.variableCode&&item.variableCode.includes(this.str))
}
this.showOptions.forEach((item,index) => item.check=index==0)
this.show = this.showOptions.length > 0
setTimeout(() => {
let fixEl = document.querySelector(`#${this.id} .fix-el`)
fixEl.scrollTop = 0
})
},
handleKeybord(e) {
let arrowMap = ['ArrowDown','ArrowLeft','ArrowUp','ArrowRight']
// 有下拉框时禁用方向键
if (arrowMap.includes(e.code) && this.show) {
let i = this.showOptions.findIndex(item=>item.check)
if (e.code == 'ArrowDown') {
this.showOptions.map((item, index) => {
if (i == this.showOptions.length - 1) {
item.check = index==i
} else {
item.check = index == i+1
}
})
this.scroll(true)
}
if (e.code == 'ArrowUp') {
this.showOptions.map((item, index) => {
if (i == 0) {
item.check = index == i
} else {
item.check = index == i - 1
}
})
this.scroll(false)
}
e.preventDefault()
}
// 按下tab 或者回车
if ((e.code == 'Tab' || e.code=='Enter')&&this.show) {
this.joinText()
e.preventDefault()
}
},
scroll(type) {
let fixEl = document.querySelector(`#${this.id} .fix-el`)
let checkItem = document.querySelector(`#${this.id} .auto-item.check`)
if(!checkItem) return
if (type) {
if (fixEl.scrollTop + fixEl.offsetHeight < checkItem.offsetHeight + checkItem.offsetTop+20) {
fixEl.scrollTop = checkItem.offsetHeight + checkItem.offsetTop
}
} else {
if (Math.abs(fixEl.scrollTop - checkItem.offsetTop) < checkItem.offsetHeight) {
fixEl.scrollTop = checkItem.offsetTop-fixEl.offsetHeight+checkItem.offsetHeight
}
}
},
joinText() {
let item = this.showOptions.find(item => item.check)
if (this.isEnd) {
this.text = this.text.replace(/[a-zA-Z]+$/, '')
this.text += item.variableCode
} else {
let beforeText = this.text.slice(0, this.cursorPos)
let afterText = this.text.slice(this.cursorPos)
beforeText = beforeText.replace(/[a-zA-Z]+$/, '') + item.variableCode
this.text = beforeText + afterText
setTimeout(() => {
let textarea = document.querySelector(`#${this.id} textarea`)
textarea.focus()
textarea.selectionStart = textarea.selectionEnd= beforeText.length
})
}
this.showOptions = []
this.show = false
},
itemClick(item) {
item.check = true
this.joinText()
},
async getVariable() {
let res = await queryVariableList({ formulaType: [1] })
if (res) {
this.options = res.map(item => {
return {
variableCode: item.variableCode,
variableName: item.variableName,
check:false
}
})
}
}
}
}
</script>
<style lang="less">
.auto-container {
position: relative;
.autoInput {
width: 100% !important;
}
.fix-el {
position: absolute;
z-index: 9000;
width: 250px;
max-height: 200px;
background-color: #fff;
overflow-y: auto;
box-shadow: 0px 1px 8px rgba(181, 176, 176, 0.3);
.auto-item {
padding-left: 5px;
line-height: 1;
padding: 10px;
font-size: 14px;
&.check {
background-color: #D7A256;
color: #fff;
}
}
}
}
</style>
文章来源地址https://www.toymoban.com/news/detail-534982.html
文章来源:https://www.toymoban.com/news/detail-534982.html
到了这里,关于文本域输入提示,自动补全功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!