場景:封裝一個組件 - 簡易計算器,在文本框點擊時顯示計算器,點擊計算器上的按鈕即數字、運算符等,就將點擊的按鈕文本插入到文本框的光標處
計算器組件calculator.vue
<template>
<!-- 計算器 -->
<div ref="calculator" class="calculator" :data-value="value">
<div class="texts">
<span v-for="(item, index) in calculatorTexts" :key="index" @click="handleTextClick(item)">
{{ item }}
</span>
</div>
<div class="close" @click.stop="handleClose">
<a-icon type="close-circle" />
</div>
<!-- <div class="operator">
<span>確定</span>
<span>取消</span>
</div> -->
</div>
</template>
<script>
export default {
name: 'Calculator',
props: {
initValue: {
type: String,
require: true,
default: ''
}
},
data() {
return {
// 計算器文本數組
calculatorTexts: (() => {
let texts = []
for (let index = 0; index < 10; index++) {
texts.push(index.toString())
}
texts = texts.concat(['.', '+', '-', '*', '/', '(', ')'])
return texts
})(),
// 計算器是否可見
calculatorVisible: true,
// 樣式
style: {
top: '0px',
left: '0px'
},
// 光標是否處於計算器內
calculatorFocus: false,
// 文本值
value: this.initValue
}
},
mounted() {
},
methods: {
// 文本點擊
handleTextClick(text) {
// 累積文本 或 覆蓋文本
this.value = text
this.$emit('updateCalculatorValue', this.value)
},
// 關閉計算器
handleClose() {
this.$refs.calculator.style.top = '0px'
this.$refs.calculator.style.left = '0px'
this.$refs.calculator.style.display = 'none'
this.value = ''
},
// 設置坐標
setLocatioin(point) {
Object.assign(this.style, point)
}
}
}
</script>
<style lang="less" scoped>
.calculator {
display: none;
width: 122px;
height: auto;
border: 1px #f0f0f0 solid;
position: absolute;
top: 0;
left: 0;
z-index: 9999;
background-color: white;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
.texts {
display: flex;
flex-wrap: wrap;
span {
flex-shrink: 0;
display: inline-block;
width: 30px;
border-right: 1px #f0f0f0 solid;
border-bottom: 1px #f0f0f0 solid;
text-align: center;
padding: 5px 10px;
&:hover {
color: orange;
border-bottom: 1px orange solid;
cursor: pointer;
}
}
}
.operator {
display: flex;
justify-content: center;
span {
display: inline-block;
border: 1px #f0f0f0 solid;
text-align: center;
padding: 5px 10px;
margin: 8px 5px;
&:hover {
color: orange;
border-bottom: 1px orange solid;
cursor: pointer;
}
}
}
.close {
display: inline-block;
position: absolute;
top: -22px;
right: -15px;
width: auto;
font-size: 22px;
cursor: pointer;
&:hover {
color: red;
}
}
}
</style>
父組件template
<a-form-model-item label="姓名" prop="name"> <a-input ref="name" id="name" v-model="form.name" placeholder="請輸入姓名" :max-length="20" @click="(event) => handleShowCalculator(event, 'calculator1', 'name')" /> <calculator ref="calculator1" @updateCalculatorValue="handleUpdateCalculatorValue" style="top: 44px;"></calculator> </a-form-model-item>
父組件script
// 顯示計算器 handleShowCalculator (event, calculatorRef, field) { this.$refs[calculatorRef].$el.style.display = 'block' event.target.setAttribute('field', field) }
// 計算器文本點擊回調 handleUpdateCalculatorValue (val) { const target = this.$refs.name.$el const pos = this.getCursorPosition(target) const frontStr = this.form.name.substring(0, pos) const behindStr = this.form.name.substring(pos, this.form.name.length) this.form.name = frontStr + val + behindStr /// 注意,定位光標需要在 Vue 數據下一次更新之后,兩種方式: /// 方法1:將 handleUpdateCalculatorValue 函數變為異步函數,方法前加上 async,然后在光標定位代碼 this.setCaretPosition(target, pos + val.length) 的前面加上等待數據更新后的代碼 await this.$nextTick() /// 方法2:將光標定位代碼 this.setCaretPosition(target, pos + val.length) 寫在 this.$nextTick() 中,即 this.$nextTick(() => { this.setCaretPosition(target, pos + val.length) }) this.$nextTick(() => { this.setCaretPosition(target, pos + val.length) }) }
封裝的兩個操作光標方法:
// 獲取光標位置 getCursorPosition (el) { let pos = 0 if ('selectionStart' in el) { pos = el.selectionStart } else if ('selection' in document) { el.focus() const selRange = document.selection.createRange() const selRangeLength = document.selection.createRange().text.length selRange.moveStart('character', -el.value.length) pos = selRange.text.length - selRangeLength } return pos }, // 設置光標位置 setCaretPosition (el, pos) { if (el.setSelectionRange) { el.focus() el.setSelectionRange(pos, pos) } else if (el.createTextRange) { const range = el.createTextRange() range.collapse(true) range.moveEnd('character', pos) range.moveStart('character', pos) range.select() } },
兩個光標操作方法基於以下代碼封裝,也可以使用下面的代碼,二選一,同樣要注意,定位光標需要在 Vue 數據下一次更新之后:
// IE瀏覽器 if (document.selection) { target.focus() const sel = document.selection.createRange() sel.text = val } else if (target.selectionStart) { // 谷歌 Firefox 等 const startPos = target.selectionStart const endPos = target.selectionEnd const restoreTop = target.scrollTop // 獲取滾動條高度 // 拼接字符 this.form.name = this.form.name.substring(0, startPos) + val + this.form.name.substring(endPos, this.form.name.length) if (restoreTop > 0) { target.scrollTop = restoreTop } target.focus() target.selectionStart = startPos + val.length target.selectionEnd = startPos + val.length } else { this.form.name += val target.focus() }
界面效果

在光標處插入字符

