Bootstrap

vue 获取光标位置

在这里插入图片描述
新建组件configFormulaSalary

<template>
  <div>
    <el-dialog title="公式配置"
        :visible="isConfigFormula" top="15vh"
        width="1200px" custom-class="config-dialog"
        :before-close="onClose"
        append-to-body
        :close-on-click-modal="false"
        @mousedown.native="handleDialogMousedown($event)"
        @mouseup.native="handleDialogMouseup($event)"
        >
        <div class="form-main">
            <div class="left-cont">
                <div class="left-title">{{info? info.name : ''}}=</div>
                <div class="input-box">
                    <el-input type="textarea" v-model="formulaValue"
                    autofocus="autofocus" @keyup.enter.native="searchHandle"
                    ref="configInput" id="configInput"
                    placeholder="请输入计算公式或选择函数" @focus="getCursor"></el-input>
                </div>
                <div class="formula-btn">
                    <div class="formula-left">
                        <el-button class="btn-experience" @click="onCheckItemFormula">公式校验</el-button>
                        <span class="margin-l-20 tips-box" v-if="msgSuccessTips">
                            <i class="el-icon-circle-check"></i>
                            {{msgSuccessTips}}
                        </span>
                        <span class="margin-l-20 tips-box err" v-if="msgErrorTips">
                            <i class="el-icon-circle-close"></i>
                            {{msgErrorTips}}
                        </span>
                    </div>
                    <div class="right-form-btn">
                        <el-select v-model="formulaRule" placeholder="请选择" class="config-select-btn">
                            <el-option
                            v-for="item in formulaRuleList"
                            :key="item.value"
                            :label="item.label"
                            :value="item.value">
                            </el-option>
                        </el-select>
                        <span class="label-text">保留</span>
                        <el-select v-model="decimalPoint" placeholder="请选择" class="select-right">
                            <el-option
                            v-for="item in decimalPointList"
                            :key="item"
                            :label="item"
                            :value="item">
                            </el-option>
                        </el-select>
                        <span class="label-text">位小数</span>
                    </div>
                </div>
                <div class="formula-item">
                    <span class="item-title">快捷运算符</span>
                    <span class="formula-label" v-for="(unit,indexU) in formulaTypeList"
                    :key="indexU" @click="onSelectOperator(unit.value)">{{unit.label}}</span>
                </div>
                <div class="bottom-drap">
                    <div class="drap-title">点击选择薪资字段</div>
                    <div class="drap-list">
                        <!-- <el-checkbox-group v-model="drapSelectList" @change="onSelect">
                            <el-checkbox-button v-for="(drap,indexD) in drapList"
                            :label="'#'+drap+'#'" :key="indexD">{{drap}}</el-checkbox-button>
                        </el-checkbox-group> -->
                        <div class="list" v-for="(drap,indexD) in drapList" :key="indexD">
                            <div class="item" @click="onSelect(drap,'#')" v-if="info.name !==drap">{{drap}}</div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="right-cont" @mouseleave="onLeave">
                <div class="title-bar">插入函数</div>
                <div class="right-box">
                    <div class="box-title-bar">常用函数</div>
                    <ul class="box-item-list" >
                        <li class="item-cont" v-for="(formula,indexV) in formulaData.common"
                        :key="indexV" @mouseenter="onEnterFormulaData(indexV,formula,1)"
                        :class="isSelectIndex1 == indexV?'is-active':''"
                        @click="onSelect(formula.grammar)">
                        <span>{{formula.title}}</span>
                        <el-button type="text">插入</el-button>
                        </li>
                    </ul>
                    <div class="box-title-bar">系统函数</div>
                    <ul class="box-item-list">
                        <li class="item-cont" v-for="(formula,indexV) in formulaData.system"
                        :key="indexV" @mouseenter="onEnterFormulaData(indexV,formula,2)" 
                        :class="isSelectIndex2 == indexV?'is-active':''"
                        @click="onSelect(formula.grammar)">
                            <span>{{formula.title}}</span>
                            <el-button type="text">插入</el-button>
                        </li>
                    </ul>
                </div>
                <div class="right-item-por" v-show="selectClass">
                    <div class="title">{{selectFunObj.title}}</div>
                    <p v-html="selectFunObj.describe"></p>
                </div>
            </div>
        </div>
        <span slot="footer" class="dialog-footer">
            <el-button @click="onClose">取 消</el-button>
            <el-button type="primary" @click="onSubmit('ruleForm')">确 定</el-button>
        </span>
    </el-dialog>
  </div>
