界面样子
html代码片段
<template><div class="threshold-wrap"><el-form class="threshold-list" ref="form"><span v-for="(v, vIndex) in thresholdList" :key="v.id"><el-form-item prop="symbol" class="symbol" v-if="v.symbol !== 'to'"><el-select v-model="v.symbol" size="mini" placeholder=""><el-optionv-for="s in symbolList":key="s.id":label="s.label":value="s.id"></el-option></el-select></el-form-item><el-form-item:prop="v.symbol !== 'to' ? 'indexValueList' : ''"class="indexValueList"><template v-if="v.symbol === 'to'"><template v-if="v.dataType === 1"><el-select v-model="v.contain1" size="mini" placeholder=""><el-optionv-for="s in toList":key="s.id":label="s.label":value="s.id"></el-option></el-select><el-date-pickerv-model="v.containVal1"type="date"size="mini":clearable="false"placeholder=""format="yyyy-MM-dd"value-format="timestamp"></el-date-picker>~<el-select v-model="v.contain2" size="mini" placeholder=""><el-optionv-for="s in toList":key="s.id":label="s.label":value="s.id"></el-option></el-select><el-date-pickerv-model="v.containVal2"type="date"size="mini":clearable="false"placeholder=""format="yyyy-MM-dd"value-format="timestamp"></el-date-picker></template><template v-else-if="v.dataType === 2"><el-select v-model="v.contain1" size="mini" placeholder=""><el-optionv-for="s in toList":key="s.id":label="s.label":value="s.id"></el-option></el-select><el-inputv-model="v.containVal1"size="mini"placeholder=""type="number"@input="handleInput($event, vIndex, 'containVal1')"></el-input>~<el-select v-model="v.contain2" size="mini" placeholder=""><el-optionv-for="s in toList":key="s.id":label="s.label":value="s.id"></el-option></el-select><el-inputv-model="v.containVal2"size="mini"placeholder=""type="number"@input="handleInput($event, vIndex, 'containVal2')"></el-input></template></template><template v-else><template v-if="v.dataType === 1"><el-date-pickerv-model="v.indexValueList"type="date"size="mini":clearable="false"placeholder=""format="yyyy-MM-dd"value-format="timestamp"></el-date-picker></template><template v-else-if="v.dataType === 2"><el-inputv-model="v.indexValueList"size="mini"placeholder=""type="number"@input="handleInput($event, vIndex)"></el-input></template></template></el-form-item><el-form-item prop="score" class="score"><el-input-numberv-model="v.score"size="mini"type="number":min="0":max="100"></el-input-number></el-form-item><el-form-item><i class="el-icon-delete" @click="del(vIndex)"></i></el-form-item></span><el-button icon="el-icon-plus" size="mini" @click="add">添加</el-button></el-form><div class="footer"><el-buttontype="primary"size="mini"@click="handleSave">保存</el-button></div></div>
</template>
js代码片段
<script>
export default {name: 'Threshold',components: {},props: {},data() {return {thresholdList: [],symbolList: [{id: 'gt',label: '>',},{id: 'lt',label: '<',},{id: 'equ',label: '=',},{id: 'ne',label: '≠',},{id: 'gte',label: '≥',},{id: 'le',label: '≤',},{id: 'to',label: '~',},],toList: [{id: 'contain',label: '含',},{id: 'notContain',label: '不含',}],toListMap: [{id: 'gt_lt',label: ['notContain', 'notContain'],},{id: 'gt_le',label: ['notContain', 'contain'],},{id: 'gte_lt',label: ['contain', 'notContain'],},{id: 'gte_le',label: ['contain', 'contain'],},],}},watch: {},computed: {},created() {},mounted() {},methods: {add() {this.thresholdList = this.thresholdList.concat({dataType: 2,symbol: null,indexValueList: null,score: null,contain1: null,contain2: null,containVal1: null,containVal2: null,})},del(vIndex) {this.thresholdList = this.thresholdList.filter((element, eIndex) => vIndex !== eIndex);},isOnlyWhitespace(str) { return /^\s*$/.test(str)},isBlank(val) {if(typeof val === 'number') {return false}else if(typeof val === 'boolean') {return false}else if(typeof val === 'string' && this.isOnlyWhitespace(val)){return true}else {return _.isEmpty(val)}},handleInput(value, vIndex, type) {this.thresholdList = this.thresholdList.map((item, index) => {if(index === vIndex) {if(item.symbol !== 'to') {return {...item, indexValueList: value.startsWith('.') ? '0' + value : value,}}else {if(type === 'containVal1') {return {...item, containVal1: value.startsWith('.') ? '0' + value : value,}}else if(type === 'containVal2') {return {...item, containVal2: value.startsWith('.') ? '0' + value : value,}}}}return item})},// 将关系符转换为区间toInterval(obj) {let start, endconst epsilon = 1e-9 // 1e-9表示浮点数,具体是0.000000001,一个很小的可以忽略不计的数// symbol='to'时,indexValueList是数组,存两个数字,否则就是string,一个数字const value = obj.symbol !== 'to' && parseFloat(obj.indexValueList)switch(obj.symbol) {case 'gt':// >start = value + epsilonend = Infinitybreakcase 'lt':// <start = -Infinityend = value - epsilonbreakcase 'gte':// ≥start = valueend = Infinitybreakcase 'le':// ≤start = -Infinityend = valuebreakcase 'equ':// =return { point: value }case 'gt_lt':// (a, b)start = parseFloat(obj.indexValueList[0]) + epsilonend = parseFloat(obj.indexValueList[1]) - epsilonbreakcase 'gt_le':// (a, b]start = parseFloat(obj.indexValueList[0]) + epsilonend = parseFloat(obj.indexValueList[1])breakcase 'gte_lt':// [a, b)start = parseFloat(obj.indexValueList[0])end = parseFloat(obj.indexValueList[1]) - epsilonbreakcase 'gte_le':// [a, b]start = parseFloat(obj.indexValueList[0])end = parseFloat(obj.indexValueList[1])break// case 'ne':// // ≠// return { neqValue: value }}return { start, end }},// 校验是否重叠checkOverlap(a, b) {// 第一种情况:两条数据一个是区间,一个是=具体数据if((a.point !== undefined && b.start !== undefined && b.end !== undefined) || (b.point !== undefined && a.start !== undefined && a.end !== undefined)) {// 分两种情况:1、第一条数据是=,第二条数据是区间;2、第一条数据是区间,第二条数据是=if(a.point !== undefined && b.start !== undefined && b.end !== undefined) {return !(a.point < b.start || a.point>b.end)}if(a.start !== undefined && a.end !== undefined && b.point !== undefined) {return !(b.point < a.start || b.point > a.end)}return false // 区间和=的没有重叠}else if(a.start !== undefined && a.end !== undefined && b.start !== undefined && b.end !== undefined) {// 第二种情况:两条数据都是区间return !(a.start > b.end || a.end < b.start)}else if(a.point !== undefined && b.point !== undefined) {// 第三种情况:两条数据都是=return a.point === b.point}},/*** * @param int1 第一条数据* @param int2 第二条数据*/check(int1, int2, i, j) {if(int1.point !== undefined && int2.start !== undefined && int2.end !== undefined) {if(this.checkOverlap({point: int1.point}, {start: int2.start, end: int2.end})) {return ` 第${i+1}行(= ${int1.point})落在第${j+1}行的区间内\n`}}if(int1.start !== undefined && int1.end !== undefined && int2.point !== undefined) {if(this.checkOverlap({start: int1.start, end: int1.end}, {point: int2.point})) {return ` 第${j+1}行(= ${int2.point})落在第${i+1}行的区间内\n`}}if(int1.start !== undefined && int1.end !== undefined && int2.start !== undefined && int2.end !== undefined) {if(this.checkOverlap({start: int1.start, end: int1.end}, {start: int2.start, end: int2.end})) {return ` 第${i+1}行和第${j+1}行有重叠\n`}}if(int1.point !== undefined && int2.point !== undefined) {if(int1.point === int2.point) {return ` 第${i+1}行(= ${int1.point})和第${j+1}行(= ${int2.point})有重复\n`}}return false},// 校验阈值,首先不能为空,其次:一行算一条数据,每个数据之间不能有重叠区间handleSave() {if(!this.thresholdList.length) {this.$message.error('请添加阈值')return}if(this.thresholdList && this.thresholdList.length>0) {const newlist = this.thresholdList.map(item => {if(item.symbol === 'to') {const foundTo = this.toListMap.find(v => JSON.stringify(v.label) === JSON.stringify([item.contain1, item.contain2]))const symbol = foundTo && foundTo.idreturn {symbol,indexValueList: [item.containVal1, item.containVal2],score: item.score,}} return item})// 1、判空let info1 = []newlist.forEach((item, index) => {if(this.isBlank(item.symbol) || this.isBlank(item.indexValueList) || this.isBlank(item.score)) {info1.push(`第${index+1}行数据不完整\n`)} })// 2、校验阈值重叠,thresholdList中不止两条数据,两两比较所有的数据const n = newlist.lengthlet info2 = []for (let i = 0; i < n; i++) {const int1 = this.toInterval(newlist[i]) //将数据转换为区间for (let j = i + 1; j < n; j++) {const int2 = this.toInterval(newlist[j])// 如果重叠,将提示信息塞进info2数组中this.check(int1, int2, i, j) && info2.push(this.check(int1, int2, i, j))}}if(info1 && info1.length>0) {this.$message({message: info1.join(''),type: 'error',customClass: 'custom-message',})}else if(info2 && info2.length>0) {this.$message({message: info2.join(''),type: 'error',customClass: 'custom-message',})}else {this.$message.success('保存成功')}}}},
}
</script>
style样式片段
<style lang="scss" scoped>
.threshold-wrap {width: 100%;height: 100%;display: flex;flex-direction: column;.threshold-list {width: 100%;flex: 1;&>span {display: flex;.indexValueList {::v-deep .el-form-item__content {display: flex;}}.el-icon-delete {cursor: pointer;margin-left: 10px;color: #409eff;}}}
}// 隐藏数字输入框的选择器
::v-deep input[type="number"]::-webkit-outer-spin-button,
::v-deep input[type="number"]::-webkit-inner-spin-button {-webkit-appearance: none;margin: 0;
}::v-deep input[type="number"] {-moz-appearance: textfield;
}
</style><style lang="scss">
.custom-message {white-space: pre-line; /* 使 \n 生效 */.el-message__content {line-height: 20px;}
}
</style>
完整代码
<template><div class="threshold-wrap"><el-form class="threshold-list" ref="form"><span v-for="(v, vIndex) in thresholdList" :key="v.id"><el-form-item prop="symbol" class="symbol" v-if="v.symbol !== 'to'"><el-select v-model="v.symbol" size="mini" placeholder=""><el-optionv-for="s in symbolList":key="s.id":label="s.label":value="s.id"></el-option></el-select></el-form-item><el-form-item:prop="v.symbol !== 'to' ? 'indexValueList' : ''"class="indexValueList"><template v-if="v.symbol === 'to'"><template v-if="v.dataType === 1"><el-select v-model="v.contain1" size="mini" placeholder=""><el-optionv-for="s in toList":key="s.id":label="s.label":value="s.id"></el-option></el-select><el-date-pickerv-model="v.containVal1"type="date"size="mini":clearable="false"placeholder=""format="yyyy-MM-dd"value-format="timestamp"></el-date-picker>~<el-select v-model="v.contain2" size="mini" placeholder=""><el-optionv-for="s in toList":key="s.id":label="s.label":value="s.id"></el-option></el-select><el-date-pickerv-model="v.containVal2"type="date"size="mini":clearable="false"placeholder=""format="yyyy-MM-dd"value-format="timestamp"></el-date-picker></template><template v-else-if="v.dataType === 2"><el-select v-model="v.contain1" size="mini" placeholder=""><el-optionv-for="s in toList":key="s.id":label="s.label":value="s.id"></el-option></el-select><el-inputv-model="v.containVal1"size="mini"placeholder=""type="number"@input="handleInput($event, vIndex, 'containVal1')"></el-input>~<el-select v-model="v.contain2" size="mini" placeholder=""><el-optionv-for="s in toList":key="s.id":label="s.label":value="s.id"></el-option></el-select><el-inputv-model="v.containVal2"size="mini"placeholder=""type="number"@input="handleInput($event, vIndex, 'containVal2')"></el-input></template></template><template v-else><template v-if="v.dataType === 1"><el-date-pickerv-model="v.indexValueList"type="date"size="mini":clearable="false"placeholder=""format="yyyy-MM-dd"value-format="timestamp"></el-date-picker></template><template v-else-if="v.dataType === 2"><el-inputv-model="v.indexValueList"size="mini"placeholder=""type="number"@input="handleInput($event, vIndex)"></el-input></template></template></el-form-item><el-form-item prop="score" class="score"><el-input-numberv-model="v.score"size="mini"type="number":min="0":max="100"></el-input-number></el-form-item><el-form-item><i class="el-icon-delete" @click="del(vIndex)"></i></el-form-item></span><el-button icon="el-icon-plus" size="mini" @click="add">添加</el-button></el-form><div class="footer"><el-buttontype="primary"size="mini"@click="handleSave">保存</el-button></div></div>
</template><script>
export default {name: 'Threshold',components: {},props: {},data() {return {thresholdList: [],symbolList: [{id: 'gt',label: '>',},{id: 'lt',label: '<',},{id: 'equ',label: '=',},{id: 'ne',label: '≠',},{id: 'gte',label: '≥',},{id: 'le',label: '≤',},{id: 'to',label: '~',},],toList: [{id: 'contain',label: '含',},{id: 'notContain',label: '不含',}],toListMap: [{id: 'gt_lt',label: ['notContain', 'notContain'],},{id: 'gt_le',label: ['notContain', 'contain'],},{id: 'gte_lt',label: ['contain', 'notContain'],},{id: 'gte_le',label: ['contain', 'contain'],},],}},watch: {},computed: {},created() {},mounted() {},methods: {add() {this.thresholdList = this.thresholdList.concat({dataType: 2,symbol: null,indexValueList: null,score: null,contain1: null,contain2: null,containVal1: null,containVal2: null,})},del(vIndex) {this.thresholdList = this.thresholdList.filter((element, eIndex) => vIndex !== eIndex);},isOnlyWhitespace(str) { return /^\s*$/.test(str)},isBlank(val) {if(typeof val === 'number') {return false}else if(typeof val === 'boolean') {return false}else if(typeof val === 'string' && this.isOnlyWhitespace(val)){return true}else {return _.isEmpty(val)}},handleInput(value, vIndex, type) {this.thresholdList = this.thresholdList.map((item, index) => {if(index === vIndex) {if(item.symbol !== 'to') {return {...item, indexValueList: value.startsWith('.') ? '0' + value : value,}}else {if(type === 'containVal1') {return {...item, containVal1: value.startsWith('.') ? '0' + value : value,}}else if(type === 'containVal2') {return {...item, containVal2: value.startsWith('.') ? '0' + value : value,}}}}return item})},// 将关系符转换为区间toInterval(obj) {let start, endconst epsilon = 1e-9 // 1e-9表示浮点数,具体是0.000000001,一个很小的可以忽略不计的数// symbol='to'时,indexValueList是数组,存两个数字,否则就是string,一个数字const value = obj.symbol !== 'to' && parseFloat(obj.indexValueList)switch(obj.symbol) {case 'gt':// >start = value + epsilonend = Infinitybreakcase 'lt':// <start = -Infinityend = value - epsilonbreakcase 'gte':// ≥start = valueend = Infinitybreakcase 'le':// ≤start = -Infinityend = valuebreakcase 'equ':// =return { point: value }case 'gt_lt':// (a, b)start = parseFloat(obj.indexValueList[0]) + epsilonend = parseFloat(obj.indexValueList[1]) - epsilonbreakcase 'gt_le':// (a, b]start = parseFloat(obj.indexValueList[0]) + epsilonend = parseFloat(obj.indexValueList[1])breakcase 'gte_lt':// [a, b)start = parseFloat(obj.indexValueList[0])end = parseFloat(obj.indexValueList[1]) - epsilonbreakcase 'gte_le':// [a, b]start = parseFloat(obj.indexValueList[0])end = parseFloat(obj.indexValueList[1])break// case 'ne':// // ≠// return { neqValue: value }}return { start, end }},// 校验是否重叠checkOverlap(a, b) {// 第一种情况:两条数据一个是区间,一个是=具体数据if((a.point !== undefined && b.start !== undefined && b.end !== undefined) || (b.point !== undefined && a.start !== undefined && a.end !== undefined)) {// 分两种情况:1、第一条数据是=,第二条数据是区间;2、第一条数据是区间,第二条数据是=if(a.point !== undefined && b.start !== undefined && b.end !== undefined) {return !(a.point < b.start || a.point>b.end)}if(a.start !== undefined && a.end !== undefined && b.point !== undefined) {return !(b.point < a.start || b.point > a.end)}return false // 区间和=的没有重叠}else if(a.start !== undefined && a.end !== undefined && b.start !== undefined && b.end !== undefined) {// 第二种情况:两条数据都是区间return !(a.start > b.end || a.end < b.start)}else if(a.point !== undefined && b.point !== undefined) {// 第三种情况:两条数据都是=return a.point === b.point}},/*** * @param int1 第一条数据* @param int2 第二条数据*/check(int1, int2, i, j) {if(int1.point !== undefined && int2.start !== undefined && int2.end !== undefined) {if(this.checkOverlap({point: int1.point}, {start: int2.start, end: int2.end})) {return ` 第${i+1}行(= ${int1.point})落在第${j+1}行的区间内\n`}}if(int1.start !== undefined && int1.end !== undefined && int2.point !== undefined) {if(this.checkOverlap({start: int1.start, end: int1.end}, {point: int2.point})) {return ` 第${j+1}行(= ${int2.point})落在第${i+1}行的区间内\n`}}if(int1.start !== undefined && int1.end !== undefined && int2.start !== undefined && int2.end !== undefined) {if(this.checkOverlap({start: int1.start, end: int1.end}, {start: int2.start, end: int2.end})) {return ` 第${i+1}行和第${j+1}行有重叠\n`}}if(int1.point !== undefined && int2.point !== undefined) {if(int1.point === int2.point) {return ` 第${i+1}行(= ${int1.point})和第${j+1}行(= ${int2.point})有重复\n`}}return false},// 校验阈值,首先不能为空,其次:一行算一条数据,每个数据之间不能有重叠区间handleSave() {if(!this.thresholdList.length) {this.$message.error('请添加阈值')return}if(this.thresholdList && this.thresholdList.length>0) {const newlist = this.thresholdList.map(item => {if(item.symbol === 'to') {const foundTo = this.toListMap.find(v => JSON.stringify(v.label) === JSON.stringify([item.contain1, item.contain2]))const symbol = foundTo && foundTo.idreturn {symbol,indexValueList: [item.containVal1, item.containVal2],score: item.score,}} return item})// 1、判空let info1 = []newlist.forEach((item, index) => {if(this.isBlank(item.symbol) || this.isBlank(item.indexValueList) || this.isBlank(item.score)) {info1.push(`第${index+1}行数据不完整\n`)} })// 2、校验阈值重叠,thresholdList中不止两条数据,两两比较所有的数据const n = newlist.lengthlet info2 = []for (let i = 0; i < n; i++) {const int1 = this.toInterval(newlist[i]) //将数据转换为区间for (let j = i + 1; j < n; j++) {const int2 = this.toInterval(newlist[j])// 如果重叠,将提示信息塞进info2数组中this.check(int1, int2, i, j) && info2.push(this.check(int1, int2, i, j))}}if(info1 && info1.length>0) {this.$message({message: info1.join(''),type: 'error',customClass: 'custom-message',})}else if(info2 && info2.length>0) {this.$message({message: info2.join(''),type: 'error',customClass: 'custom-message',})}else {this.$message.success('保存成功')}}}},
}
</script><style lang="scss" scoped>
.threshold-wrap {width: 100%;height: 100%;display: flex;flex-direction: column;.threshold-list {width: 100%;flex: 1;&>span {display: flex;.indexValueList {::v-deep .el-form-item__content {display: flex;}}.el-icon-delete {cursor: pointer;margin-left: 10px;color: #409eff;}}}
}// 隐藏数字输入框的选择器
::v-deep input[type="number"]::-webkit-outer-spin-button,
::v-deep input[type="number"]::-webkit-inner-spin-button {-webkit-appearance: none;margin: 0;
}::v-deep input[type="number"] {-moz-appearance: textfield;
}
</style><style lang="scss">
.custom-message {white-space: pre-line; /* 使 \n 生效 */.el-message__content {line-height: 20px;}
}
</style>