簡單實現(xiàn)一個vue公式編輯器組件demo
基于vue實現(xiàn)數(shù)據(jù)統(tǒng)計公式的基本功能

一個基于vue實現(xiàn)數(shù)據(jù)統(tǒng)計公式的基本功能。
新建一個 formula.vue 組件
使用
<formula ref="formulaPage" :data-list="dataList" ></formula>
完整demo
<template>
<div id="formulaPage">
<!-- 公式編輯區(qū)域 -->
<div
class="formulaView"
id="formulaView"
ref="formulaView"
@click.stop="recordPosition()"
>
<div class="content-item" v-for="(item,index) in formulaList" :key="index" @click.stop="recordPosition(index)" >
<div class="num" v-if="item.type == 'num'" >
‍{{item.value}}
</div>
<div class="plain" v-else-if="item.type == 'plain'" >
‍{{item.value}}
</div>
<div class="obj" v-else-if="item.type == 'obj'" >
‍{{item.value}}
</div>
<!--光標-->
<div class="cursor" v-if="item.cursor" ></div>
</div>
</div>
<div class="tab mt_10 flex-lr">
<div class="">
<el-select @change="e=>{addItem(e,'obj',)}"
style="width: 120px"
v-model="dataId" placeholder="選擇指標" >
<el-option v-for="item in dataList"
:label="item.name" :value="item.id"
:key="item.id" ></el-option>
</el-select>
<el-select @change="e=>{addItem(e,'plain',)}" v-model="operatorId"
placeholder="選擇數(shù)學(xué)運算符"
style="width: 120px"
class="ml_20"
>
<el-option v-for="item in operatorList" :label="item.name" :value="item.id" :key="item.id" >
</el-option>
</el-select>
</div>
<div class="">
<span class="mr_10 pointer theme-col" @click="clearAll()" > 清除全部</span>
</div>
</div>
</div>
</template>
<script>
/**
* dataList 需要選擇數(shù)據(jù)的集合
* defaultList 初始值的集合
* @change 比變更后回傳的數(shù)據(jù)
* **/
export default {
name: '',
props:{
dataList:{
type:Array,
default() {
return [];
},
},
defaultList:{
type:Array,
default() {
return [];
},
},
},
data: function () {
return {
// 公式字符串
formulaStr:'',
dataId:'',
operatorId:'',
formulaList:[],
//運算符
operatorList:[
{
name:'+',
id:'+'
},
{
name:'-',
id:'-'
},
{
name:'*',
id:'*'
},
{
name:'/',
id:'/'
},
{
name:'()',
id:'()'
},
]
}
},
watch:{
formulaList(val){
this.$emit('change',val)
}
},
created() {
//監(jiān)聽鼠標事件
this.$nextTick(function () {
document.addEventListener("keydown", this.keydown, false);
document.addEventListener('click',this.formulaBlur);
});
},
destroyed () {
//移除監(jiān)聽事件
document.removeEventListener("keydown", this.keydown, false);
document.removeEventListener('click',this.formulaBlur);
},
methods: {
// 獲取
getFormula: function(){
},
// 點選時記錄光標位置
recordPosition(index) {
if(this.formulaList && this.formulaList.length >0){
this.formulaList = this.formulaList.map((item,itemIndex)=>{
item.cursor = false;
if(index > -1 && index == itemIndex){
item.cursor = true;
}else if((index!==0 && !index) && itemIndex == (this.formulaList.length -1)){
item.cursor = true;
}
return item
});
}else {
this.formulaList = [
{
cursor:true,
type:'placeholder',
value:'',
}
]
}
// this.$forceUpdate();
},
//失去焦點
formulaBlur(e){
this.formulaList = this.formulaList?.map(item=>{
item.cursor = false;
return item
})
},
/**
* @returns {addItem<*, void, *>}
* 添加字段
* type obj 字段 num 數(shù)字 plain符號
* place 是否修改光標位置
*/
addItem: function (val, type,place = true) {
if(!val) return false;
let that = this;
//插入括號
if(type == 'plain' && val == '()'){
val = '(';
setTimeout(function () {
that.addItem(')',type,false)
},50)
}
let obj={},data = {
value:'',
key:val,
type:type,
};
if(type == 'obj'){
//獲取數(shù)據(jù) 為 value 賦值
obj = this.dataList?.find(item=>item.id == val);
data.value = obj.name;
}else {
data.value = val;
}
if(this.formulaList && this.formulaList.length>0){
const length = this.formulaList.length;
for (let i = 0; i < length; i++) {
//查找光標位置 如果光標位置為空 則在最后添加
if(this.formulaList[i].cursor){
this.formulaList.splice(i+1,0,data);
place && this.recordPosition(i+1);
break;
}else if(i === (this.formulaList.length - 1)){
this.formulaList.push(data)
this.recordPosition(this.formulaList.length - 1);
}
}
}else {
if(!this.formulaList){
this.formulaList = [];
}
this.formulaList.push(data);
this.recordPosition(this.formulaList.length - 1);
}
},
//清除全部
clearAll(){
this.formulaList = [];
let that = this;
setTimeout(function () {
that.recordPosition();
},100);
},
//刪除
deleteItem(type){
let arr = JSON.parse(JSON.stringify(this.formulaList)),index = null;
const length = arr?.length;
for (let i = 0; i < length; i++) {
if(arr[i].cursor && arr[i].key){
index = i;
if(type == 'del'){
index = i + 1;
}
if(index > -1){
this.formulaList.splice(index,1);
if(type == 'del') {
}else {
this.recordPosition(index - 1);
}
}
break;
}
}
},
// 鍵盤輸入
keydown(e){
//禁止輸入
// 檢測光標是否存在
let index,cursorData = this.formulaList?.find((item,itemIndex)=>{
if(item.cursor){
index = itemIndex
}
return item.cursor
});
if(!cursorData){
return false;
}
//左右移動鍵控制光標位置
if (e && [37,39].includes(e.keyCode)){
if(e.keyCode == 37){index = index - 1;}else {index = index + 1;}
if(index > -1 && index < this.formulaList.length){
this.recordPosition(index);
}
}else if (e && e.keyCode == 8){
//Backspace 鍵 刪除前面的值
this.deleteItem();
}else if (e && [107,109,106,111].includes(e.keyCode)){
//運算符列表
this.addItem(e.key,'plain')
}else if (e && e.shiftKey && [48,57].includes(e.keyCode) ){
//括號
if( e.keyCode == 48) e.key = ')';
if( e.keyCode == 57) e.key = '(';
this.addItem(e.key,'plain')
}else if (e && e.keyCode == 46){
//delete鍵刪除光標后面的值
this.deleteItem('del');
}else {
document.returnValue = false;
var tt=/^([1-9]{1}[0-9]{0,7})$/;//能輸入正數(shù)
if(tt.test(e.key)){
//輸入為數(shù)字 插入數(shù)字
this.addItem(e.key,'num')
}
}
},
/**
* 公式轉(zhuǎn)為字符串
* 格式 [id]符號數(shù)字
* **/
parsingFormula: function(formulaStr){
let str = '',arr = [];
arr = this.formulaList?.map(item=>{
let val = item.key;
if(val){
if(item.type == 'obj'){
val = '['+val+']'
}
str = str+val;
}
return val
});
return str
},
/**
* 格式效驗
* */
formatValidation(){
let objData = null;
let arr = this.formulaList?.filter(item=>{
if(item.type == 'obj'){
objData = item;
}
return item.key;
}),data = {type:true,mag:''};
if(!objData){
data.mag = '至少添加一個指標';
}else {
for (let i = 0; i < arr.length; i++) {
if(i < arr.length-1){
//判斷當(dāng)前類型
if(arr[i].type == 'obj' && arr[i+1].type =='plain' ){
//類型為obj時 后一個 需以 符號結(jié)尾
data.mag = '指標后綴';
}
}
}
}
if(data.mag){
data.type = false;
}
return data;
},
}
}
</script>
<style lang="scss">
#formulaPage {
.formulaView{
padding: 3px 4px;
width: 100%;
height: 120px;
border: 1px solid #eee;
line-height: 1.3;
font-size: 12px;
overflow-y: scroll;
.content-item{
position: relative;
height: 16px;
cursor: text;
user-select: none;
display: flex;
align-items: center;
float: left;
.cursor{
height: 13px;
width: 1px;
background: #333;
animation:defaultCursor 1s steps(2) infinite;
position: absolute;
right: 0;
}
.obj {
padding: 0 5px;
margin: 0 1px;
background: #f1f1f1;
border-radius: 3px;
}
.num{
color: #000;
background: #fff;
padding: 0 1px 0 0;
}
}
}
}
@keyframes defaultCursor {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
</style>以上就是簡單實現(xiàn)一個vue公式編輯器組件demo的詳細內(nèi)容,更多關(guān)于vue公式編輯器組件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Element plus實現(xiàn)圖片手動上傳與回顯的過程
近期,發(fā)現(xiàn)點擊修改,element ui 的圖片沒有回顯到框中,所以本文給大家介紹了Element plus實現(xiàn)圖片手動上傳與回顯的過程,文中通過代碼示例給大家介紹的非常詳細,具有一定的參考價值,需要的朋友可以參考下2024-09-09
詳解Vue返回值動態(tài)生成表單及提交數(shù)據(jù)的辦法
這篇文章主要為大家介紹了Vue返回值動態(tài)生成表單及提交數(shù)據(jù),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2021-12-12
VUE使用vue?create命令創(chuàng)建vue2.0項目的全過程
vue-cli是創(chuàng)建Vue項目的一個腳手架工具,vue-cli提供了vue create等命令,下面這篇文章主要給大家介紹了關(guān)于VUE使用vue?create命令創(chuàng)建vue2.0項目的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2022-07-07
vue中watch和computed為什么能監(jiān)聽到數(shù)據(jù)的改變以及不同之處
這篇文章主要介紹了vue中watch和computed為什么能監(jiān)聽到數(shù)據(jù)的改變以及不同之處,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12
Vue3 + Vue-PDF 實現(xiàn)PDF 文件在線預(yù)覽實戰(zhàn)
這篇文章主要介紹了Vue3 + Vue-PDF 實現(xiàn)PDF 文件在線預(yù)覽實戰(zhàn),文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-06-06
詳解vue與后端數(shù)據(jù)交互(ajax):vue-resource
本篇文章主要介紹了詳解vue與后端數(shù)據(jù)交互(ajax):vue-resource,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-03-03
Vue項目中大文件切片上傳實現(xiàn)秒傳與斷點續(xù)傳的詳細實現(xiàn)過程
這篇文章主要給大家介紹了關(guān)于Vue項目中大文件切片上傳實現(xiàn)秒傳與斷點續(xù)傳的詳細實現(xiàn)過程, 在開發(fā)中,如果上傳的文件過大,可以考慮分片上傳,分片就是說將文件拆分來進行上傳,將各個文件的切片傳遞給后臺,然后后臺再進行合并,需要的朋友可以參考下2023-08-08

