123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784 |
- <template>
- <div :id="$style.formulaPage">
- <!-- 公式编辑区域 -->
- <div
- :id="$style.formulaView"
- ref="formulaView"
- :class="$style.formulaView"
- @click.stop="recordPosition"
- >
- <div
- v-for="(item, index) in formulaList"
- :key="index"
- :class="$style.contentItem"
- @click.stop="recordPosition(index)"
- >
- <div v-if="item.type === 'placeholder'" :class="$style.placeholder"> ‍</div>
- <div v-if="item.type === 'num'" :class="$style.num"> ‍{{ item.value }} </div>
- <div v-if="item.type === 'bd'" :class="$style.num"> ‍{{ item.value }} </div>
- <div v-else-if="item.type === 'plain'" :class="$style.plain"> ‍{{ item.value }} </div>
- <div v-else-if="item.type === 'obj'" :class="$style.obj"> ‍{{ item.value }} </div>
- <!--光标-->
- <div v-if="item.cursor" :class="$style.cursor"></div>
- </div>
- <div
- v-if="formulaList.length === 0"
- style="color: rgb(178, 178, 178);padding-left: 45%;padding-top: 10px;"
- >点击此处创建公式</div
- >
- </div>
- <div v-show="!readOnly" class="tab mt_10 flex-lr">
- <div class="">
- <div class="" style="display:inline-block;position:relative;padding: 0 10px;">
- <a-button type="primary" :class="classStyle" @click="selectZb">选择指标</a-button>
- </div>
- <div class="" style="display:inline-block;position:relative;">
- <a-button :class="classStyle" @click="deleteItem">退格</a-button>
- </div>
- <div class="" style="display:inline-block;position:relative;padding: 0 10px;">
- <a-button :class="classStyle" @click="deleteItem('kh')">清空括号</a-button>
- </div>
- <div class="" style="display:inline-block;position:relative;">
- <a-button :class="classStyle" @click="clearAll">清空公式</a-button>
- </div>
- <div class="" style="display:inline-block;position:relative;padding: 0 10px;">
- <a-button :class="$style.classStylejygs" @click="checkformula">校验公式</a-button>
- </div>
- </div>
- </div>
- <a-modal v-model="ModalVisible" title="手动录入数字" @ok="ModalHandleOk">
- <a-form-model ref="ModalForm" :model="ModalForm">
- <a-form-model-item label="数字" prop="num">
- <a-input v-model="ModalForm.num" />
- </a-form-model-item>
- </a-form-model>
- </a-modal>
- <!-- <iam-audit-modal
- :single="true"
- :check-type="'radio'"
- select-type="table"
- :visible="visible"
- :treeparams="{
- configId: '41',
- }"
- :columns="columns"
- @listSelected="listSelected"
- @getCheckNode="getCheckNode"
- @handleCancel="handleCancel"
- ></iam-audit-modal> -->
- <a-modal
- title="选择指标"
- :visible="visible"
- :confirm-loading="confirmLoading"
- width="1000px"
- @ok="handleOk"
- @cancel="handleCancel"
- >
- <sd-data-table-ex
- ref="zblist"
- :data-url="zbDataUrl"
- :process-req="processReq"
- form-id="kpiIndiDef"
- page-id="kpi/def/definition/kpiIndiDef"
- :columns="zbColumns"
- :search-fields="['indiName']"
- :filter-expressions="zbExpressions"
- :checkType="'radio'"
- show-selection
- >
- </sd-data-table-ex>
- </a-modal>
- </div>
- </template>
- <script>
- import { message, Modal } from 'ant-design-vue'
- // import iamAuditModal from '@product/iam/components/iam-audit-modal.vue'
- import kpiService from '../kpi-service'
- import components from './_import-components/kpi-formula-editor-import'
- export default {
- name: 'KpiFormulaEditor',
- metaInfo: {
- title: 'KpiFormulaEditor',
- },
- components: {
- ...components,
- },
- props: {
- dataList: {
- type: Array,
- default() {
- return []
- },
- },
- defaultList: {
- type: Array,
- default() {
- return []
- },
- },
- readOnly: {
- type: Boolean,
- default: false,
- },
- zbid: {
- type: String,
- default: '',
- },
- },
- data() {
- return {
- confirmLoading: false,
- ModalForm: {
- num: '', // 数字
- },
- currentEntryField: '', // 当前录入的表单字段
- ModalVisible: false,
- num: [],
- // 公式字符串
- formulaStr: '',
- zbDataUrl: 'api/xcoa-mobile/v1/kpi-indi-def/getIndisByAttr',
- dataId: '',
- operatorId: '',
- formulaList: [],
- // 运算符
- operatorList: [
- {
- name: '+',
- id: '+',
- },
- {
- name: '-',
- id: '-',
- },
- {
- name: '*',
- id: '*',
- },
- {
- name: '/',
- id: '/',
- },
- {
- name: '()',
- id: '()',
- },
- ],
- classStyle: '',
- flag: true,
- visible: false,
- zbColumns: [
- {
- title: '序号',
- dataIndex: 'sortNum',
- customRender: (text, record, index) => `${index + 1}`,
- width: '80px',
- },
- {
- title: '指标名称',
- dataIndex: 'indiName',
- },
- {
- title: '业务分类',
- dataIndex: 'busiClassName',
- },
- {
- title: '维护单位',
- dataIndex: 'createDeptName',
- },
- ],
- zbExpressions: [],
- selectedRowKeys: [],
- selectZbkey: 0,
- searchValInit: '',
- // sdataList: [],
- selectList: [],
- }
- },
- watch: {
- formulaList(val) {
- this.$emit('change', val)
- if (!this.flag) {
- this.$emit('change', '')
- }
- },
- },
- created() {
- if (this.defaultList.length > 0) {
- this.formulaList = [...this.defaultList]
- }
- // this.sdataList = [...this.dataList]
- // 监听鼠标事件
- this.$nextTick(function() {
- // document.addEventListener('keydown', this.keydown, false)
- document.addEventListener('keypress', this.keypress, false)
- document.addEventListener('click', this.formulaBlur)
- })
- this.zbExpressions.push({
- dataType: 'str',
- name: 'id',
- op: 'ne',
- stringValue: `${this.zbid}`,
- })
- },
- destroyed() {
- // 移除监听事件
- // document.removeEventListener('keydown', this.keydown, false)
- document.removeEventListener('keypress', this.keypress, false)
- document.removeEventListener('click', this.formulaBlur)
- },
- methods: {
- processReq(req) {
- // 获取查询内容
- return req
- },
- handleOk() {
- const list = this.$refs.zblist.getSelectedRows()
- if (list.length === 0) {
- message.error('请选择指标!')
- } else {
- this.listSelected([], list)
- this.$refs.zblist.clearSelection()
- this.visible = false
- }
- },
- handleCancel() {
- this.visible = false
- },
- listSelected(keys, keyinfos) {
- this.visible = false
- if (keyinfos?.length > 0) {
- const zbid = '#v_' + keyinfos[0].id
- this.selectList = keyinfos[0]
- this.addItem(zbid, 'obj')
- }
- },
- getCheckNode(checkNode) {
- this.visible = false
- },
- selectZb(event) {
- // 阻止丢失事件
- event.stopPropagation()
- this.visible = true
- this.selectZbkey += 1
- },
- checkformula() {
- // 校验所有数字是否合规,两个plain中间的是否是数字格式
- let strnum = ''
- let info = '该公式合法'
- let flag = true
- let gs = ''
- this.formulaList.forEach((v) => {
- if (v.type === 'obj') {
- gs += 'o'
- } else {
- gs += v.value
- }
- })
- let tt = /([+\-*/]{2,})/ // +-*/运算符不能连着
- if (tt.test(gs)) {
- flag = false
- }
- tt = /\(\)/ // 不可含有空括号
- if (tt.test(gs)) {
- flag = false
- }
- tt = /^[+\-*/]/ // 运算符开头
- if (tt.test(gs)) {
- flag = false
- }
- tt = /[+\-*/]$/ // 运算符结尾
- if (tt.test(gs)) {
- flag = false
- }
- // 数字和 指标不能连着
- // tt = /([{0-9}{o}]{2,})/
- // if (tt.test(gs)) {
- // flag = false
- // }
- // 错误情况,(后面是运算符
- tt = /\([+\-*/]/
- if (tt.test(gs)) {
- flag = false
- }
- // 错误情况,)前面是运算符
- if (/[+\-*/]\)/.test(gs)) {
- flag = false
- }
- // 错误情况,(前面不是运算符
- // if (/[^{+}{-}{*}{/}]\(/.test(gs)) {
- // flag = false
- // }
- // 错误情况,使用除()+-*/之外的字符
- // if (/[^{+}{-}{*}{/}0-9.a-zA-Z\(\)]/.test(gs)) {
- // flag = false
- // }
- // 错误情况,(后面不是运算符
- // if (/\([+\-*/]/.test(gs)) {
- // flag = false
- // }
- if (flag) {
- // 判断数字是否标准,判断括号是否对应
- const stack = []
- const f = [...this.formulaList]
- this.formulaList.forEach((v, i) => {
- if (v.type === 'num' || v.type === 'bd') {
- strnum += v.value
- } else if (v.type === 'plain') {
- if (strnum !== '' && isNaN(strnum)) {
- flag = false
- info += ',' + strnum + '不是数字'
- } else if (v.value === '(') {
- stack.push('(')
- } else if (v.value === ')') {
- if (stack.length > 0) {
- stack.pop()
- } else {
- flag = false
- info += ',括号对应不上'
- }
- }
- strnum = ''
- } else if (v.type === 'obj') {
- if (strnum !== '') {
- flag = false
- info += ',指标前不能为数字'
- strnum += 'o'
- } else if (i !== 0 && f[i - 1].type === 'obj') {
- flag = false
- }
- }
- })
- if (strnum !== '' && isNaN(strnum)) {
- flag = false
- info += ',' + strnum + '不是数字'
- }
- if (stack.length !== 0) {
- flag = false
- }
- if (info !== '') {
- info = info.substring(1, info.length) + ',请修改'
- }
- if (flag) {
- if (f.length > 1 && f[f.length - 1].value === '-') {
- flag = false
- } else if (f.length > 1 && f[0].value === '-') {
- flag = false
- }
- }
- }
- if (!flag) {
- info = '该公式不合法'
- } else {
- if(gs.indexOf("o") === -1){
- info = '该公式至少选择一个指标'
- }else{
- info = '该公式合法'
- }
- }
- // if (!flag) {
- Modal.info({
- content: info,
- })
- return flag
- // }
- },
- isNumber(val) {
- // isNumber(val){
- const regPos = /^\d+(\.\d+)?$/
- const regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/
- if (regPos.test(val) && regNeg.test(val)) {
- return true
- } else {
- return false
- }
- },
- openmodal() {
- this.ModalForm.num = ''
- this.currentEntryField = 'field'
- this.ModalVisible = true
- },
- ModalHandleOk() {
- this.addItem(this.ModalForm.num, 'num')
- this.ModalVisible = false
- },
- // 获取
- getFormula: function() {},
- // 点选时记录光标位置
- recordPosition(index) {
- // 只读模式,不可编辑
- if (this.readOnly) {
- return false
- }
- 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) {
- if (!this.visible) {
- const v = this.formulaList.findIndex((i) => i.value === '')
- if (this.formulaList.length === 1 && v > -1) {
- this.formulaList = []
- } else if (this.formulaList.length > 0 && this.formulaList[0].value !== '') {
- this.formulaList.splice(0, 0, {
- cursor: false,
- type: 'placeholder',
- value: '',
- })
- }
- this.formulaList = this.formulaList?.map((item) => {
- item.cursor = false
- return item
- })
- }
- },
- /**
- * @returns {addItem<*, void, *>}
- * 添加字段
- * type obj 字段 num 数字 plain符号
- * place 是否修改光标位置
- */
- addItem: function(val, type, place = true) {
- if (!val) return false
- const that = this
- // 插入括号
- if (type === 'plain' && val === '()') {
- val = '('
- setTimeout(function() {
- that.addItem(')', type, false)
- }, 50)
- }
- const obj = {}
- const data = {
- value: '',
- key: val,
- type: type,
- }
- if (type === 'obj') {
- // 获取数据 为 value 赋值
- // const obj = this.selectList?.find((item) => '#v_' + item.id === val)
- const obj = this.selectList
- data.value = obj.indiName
- } 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)
- if (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 = []
- const that = this
- setTimeout(function() {
- that.recordPosition()
- }, 100)
- },
- // 删除
- deleteItem(type) {
- const arr = JSON.parse(JSON.stringify(this.formulaList))
- var index = null
- const length = arr?.length
- var gbi = 0
- if (type === 'kh') {
- for (let i = length - 1; i > 0; i--) {
- if (arr[i].key && (arr[i].value === '(' || arr[i].value === ')')) {
- index = i
- this.formulaList.splice(index, 1)
- }
- }
- const that = this
- setTimeout(function() {
- that.recordPosition()
- }, 100)
- } else {
- for (let i = 0; i < length; i++) {
- if (arr[i].cursor && arr[i].key) {
- index = i
- if (type === 'del') {
- index = i + 1
- gbi = index
- break
- }
- if (index > -1) {
- this.formulaList.splice(index, 1)
- if (type === 'del') {
- } else {
- gbi = index - 1
- break
- }
- }
- }
- }
- const that = this
- setTimeout(function() {
- that.recordPosition(gbi)
- }, 100)
- }
- },
- // 键盘输入
- keydown(e) {
- // 禁止输入
- // 检测光标是否存在
- var index = null
- const 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 && [190].includes(e.keyCode)) {
- this.addItem(e.key, 'plain')
- } else if (e && e.shiftKey && [16, 49, 57].includes(e.keyCode)) {
- if (e.keyCode === 49) e.key = ')'
- } 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})$/ // 能输入正数
- if (tt.test(e.key)) {
- // 输入为数字 插入数字
- // this.addItem(e.key, 'num')
- this.addItem(String.fromCharCode(e.key), 'num')
- } else {
- this.addItem(String.fromCharCode(e.key), 'plain')
- }
- }
- },
- keypress(e) {
- // 禁止输入
- // 检测光标是否存在
- var index = null
- const cursorData = this.formulaList?.find((item, itemIndex) => {
- if (item.cursor) {
- index = itemIndex
- }
- return item.cursor
- })
- if (!cursorData) {
- return false
- }
- var key = e.keyCode
- // 左右移动键控制光标位置
- 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 && [190].includes(e.keyCode)) {
- this.addItem(e.key, 'plain')
- } else if (e && e.shiftKey && [16, 49, 57].includes(e.keyCode)) {
- if (e.keyCode === 49) e.key = ')'
- } 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 = /^([0-9]{1}[0-9]{0,7})$/ // 能输入正数
- // var tt = /^\\d+(\\.{0,1}\\d+){0,1}$/
- if (tt.test(e.key)) {
- // 输入为数字 插入数字
- // this.addItem(e.key, 'num')
- if (tt.test(String.fromCharCode(key))) {
- this.addItem(String.fromCharCode(key), 'num')
- } else if (['+', '-', '*', '/', '.'].includes(String.fromCharCode(key))) {
- this.addItem(String.fromCharCode(key), 'plain')
- }
- } else {
- if (
- String.fromCharCode(key) === '+' ||
- String.fromCharCode(key) === '-' ||
- String.fromCharCode(key) === '*' ||
- String.fromCharCode(key) === '/' ||
- String.fromCharCode(key) === '(' ||
- String.fromCharCode(key) === ')'
- ) {
- this.addItem(String.fromCharCode(key), 'plain')
- } else if (String.fromCharCode(key) === '.') {
- this.addItem(String.fromCharCode(key), 'bd')
- }
- }
- }
- },
- /**
- * 公式转为字符串
- * 格式 [id]符号数字
- * **/
- parsingFormula: function(formulaStr) {
- let str = ''
- let 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
- },
- },
- }
- </script>
- <style module lang="scss">
- @use '@/common/design' as *;
- #formulaPage {
- .formulaView {
- padding: 10px 11px;
- width: 100%;
- height: 58px;
- border: 1px solid #d9d9d9;
- line-height: 1.3;
- font-size: 16px;
- overflow-y: scroll;
- color: rgba(0, 0, 0, 0.65);
- border-radius: 4px;
- .contentItem {
- position: relative;
- height: 16px;
- cursor: text;
- user-select: none;
- display: flex;
- align-items: center;
- float: left;
- .cursor {
- height: 16px;
- 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;
- }
- .plain {
- margin: 0px 4px;
- }
- .placeholder {
- width: 4px;
- }
- }
- }
- .classStylejygs {
- border-color: #40a9ff;
- }
- }
- @keyframes defaultCursor {
- 0% {
- opacity: 1;
- }
- 100% {
- opacity: 0;
- }
- }
- .search {
- // width: 219px;
- margin-bottom: 10px;
- width: 50%;
- }
- </style>
|