1. 定义拖拽指令
2. 使用
3. 效果
4. 完整代码
<template>
<div class="optimization-result-company" v-loading="loading">
<div class="optimization-result-company-table">
<AgBasicTable
style="height: 100%;width:100%"
:horizontalScrollHidden="true"
:tableData="tableData"
:suppressCellSelection="true"
:columns="columns"
customOverlayNoRowsTemplate="暂无数据"
/>
</div>
<div class="optimization-result-company-scatter_chart">
<el-empty
class="optimization-result-company-scatter_chart-empty"
v-if="tableData.length === 0"
description=" "
:image-size="200"
/>
<div v-else class="optimization-result-company-scatter_chart-data drag-parent">
<div class="xAxisName">{{ optimizeInfo.optimizeMethodName }}模型得分</div>
<div class="yAxisName">{{ optimizeInfo.targetTypeName }}得分</div>
<div class="legend drag_box" v-drag v-if="optimizeInfo.vcorr || optimizeInfo.rcorr">
<p>相关系数:{{ optimizeInfo.vcorr }}</p>
<p>排序相关系数:{{ optimizeInfo.rcorr }}</p>
</div>
<v-chart autoresize style="width: 100%; height: 100%" :options="option" />
</div>
</div>
</div>
</template>
<script>
import AgBasicTable from "@/pc/components/AgBasicTable.vue";
import { lte } from "lodash";
let tableDataMap = {};
export default {
name: "OptimizationResult",
components: {
AgBasicTable
},
props: {
menuParams: {
type: Object
}
},
watch: {
"menuParams.planId"(value) {
this.getData(value);
}
},
directives: {
drag: function(el) {
const dragBox = el; //获取当前元素
// offsetLeft 是一个只读属性,返回当前元素相对于 offsetParent 节点左边界的偏移像素值。
dragBox.onmousedown = e => {
const parentHeight = dragBox.parentNode.offsetHeight;
const parentWidth = dragBox.parentNode.offsetWidth;
const currentWidth = dragBox.offsetWidth;
const currentHeight = dragBox.offsetHeight;
const rightWidth = parentWidth - currentWidth;
const topHeight = parentHeight - currentHeight;
//算出鼠标相对元素的位置
const disX = e.clientX - dragBox.offsetLeft;
const disY = e.clientY - dragBox.offsetTop;
document.onmousemove = e => {
//用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = e.clientX - disX;
let top = e.clientY - disY;
console.log("letft=", left, "top=", top);
//移动当前元素
if (left < 0) {
left = 0;
}
if (left > rightWidth) {
left = rightWidth - 10;
}
if (top < 0) {
top = 0;
}
if (top > topHeight) {
top = topHeight;
}
dragBox.style.left = left + "px";
dragBox.style.top = top + "px";
};
document.onmouseup = e => {
//鼠标弹起来的时候不再移动
document.onmousemove = null;
//预防鼠标弹起来后还会循环(即预防鼠标放上去的时候还会移动)
document.onmouseup = null;
};
};
}
},
data() {
return {
option: {},
loading: false,
tableData: [],
optimizeInfo: {},
columns: [
{
prop: "companyFullName",
label: "主体全称",
minWidth: 220,
align: "left"
},
{
prop: "companyShortName",
label: "主体简称",
minWidth: 100,
align: "left"
},
{
prop: "targetValue",
label: "目标值",
minWidth: 80,
type: "number"
},
{
prop: "score",
label: "模型得分",
minWidth: 80,
type: "number"
}
]
};
},
methods: {
async getData(planId) {
this.loading = true;
try {
const data = await this.$api.CreditScore.listOptimizationResult({
batchId: planId
});
this.tableData = data?.optimizationResultList || [];
if (this.tableData.length === 0) return;
tableDataMap = this.tableData.reduce((targetMap, cur) => {
const key = `${cur.score}|${cur.targetValue}`;
if (!targetMap[key]) {
targetMap[key] = cur;
}
return targetMap;
}, {});
this.optimizeInfo = data.optimizeInfo;
this.initScatterEcharts();
} finally {
this.loading = false;
}
},
initScatterEcharts() {
const data = this.tableData.map(item => [item.score, item.targetValue]);
this.option = {
tooltip: {
formatter: function(params) {
const info = tableDataMap[`${params.data[0] || ""}|${params.data[1] || ""}`];
const style = `<div>
<p>主体名称: ${info.companyFullName || ""}</p>
<p>目标值: ${info.targetValue?.toFixed(2) || ""}</p>
<p>模型得分: ${info.score?.toFixed(2) || ""}</p>
</div>`;
return style;
}
},
xAxis: {
splitLine: { show: false },
scale: true,
axisLine: {
lineStyle: { color: "#909399" }
}
},
yAxis: {
scale: true,
splitLine: {
show: false,
lineStyle: {
// 使用深浅的间隔色
color: ["#dde2eb"]
}
},
axisLine: {
lineStyle: { color: "#909399" }
}
},
grid: { left: "40", top: "10", right: "30", bottom: "40", show: false },
series: [
{
type: "scatter",
data: data,
symbolSize: 8,
itemStyle: { color: "#4e81bf" }
}
]
};
}
},
created() {
this.getData(this.menuParams.planId);
}
};
</script>
<style lang="less" scoped>
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently*/
}
.optimization-result-company {
height: 380px;
border-radius: 2px;
border: 1px solid rgba(53, 56, 63, 0.1);
box-sizing: border-box;
background-color: #fff;
padding: 8px 10px;
display: flex;
flex-direction: row-reverse;
&-table {
flex: 1;
height: calc(100%);
}
&-scatter_chart {
flex: 1;
&-empty {
text-align: center;
::v-deep.el-empty__image {
margin: 0 auto;
}
}
&-data {
width: 100%;
height: 100%;
position: relative;
font-size: 12px;
font-weight: bold;
color: rgb(96, 98, 102);
.xAxisName {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
letter-spacing: 1px;
}
.yAxisName {
position: absolute;
left: 0;
top: 40%;
transform: translateY(-50%);
width: 14px;
word-wrap: break-word;
letter-spacing: 1px;
}
.legend {
position: absolute;
width: 150px;
top: 8px;
right: 10px;
padding: 5px;
font-weight: normal;
background: rgba(0, 0, 0, 0.1);
box-shadow: 3px 3px 6px 3px rgba(0, 0, 0, 0.3);
cursor: move;
.noselect();
z-index: 3000;
> p::before {
content: "";
display: inline-block;
width: 8px;
height: 8px;
background-color: #bd8c46;
margin-right: 4px;
vertical-align: inherit;
}
&::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
filter: blur(20px);
z-index: -1;
margin: -10px;
}
}
}
}
}
</style>