界面样子
html代码片段
javascript
<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-option
v-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-option
v-for="s in toList"
:key="s.id"
:label="s.label"
:value="s.id"
>
</el-option>
</el-select>
<el-date-picker
v-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-option
v-for="s in toList"
:key="s.id"
:label="s.label"
:value="s.id"
>
</el-option>
</el-select>
<el-date-picker
v-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-option
v-for="s in toList"
:key="s.id"
:label="s.label"
:value="s.id"
>
</el-option>
</el-select>
<el-input
v-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-option
v-for="s in toList"
:key="s.id"
:label="s.label"
:value="s.id"
>
</el-option>
</el-select>
<el-input
v-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-picker
v-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-input
v-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-number
v-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-button
type="primary"
size="mini"
@click="handleSave"
>保存</el-button
>
</div>
</div>
</template>
js代码片段
javascript
<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, end
const 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 + epsilon
end = Infinity
break
case 'lt':
// <
start = -Infinity
end = value - epsilon
break
case 'gte':
// ≥
start = value
end = Infinity
break
case 'le':
// ≤
start = -Infinity
end = value
break
case 'equ':
// =
return { point: value }
case 'gt_lt':
// (a, b)
start = parseFloat(obj.indexValueList[0]) + epsilon
end = parseFloat(obj.indexValueList[1]) - epsilon
break
case 'gt_le':
// (a, b]
start = parseFloat(obj.indexValueList[0]) + epsilon
end = parseFloat(obj.indexValueList[1])
break
case 'gte_lt':
// [a, b)
start = parseFloat(obj.indexValueList[0])
end = parseFloat(obj.indexValueList[1]) - epsilon
break
case '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.id
return {
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.length
let 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样式片段
javascript
<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>
完整代码
javascript
<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-option
v-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-option
v-for="s in toList"
:key="s.id"
:label="s.label"
:value="s.id"
>
</el-option>
</el-select>
<el-date-picker
v-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-option
v-for="s in toList"
:key="s.id"
:label="s.label"
:value="s.id"
>
</el-option>
</el-select>
<el-date-picker
v-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-option
v-for="s in toList"
:key="s.id"
:label="s.label"
:value="s.id"
>
</el-option>
</el-select>
<el-input
v-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-option
v-for="s in toList"
:key="s.id"
:label="s.label"
:value="s.id"
>
</el-option>
</el-select>
<el-input
v-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-picker
v-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-input
v-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-number
v-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-button
type="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, end
const 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 + epsilon
end = Infinity
break
case 'lt':
// <
start = -Infinity
end = value - epsilon
break
case 'gte':
// ≥
start = value
end = Infinity
break
case 'le':
// ≤
start = -Infinity
end = value
break
case 'equ':
// =
return { point: value }
case 'gt_lt':
// (a, b)
start = parseFloat(obj.indexValueList[0]) + epsilon
end = parseFloat(obj.indexValueList[1]) - epsilon
break
case 'gt_le':
// (a, b]
start = parseFloat(obj.indexValueList[0]) + epsilon
end = parseFloat(obj.indexValueList[1])
break
case 'gte_lt':
// [a, b)
start = parseFloat(obj.indexValueList[0])
end = parseFloat(obj.indexValueList[1]) - epsilon
break
case '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.id
return {
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.length
let 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>