</template>

<script>
// import {getCommonAndSystemFunctions,checkItemFormula} from "@/api/salary"
export default {
    props: {
        isConfigFormula: Boolean,
        info: Object,
        drapList: Array,
        fieldGroupList: Array
    },
    data () {
        return {
            formulaValue: '',
            formulaRule: 'round',
            formulaRuleList: [
                { value: 'round', label:'四舍五入' },
                { value: 'floor', label:'向下取整' },
                { value: 'ceil', label:'向上取整' },
            ],
            blurIndex: null,//输入框光标位置
            msgSuccessTips: '',
            msgErrorTips: '',
            decimalPoint: 2,
            decimalPointList:  [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
            formulaTypeList: [
                {value: '+', label: '+'},
                {value: '-', label: '-'},
                {value: '*', label: '*'},
                {value: '/', label: '/'},
                {value: '()', label: '()'},
                {value: '>', label: '>'},
                {value: '<', label: '<'},
                {value: '=', label: '='},
                {value: '>=', label: '≥'},
                {value: '<=', label: '≤'},
                {value: '!=', label: '≠'},
                {value: "''", label: "''"},
            ],
            drapSelectList: [],
            selectFunObj: {},
            selectClass: false,
            isSelectIndex1: null,
            isSelectIndex2: null,
            formulaData:{
                system: [],
                common: [],
            },
            classDialogmodel: false,
        }
    },
    created(){
        if (this.info) {
            this.formulaValue = this.info.text
            this.formulaRule = this.info.mode || 'round'
            this.decimalPoint = this.info.count !==null && this.info.count !== undefined ? this.info.count : 2
            this.getList()
        }
        
    },
    methods: {
        // 选择薪资项
        onSelect(e,value){
            let index=this.blurIndex
            let str=this.formulaValue
            if(value) {
                // this.formulaValue  = this.formulaValue + ' #'+e+'#'
                let formulaValue  ='#'+e+'#'
                this.insertAtCursor(formulaValue)
            } else {
                // this.formulaValue  = this.formulaValue +e
                // this.formulaValue=str.slice(0, index) + e + str.slice(index);
                this.insertAtCursor(e)
            }
        },
        onSelectOperator(e){
            // this.formulaValue  = this.formulaValue +e
            this.insertAtCursor(e)
        },
         // 获取光标位置
        async insertAtCursor(myValue) {
            const myField = document.querySelector('#configInput');
            // const myField = this.$refs.configInput;
            if (myField.selectionStart || myField.selectionStart === 0) {
                var startPos = myField.selectionStart
                var endPos = myField.selectionEnd
                this.formulaValue = myField.value.substring(0, startPos) + myValue 
                            + myField.value.substring(endPos, myField.value.length)
                await this.$nextTick() // 这句是重点, 圈起来
                myField.focus()
                // 检测函数是否下一次插入点位置 设置光标位置
                if(myValue.indexOf('(') !== -1) {
                    let arr = myValue.split('')
                    let index = arr.findIndex(o=>{return o=='('})
                    // myField.setSelectionRange(endPos + myValue.length, endPos + myValue.length)
                    myField.setSelectionRange(endPos + index+1, endPos + index+1)
                } else {
                    myField.setSelectionRange(endPos + myValue.length, endPos + myValue.length)
                }
            } else {
                this.formulaValue += myValue
            }
        },
        onCheckItemFormula() {
            let arr = this.fieldGroupList.reduce((last,next)=>{
                return last.concat(...next.fieldList)
            },[])
            let parmetn = {
                checkItemName: this.info.name,
                formulaText: this.formulaValue,
                salaryItemGroupList: Array.from(new Set(arr)),
            }
            this.msgSuccessTips = ''
            this.msgErrorTips = ''
            // checkItemFormula(parmetn).then(res=> {
            //     if(res.code='SUCCESS') {
            //         this.msgSuccessTips = res.message
            //     }
            // }).catch(err=> {
            //     this.$message.error({
            //         message: err.message
            //     })
            //     this.msgErrorTips = err.message
            // })
        },
        getCursor(e){
            this.blurIndex = e.srcElement.selectionStart
        },
        searchHandle(event) {
            event.preventDefault();
        },
        onLeave(vule){
            this.selectClass = false;
            this.isSelectIndex1 = null
            this.isSelectIndex2 = null
        },
        onEnterFormulaData(index,row,type) {
            this.selectClass = true
            this.selectFunObj = row
            if(type==1) {
                this.isSelectIndex1 = index
                this.isSelectIndex2 = null
            } else {
                this.isSelectIndex1 = null
                this.isSelectIndex2 = index
            }
        },
        
        onClose () {
            this.$emit('onClose')
        },
        onSubmit () {
            let data = {
                formulaValue: this.formulaValue,
                mode: this.formulaRule,
                count: this.decimalPoint,
            }
            this.$emit('onSubmitEdit',data)
        },
        onRuleChange(event) {
            
            this.ruleForm.ruleId = event
        },
        getList () {
            // getCommonAndSystemFunctions().then(res=> {
            //     if(res.code=="SUCCESS") {
            //         this.formulaData.common = res.common
            //         this.formulaData.system = res.system
            //     }
            // }).catch(err=> {
            //     this.$message.error({
            //         message: err.message
            //     })
            // })
        },
        // 监测弹框鼠标事件
        handleDialogMousedown(e) {
            // 如果为true,则表示点击发生在遮罩层
            this.classDialogmodel= !!e.target.classList.contains('el-dialog__wrapper')
        },
        handleDialogMouseup(e) {
            if((!!e.target.classList.contains('el-dialog__wrapper')) && this.classDialogmodel){
                this.onClose()
            }
            this.classDialogmodel=false
        },
    },
    watch: {
        isConfigFormula() {
            if(this.isConfigFormula) {
                this.getList()
            }
        }
    }
}
</script>


<style lang="less" scoped>
@deep: ~'>>>';
// 删除薪资组
@{deep}.config-dialog {
    .el-dialog__header {
        padding: 20px 24px 14px;
        font-size: 18px;
        line-height: 18px;
        font-family: PingFang SC;
        font-weight: 400;
        color: #333;
        border-bottom: 1px solid #E9E9E9;
    }
    .form-main {
        display: flex;
        justify-content: flex-start;
    }
    .el-dialog__body {
        padding: 0 !important;
    }
    .el-dialog__footer {
        text-align: right;
        padding-top: 10px;
        padding-bottom: 14px;
        border-top: 1px solid #E9E9E9;
        .el-button {
            padding: 0;
            width: 80px;
            height: 32px;
            text-align: center;
            line-height: 32px;
            font-size: 14px;
            font-family: PingFang SC;
            font-weight: 400;
            
        }
        .close-but {
            border: 1px solid #D9D9D9;
            border-radius: 4px;
            color: #666666;
            &:hover {
                background: transparent;
            }
        }
        .primary-btn {
            color: #FFFFFF;
            background: #3277FF;
            border-radius: 4px;
        }
    }
    .left-cont {
        width: 850px;
        padding: 0 24px;
        border-right: 1px solid #E8E8E8;
        .left-title {
            font-size: 14px;
            font-family: 'PingFang-SC-Bold';
            font-weight: bold;
            color: #333;
            padding: 14px 0;
        }
        .input-box {
            padding-bottom: 10px;
            .el-textarea__inner {
                width: 100%;
                height: 124px;
                line-height: 26px;
                border: 1px solid #D5D5D5;
                border-radius: 4px;
                font-size: 14px;
                color: #333;
                padding: 0 10px;
                &::placeholder {
                    font-size: 14px;
                    font-family: PingFang SC;
                    font-weight: 400;
                    // color: #CCCCCC;
                }
            }
        }
        .formula-btn {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding-bottom: 24px;
            .btn-experience {
                width: 86px;
                height: 32px;
                padding: 0;
                text-align: center;
                line-height: 32px;
                background: #3277FF;
                border-radius: 4px;
                border-color: #3277FF;
                font-size: 14px;
                font-family: PingFang SC;
                font-weight: 400;
                color: #FFFFFF;
            }
            .right-form-btn {

            }
            .config-select-btn {
                .el-input__inner {
                    width: 102px;
                    height: 32px;
                    line-height: 32px;
                    background: #FFFFFF;
                    border: 1px solid #D5D5D5;
                    border-radius: 4px;
                    // color: #333;
                    &::placeholder {
                        // color: #666;
                    }
                }
                .el-input__icon {
                    line-height: 32px;
                }
            }
            .select-right {
                margin-left: 10px;
                .el-input__inner {
                    width: 56px;
                    height: 32px;
                    line-height: 32px;
                    background: #FFFFFF;
                    border: 1px solid #D5D5D5;
                    border-radius: 4px;
                    // color: #333;
                    &::placeholder {
                        // color: #666;
                    }
                }
                .el-input__icon {
                    line-height: 32px;
                }
            }
            .label-text {
                display: inline-block;
                font-size: 14px;
                font-family: PingFang SC;
                padding-left: 10px;
                font-weight: 400;
                color: #666666;
            }
        }
        .formula-item {
            display: flex;
            justify-content: flex-start;
            align-items: center;
            padding-bottom: 14px;
            .item-title {
                display: inline-block;
                font-size: 14px;
                font-family: 'PingFang-SC-Bold';
                font-weight: bold;
                color: #333;
                margin-right: 12px;
            }
            .formula-label {
                display: inline-block;
                width: 44px;
                height: 32px;
                text-align: center;
                line-height: 30px;
                font-size: 20px;
                font-family: 'PingFang-SC-Medium';
                font-weight: 500;
                color: #666666;
                border: 1px solid #D5D5D5;
                border-radius: 4px;
                margin-right: 14px;
                cursor: pointer;
                &:hover,&.is-active {
                    border-color: #3277FF;
                    color: #3277FF;
                }
            }
        }
        .bottom-drap {
            padding-bottom: 25px;
            .drap-title {
                font-size: 14px;
                line-height: 14px;
                padding-bottom: 15px;
                font-family: 'PingFang-SC-Bold';
                font-weight: bold;
                color: #333;
            }
            .drap-list {
                width: 801px;
                height: 200px;
                background: #FAFAFA;
                overflow: hidden;
                overflow-y: scroll;
                padding: 15px 10px;
                display: flex;
                // align-items: center;
                flex-wrap: wrap;
                .item{
                    width: 118px;
                    padding: 0 10px;
                    height: 32px;
                    line-height: 30px;
                    text-align: center;
                    font-size: 14px;
                    font-family: PingFang SC;
                    font-weight: 400;
                    color: #333;
                    background: #FFFFFF;
                    border: 1px solid #D9D9D9;
                    border-radius: 4px;
                    white-space:nowrap;
                    overflow:hidden;
                    text-overflow:ellipsis;
                    margin-right: 14px;
                    margin-bottom: 14px;
                    cursor: pointer;
                }
                &::-webkit-scrollbar { /*滚动条整体样式*/
                  display: none;
                }
            }
            .el-checkbox-button {
                .el-checkbox-button__inner {
                    width: 118px;
                    padding: 0 10px;
                    height: 32px;
                    line-height: 30px;
                    text-align: center;
                    font-size: 14px;
                    font-family: PingFang SC;
                    font-weight: 400;
                    color: #333;
                    background: #FFFFFF;
                    border: 1px solid #D9D9D9;
                    border-radius: 4px;
                    white-space:nowrap;
                    overflow:hidden;
                    text-overflow:ellipsis;
                    margin-right: 14px;
                    margin-bottom: 14px;
                    
                }
                &:nth-child(6n){
                    .el-checkbox-button__inner {
                        margin-right: 0;
                    }
                    
                }
                &.is-checked,&:hover {
                    .el-checkbox-button__inner {
                        border-color: #3277FF;
                        color: #3277FF;
                    }
                    
                }
            }
        }
    }
    .formula-left {
        display: flex;
        justify-content: flex-start;
        align-items: center;
        .tips-box {
            display: inline-block;
            font-size: 14px;
            max-width: 350px;
            line-height: 16px;
            color: #15bc83;
            &.err {
                color: #ff4141;
            }
        }
    }
    
    .right-cont {
        padding: 0 24px;
        position: relative;
        .title-bar {
            font-size: 14px;
            font-family: PingFang SC;
            font-weight: 400;
            color: #333;
            line-height: 42px;
        }
        .right-box {
            width: 301px;
            height: 480px;
            border: 1px solid #DCDCDC;
            border-radius: 4px;
            overflow: hidden;
            overflow-y: scroll;
            padding: 12px 0px;
            &::-webkit-scrollbar { /*滚动条整体样式*/
              width: 6px; /*高宽分别对应横竖滚动条的尺寸*/
              height: 1px;
              background-color: transparent;
            }
            &::-webkit-scrollbar-thumb { /*滚动条里面小方块*/
              border-radius: 2px;
              border: 5px solid #A9AAB1;
              box-shadow: 5px 0 0 #A9AAB1 inset;
            }
            &::-webkit-scrollbar-thumb:hover {
              box-shadow: 5px 0 0 #A9AAB1 inset;
            }
            .box-title-bar {
                font-size: 14px;
                font-family: PingFang SC;
                font-weight: 400;
                color: #AEAEAE;
                line-height: 14px;
                padding:0 5px,
            }
            .box-item-list {
                padding: 8px 0;
            }
            .item-cont {
                font-size: 14px;
                font-family: PingFang SC;
                font-weight: 400;
                color: #333;
                line-height: 14px;
                padding: 10px 12px;
                cursor: pointer;
                display: flex;
                justify-content: space-between;
                align-items: center;
                &:hover, &.is-active {
                    background: #3277FF;
                    color: #fff;
                }
                .el-button {
                    padding: 0;
                    color: #fff;
                }
            }
        }
        .right-item-por {
            position: absolute;
            left: -280px;
            height: 480px;
            width: 300px;
            overflow: hidden;
            overflow-y: scroll;
            padding: 10px 20px;
            background: #fff;
            border: 1px solid #DCDCDC;
            top: 41px;
            border-radius: 4px;
            font-size: 12px;
            color: #333;
            line-height: 30px;
            &::-webkit-scrollbar { /*滚动条整体样式*/
              width: 6px; /*高宽分别对应横竖滚动条的尺寸*/
              height: 1px;
              background-color: transparent;
            }
            &::-webkit-scrollbar-thumb { /*滚动条里面小方块*/
              border-radius: 2px;
              border: 5px solid #A9AAB1;
              box-shadow: 5px 0 0 #A9AAB1 inset;
            }
            &::-webkit-scrollbar-thumb:hover {
              box-shadow: 5px 0 0 #A9AAB1 inset;
            }
            .title {
                font-size: 16px;
                color: #333;
                line-height: 30px;
            }
        }
    }
}
</style>

在父组件中引入configFormulaSalary

<!-- 配置 -->
            <configFormulaSalary :isConfigFormula="isConfigFormula" :drapList="drapList" :info="formulaSalaryInfo"
            @onClose="onConfigFormulaClose" @onSubmitEdit="onConfigFormulaGroup" v-if="isConfigFormula"
            :fieldGroupList="fieldGroupList"></configFormulaSalary>
            <script>

import configFormulaSalary from './components/configFormulaSalary.vue'
export default {
  name: 'App',
  components: {
    HelloWorld,
    configFormulaSalary
  },
  data () {
      return {
        isConfigFormula:false,
        classmodel: false,
        drapList:[],
        formulaSalaryInfo:{},
        fieldGroupList: []
      }
    },
    methods: {
      // 关闭配置弹框
      onConfigFormulaClose(){
            this.isConfigFormula = false
        },
        // 打开配置弹框
        onConfigFormulaGroup(e){
            this.isConfigFormula = true
        },
    }
}
</script>
;