Commit 0a22694f by jiang'yun

修改

parent 4059f610
<template> <template>
<div class="drilling-time-chart-container" v-loading="loading"> <div class="drilling-time-chart-container" v-loading="loading">
<div v-if="jh" class="well-number-display"> <div v-if="jh" class="well-number-display">
<span class="well-label">井号:</span> <span class="well-label">井号:</span>
<span class="well-number">{{ jh }}</span> <span class="well-number">{{ jh }}</span>
</div> </div>
<div class="chart-wrapper"> <div class="chart-wrapper">
<!-- <el-button type="primary" icon="el-icon-download" size="small" class="export-btn" @click="exportChart" <!-- <el-button type="primary" icon="el-icon-download" size="small" class="export-btn" @click="exportChart"
:disabled="!myChart"> :disabled="!myChart">
导出图片 导出图片
</el-button> --> </el-button> -->
<div id="drillingTimeChart" class="chart"></div> <div id="drillingTimeChart" class="chart"></div>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
import * as echarts from 'echarts'; import * as echarts from "echarts";
import { getljqxData } from '@/api/system/cjsjLas'; import { getljqxData } from "@/api/system/cjsjLas";
import { listLjSssjSd } from '@/api/optimization/ljSssjSd'; import { listLjSssjSd } from "@/api/optimization/ljSssjSd";
export default { export default {
name: "DrillingTimeChart", name: "DrillingTimeChart",
props: { props: {
jh: { jh: {
type: String, type: String,
default: '' default: "",
}
},
data() {
return {
chartData: null,
myChart: null,
resizeHandler: null,
loading: false
};
}, },
mounted() { },
data() {
return {
chartData: null,
myChart: null,
resizeHandler: null,
loading: false,
};
},
mounted() {
this.loadChartData();
this.$once("hook:beforeDestroy", this.cleanup);
},
watch: {
jh: {
handler() {
this.loadChartData(); this.loadChartData();
this.$once('hook:beforeDestroy', this.cleanup); },
immediate: false,
}, },
watch: { },
jh: { methods: {
handler() { loadChartData() {
this.loadChartData(); if (!this.jh) {
}, return;
immediate: false }
} this.loading = true;
// 获取录井曲线数据和录井整米数据
Promise.all([getljqxData({ jh: this.jh }), listLjSssjSd({ jh: this.jh })])
.then(([ljqxRes, ljSssjRes]) => {
this.chartData = this.processData(ljqxRes, ljSssjRes);
this.initChart();
this.loading = false;
})
.catch((error) => {
console.error("获取数据失败:", error);
this.$message.error("获取数据失败,请重试");
// 如果API失败,使用模拟数据
this.chartData = this.generateMockData();
this.initChart();
this.loading = false;
});
}, },
methods: {
loadChartData() {
if (!this.jh) {
return;
}
this.loading = true;
// 获取录井曲线数据和录井整米数据
Promise.all([
getljqxData({ jh: this.jh }),
listLjSssjSd({ jh: this.jh })
]).then(([ljqxRes, ljSssjRes]) => {
this.chartData = this.processData(ljqxRes, ljSssjRes);
this.initChart();
this.loading = false;
}).catch(error => {
console.error('获取数据失败:', error);
this.$message.error('获取数据失败,请重试');
// 如果API失败,使用模拟数据
this.chartData = this.generateMockData();
this.initChart();
this.loading = false;
});
},
processData(ljqxRes, ljSssjRes) { processData(ljqxRes, ljSssjRes) {
// 处理录井曲线数据 // 处理录井曲线数据
const processArrayData = (dataList, fieldName) => { const processArrayData = (dataList, fieldName) => {
if (!dataList || !Array.isArray(dataList) || dataList.length === 0) { if (!dataList || !Array.isArray(dataList) || dataList.length === 0) {
return []; return [];
} }
// 如果数据是对象数组格式 [{dept: xxx, value: xxx}, ...] // 如果数据是对象数组格式 [{dept: xxx, value: xxx}, ...]
if (typeof dataList[0] === 'object' && dataList[0].hasOwnProperty('dept')) { if (
return dataList.map(item => ({ typeof dataList[0] === "object" &&
depth: item.dept || item.depth, dataList[0].hasOwnProperty("dept")
value: item[fieldName] || item.value || item ) {
})); return dataList.map((item) => ({
} depth: item.dept || item.depth,
// 如果是普通数组,需要从录井整米数据中获取深度 value: item[fieldName] || item.value || item,
return dataList.map((value, index) => { }));
const depth = ljSssjRes && ljSssjRes.rows && ljSssjRes.rows[index] }
? ljSssjRes.rows[index].js // 如果是普通数组,需要从录井整米数据中获取深度
: null; return dataList
return { depth, value }; .map((value, index) => {
}).filter(item => item.depth !== null && item.value !== null && item.value !== undefined); const depth =
}; ljSssjRes && ljSssjRes.rows && ljSssjRes.rows[index]
? ljSssjRes.rows[index].js
// 处理钻时数据(从录井整米数据中获取) : null;
let drillingTimeData = []; return { depth, value };
if (ljSssjRes && ljSssjRes.rows && Array.isArray(ljSssjRes.rows)) { })
drillingTimeData = ljSssjRes.rows .filter(
.filter(item => item.js !== null && item.js !== undefined && item.zs !== null && item.zs !== undefined) (item) =>
.map(item => ({ item.depth !== null &&
depth: item.js, item.value !== null &&
value: item.zs item.value !== undefined
})); );
} };
// 处理钻时数据(从录井整米数据中获取)
let drillingTimeData = [];
if (ljSssjRes && ljSssjRes.rows && Array.isArray(ljSssjRes.rows)) {
drillingTimeData = ljSssjRes.rows
.filter(
(item) =>
item.js !== null &&
item.js !== undefined &&
item.zs !== null &&
item.zs !== undefined
)
.map((item) => ({
depth: item.js,
value: item.zs,
}));
}
// 处理其他数据
const torqueData = processArrayData(ljqxRes.njList, "nj");
const standpipePressureData = processArrayData(ljqxRes.lyList, "ly");
const drillingPressureData = processArrayData(ljqxRes.zyList, "zy");
const rpmData = processArrayData(ljqxRes.zs1List, "zs1");
// 处理泵冲数据(总泵冲)
let pumpStrokeData = [];
if (ljSssjRes && ljSssjRes.rows && Array.isArray(ljSssjRes.rows)) {
pumpStrokeData = ljSssjRes.rows
.filter(
(item) =>
item.js !== null &&
item.js !== undefined &&
item.zbc !== null &&
item.zbc !== undefined
)
.map((item) => ({
depth: item.js,
value: item.zbc,
}));
}
return {
drillingTime: drillingTimeData,
torque: torqueData,
standpipePressure: standpipePressureData,
drillingPressure: drillingPressureData,
rpm: rpmData,
pumpStroke: pumpStrokeData,
};
},
// 处理其他数据 generateMockData() {
const torqueData = processArrayData(ljqxRes.njList, 'nj'); // 生成模拟数据,作为备用
const standpipePressureData = processArrayData(ljqxRes.lyList, 'ly'); const depthData = [];
const drillingPressureData = processArrayData(ljqxRes.zyList, 'zy'); const drillingTimeData = [];
const rpmData = processArrayData(ljqxRes.zs1List, 'zs1'); const torqueData = [];
const standpipePressureData = [];
// 处理泵冲数据(总泵冲) const drillingPressureData = [];
let pumpStrokeData = []; const rpmData = [];
if (ljSssjRes && ljSssjRes.rows && Array.isArray(ljSssjRes.rows)) { const pumpStrokeData = [];
pumpStrokeData = ljSssjRes.rows
.filter(item => item.js !== null && item.js !== undefined && item.zbc !== null && item.zbc !== undefined) const startDepth = 1695;
.map(item => ({ const endDepth = 2095;
depth: item.js, const step = 1;
value: item.zbc
})); for (let depth = startDepth; depth <= endDepth; depth += step) {
} depthData.push(depth);
drillingTimeData.push({ depth, value: Math.random() * 15 + 5 });
torqueData.push({ depth, value: Math.random() * 5 + 5 });
standpipePressureData.push({ depth, value: Math.random() * 5 + 10 });
drillingPressureData.push({ depth, value: Math.random() * 50 + 50 });
rpmData.push({ depth, value: Math.random() * 10 + 5 });
pumpStrokeData.push({ depth, value: Math.random() * 20 + 120 });
}
return {
drillingTime: drillingTimeData,
torque: torqueData,
standpipePressure: standpipePressureData,
drillingPressure: drillingPressureData,
rpm: rpmData,
pumpStroke: pumpStrokeData,
};
},
return { initChart() {
drillingTime: drillingTimeData, if (!this.chartData) {
torque: torqueData, const chartDom = document.getElementById("drillingTimeChart");
standpipePressure: standpipePressureData, if (chartDom && this.myChart) {
drillingPressure: drillingPressureData, this.myChart.clear();
rpm: rpmData, }
pumpStroke: pumpStrokeData return;
}; }
const chartDom = document.getElementById("drillingTimeChart");
if (!chartDom) {
console.error("未找到图表容器");
return;
}
if (chartDom.offsetWidth === 0 || chartDom.offsetHeight === 0) {
console.log("容器尺寸为0,200ms后重试");
setTimeout(() => this.initChart(), 200);
return;
}
if (this.myChart) {
this.myChart.dispose();
}
this.myChart = echarts.init(chartDom);
const {
drillingTime,
torque,
standpipePressure,
drillingPressure,
rpm,
pumpStroke,
} = this.chartData;
// 转换数据格式为 [depth, value] 格式
const convertToChartData = (dataArray) => {
if (!dataArray || !Array.isArray(dataArray)) {
return [];
}
return dataArray
.filter(
(item) =>
item &&
item.depth !== null &&
item.depth !== undefined &&
item.value !== null &&
item.value !== undefined
)
.map((item) => [item.depth, item.value])
.sort((a, b) => a[0] - b[0]); // 按深度排序
};
const drillingTimeChartData = convertToChartData(drillingTime);
console.log(drillingTimeChartData, "drillingTimeChartData");
const torqueChartData = convertToChartData(torque);
const standpipePressureChartData = convertToChartData(standpipePressure);
const drillingPressureChartData = convertToChartData(drillingPressure);
const rpmChartData = convertToChartData(rpm);
const pumpStrokeChartData = convertToChartData(pumpStroke);
// 计算深度范围
const allDepths = [
...drillingTimeChartData.map((d) => d[0]),
...torqueChartData.map((d) => d[0]),
...standpipePressureChartData.map((d) => d[0]),
...drillingPressureChartData.map((d) => d[0]),
...rpmChartData.map((d) => d[0]),
...pumpStrokeChartData.map((d) => d[0]),
].filter((d) => d !== null && d !== undefined);
const minDepth =
allDepths.length > 0 ? Math.floor(Math.min(...allDepths) / 10) * 10 : 0;
const maxDepth =
allDepths.length > 0
? Math.ceil(Math.max(...allDepths) / 10) * 10
: 1000;
const option = {
title: {
text: "录井钻时图",
left: "center",
top: 10,
}, },
tooltip: {
generateMockData() { trigger: "axis",
// 生成模拟数据,作为备用 axisPointer: {
const depthData = []; type: "cross",
const drillingTimeData = []; },
const torqueData = []; formatter: (params) => {
const standpipePressureData = []; if (!params || params.length === 0) {
const drillingPressureData = []; return "";
const rpmData = [];
const pumpStrokeData = [];
const startDepth = 1695;
const endDepth = 2095;
const step = 1;
for (let depth = startDepth; depth <= endDepth; depth += step) {
depthData.push(depth);
drillingTimeData.push({ depth, value: Math.random() * 15 + 5 });
torqueData.push({ depth, value: Math.random() * 5 + 5 });
standpipePressureData.push({ depth, value: Math.random() * 5 + 10 });
drillingPressureData.push({ depth, value: Math.random() * 50 + 50 });
rpmData.push({ depth, value: Math.random() * 10 + 5 });
pumpStrokeData.push({ depth, value: Math.random() * 20 + 120 });
} }
// 获取深度值(x轴的值)
return { const depth =
drillingTime: drillingTimeData, params[0].axisValue ||
torque: torqueData, (params[0].data && params[0].data[0]) ||
standpipePressure: standpipePressureData, "";
drillingPressure: drillingPressureData, let result = `井深: ${depth} m<br/>`;
rpm: rpmData, params.forEach((param) => {
pumpStroke: pumpStrokeData if (param.value !== null && param.value !== undefined) {
}; const unit = param.seriesName.includes("钻时")
? "min/m"
: param.seriesName.includes("扭矩")
? "kN•m"
: param.seriesName.includes("立压")
? "MPa"
: param.seriesName.includes("钻压")
? "kN"
: param.seriesName.includes("转速")
? "rpm"
: "";
result += `${param.seriesName}: ${param.value}${
unit ? " " + unit : ""
}<br/>`;
}
});
return result;
},
}, },
legend: {
initChart() { data: [
if (!this.chartData) { "钻时 (min/m)",
const chartDom = document.getElementById('drillingTimeChart'); "扭矩 (kN•m)",
if (chartDom && this.myChart) { "立压 (MPa)",
this.myChart.clear(); "钻压 (kN)",
} "转速 (rpm)",
return; "泵冲",
} ],
top: 40,
const chartDom = document.getElementById('drillingTimeChart'); right: 50,
if (!chartDom) { icon: "rect",
console.error('未找到图表容器'); itemWidth: 20,
return; itemHeight: 2,
} itemGap: 10,
if (chartDom.offsetWidth === 0 || chartDom.offsetHeight === 0) {
console.log('容器尺寸为0,200ms后重试');
setTimeout(() => this.initChart(), 200);
return;
}
if (this.myChart) {
this.myChart.dispose();
}
this.myChart = echarts.init(chartDom);
const { drillingTime, torque, standpipePressure, drillingPressure, rpm, pumpStroke } = this.chartData;
// 转换数据格式为 [depth, value] 格式
const convertToChartData = (dataArray) => {
if (!dataArray || !Array.isArray(dataArray)) {
return [];
}
return dataArray
.filter(item => item && item.depth !== null && item.depth !== undefined && item.value !== null && item.value !== undefined)
.map(item => [item.depth, item.value])
.sort((a, b) => a[0] - b[0]); // 按深度排序
};
const drillingTimeChartData = convertToChartData(drillingTime);
console.log(drillingTimeChartData, 'drillingTimeChartData');
const torqueChartData = convertToChartData(torque);
const standpipePressureChartData = convertToChartData(standpipePressure);
const drillingPressureChartData = convertToChartData(drillingPressure);
const rpmChartData = convertToChartData(rpm);
const pumpStrokeChartData = convertToChartData(pumpStroke);
// 计算深度范围
const allDepths = [
...drillingTimeChartData.map(d => d[0]),
...torqueChartData.map(d => d[0]),
...standpipePressureChartData.map(d => d[0]),
...drillingPressureChartData.map(d => d[0]),
...rpmChartData.map(d => d[0]),
...pumpStrokeChartData.map(d => d[0])
].filter(d => d !== null && d !== undefined);
const minDepth = allDepths.length > 0 ? Math.floor(Math.min(...allDepths) / 10) * 10 : 0;
const maxDepth = allDepths.length > 0 ? Math.ceil(Math.max(...allDepths) / 10) * 10 : 1000;
const option = {
title: {
text: '录井钻时图',
left: 'center',
top: 10
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
formatter: (params) => {
if (!params || params.length === 0) {
return '';
}
// 获取深度值(x轴的值)
const depth = params[0].axisValue || (params[0].data && params[0].data[0]) || '';
let result = `井深: ${depth} m<br/>`;
params.forEach(param => {
if (param.value !== null && param.value !== undefined) {
const unit = param.seriesName.includes('钻时') ? 'min/m' :
param.seriesName.includes('扭矩') ? 'kN•m' :
param.seriesName.includes('立压') ? 'MPa' :
param.seriesName.includes('钻压') ? 'kN' :
param.seriesName.includes('转速') ? 'rpm' : '';
result += `${param.seriesName}: ${param.value}${unit ? ' ' + unit : ''}<br/>`;
}
});
return result;
}
},
legend: {
data: ['钻时 (min/m)', '扭矩 (kN•m)', '立压 (MPa)', '钻压 (kN)', '转速 (rpm)', '泵冲'],
top: 40,
right: 50,
icon: 'rect',
itemWidth: 20,
itemHeight: 2,
itemGap: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '15%',
containLabel: true
},
xAxis: {
type: 'value',
name: '井深 (m)',
nameLocation: 'center',
nameGap: 30,
axisLabel: {
formatter: '{value}'
},
min: minDepth,
max: maxDepth
},
yAxis: [
{
type: 'value',
name: '钻时 / 扭矩 / 立压',
position: 'left',
nameLocation: 'center',
nameGap: 50,
axisLabel: {
formatter: '{value}'
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed'
}
}
},
{
type: 'value',
name: '钻压 / 转速 / 泵冲',
position: 'right',
nameLocation: 'center',
nameGap: 50,
axisLabel: {
formatter: '{value}'
},
splitLine: {
show: false
}
}
],
series: [
{
name: '钻时 (min/m)',
type: 'line',
yAxisIndex: 0,
data: drillingTimeChartData,
smooth: true,
lineStyle: {
color: '#ff6e6e',
width: 2,
type: 'solid'
},
symbol: 'none'
},
{
name: '扭矩 (kN•m)',
type: 'line',
yAxisIndex: 0,
data: torqueChartData,
smooth: true,
lineStyle: {
color: '#275081',
width: 2,
type: 'dashed'
},
symbol: 'none'
},
{
name: '立压 (MPa)',
type: 'line',
yAxisIndex: 0,
data: standpipePressureChartData,
smooth: true,
lineStyle: {
color: '#e57113',
width: 2,
type: 'dashed'
},
symbol: 'none'
},
{
name: '钻压 (kN)',
type: 'line',
yAxisIndex: 1,
data: drillingPressureChartData,
smooth: true,
lineStyle: {
color: '#757575',
width: 2,
type: 'solid'
},
symbol: 'none'
},
{
name: '转速 (rpm)',
type: 'line',
yAxisIndex: 1,
data: rpmChartData,
smooth: true,
lineStyle: {
color: '#cda2a1',
width: 2,
type: 'dashed'
},
symbol: 'none'
},
{
name: '泵冲',
type: 'line',
yAxisIndex: 1,
data: pumpStrokeChartData,
smooth: true,
lineStyle: {
color: '#637441',
width: 2,
type: 'solid'
},
symbol: 'none'
}
]
};
this.myChart.setOption(option);
this.resizeHandler = () => this.myChart.resize();
window.addEventListener('resize', this.resizeHandler);
}, },
grid: {
exportChart() { left: "3%",
if (!this.myChart) { right: "4%",
this.$message.warning('图表未初始化,无法导出'); bottom: "3%",
return; top: "15%",
} containLabel: true,
try {
const url = this.myChart.getDataURL({
type: 'png',
pixelRatio: 2,
backgroundColor: '#fff'
});
const link = document.createElement('a');
link.href = url;
link.download = `录井钻时图_${this.jh}_${new Date().getTime()}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
this.$message.success('图片导出成功');
} catch (error) {
console.error('导出图片失败:', error);
this.$message.error('导出图片失败,请重试');
}
}, },
xAxis: {
type: "value",
name: "井深 (m)",
nameLocation: "center",
nameGap: 30,
axisLabel: {
formatter: "{value}",
},
min: minDepth,
max: maxDepth,
},
yAxis: [
{
type: "value",
name: "钻时 / 扭矩 / 立压",
position: "left",
nameLocation: "center",
nameGap: 50,
axisLabel: {
formatter: "{value}",
},
splitLine: {
show: true,
lineStyle: {
type: "dashed",
},
},
},
{
type: "value",
name: "钻压 / 转速 / 泵冲",
position: "right",
nameLocation: "center",
nameGap: 50,
axisLabel: {
formatter: "{value}",
},
splitLine: {
show: false,
},
},
],
series: [
{
name: "钻时 (min/m)",
type: "line",
yAxisIndex: 0,
data: drillingTimeChartData,
smooth: true,
symbol: "none",
},
{
name: "扭矩 (kN•m)",
type: "line",
yAxisIndex: 0,
data: torqueChartData,
smooth: true,
symbol: "none",
},
{
name: "立压 (MPa)",
type: "line",
yAxisIndex: 0,
data: standpipePressureChartData,
smooth: true,
symbol: "none",
},
{
name: "钻压 (kN)",
type: "line",
yAxisIndex: 1,
data: drillingPressureChartData,
smooth: true,
symbol: "none",
},
{
name: "转速 (rpm)",
type: "line",
yAxisIndex: 1,
data: rpmChartData,
smooth: true,
symbol: "none",
},
{
name: "泵冲",
type: "line",
yAxisIndex: 1,
data: pumpStrokeChartData,
smooth: true,
symbol: "none",
},
],
};
this.myChart.setOption(option);
this.resizeHandler = () => this.myChart.resize();
window.addEventListener("resize", this.resizeHandler);
},
cleanup() { exportChart() {
if (this.myChart) { if (!this.myChart) {
this.myChart.dispose(); this.$message.warning("图表未初始化,无法导出");
this.myChart = null; return;
} }
if (this.resizeHandler) { try {
window.removeEventListener('resize', this.resizeHandler); const url = this.myChart.getDataURL({
this.resizeHandler = null; type: "png",
} pixelRatio: 2,
} backgroundColor: "#fff",
} });
const link = document.createElement("a");
link.href = url;
link.download = `录井钻时图_${this.jh}_${new Date().getTime()}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
this.$message.success("图片导出成功");
} catch (error) {
console.error("导出图片失败:", error);
this.$message.error("导出图片失败,请重试");
}
},
cleanup() {
if (this.myChart) {
this.myChart.dispose();
this.myChart = null;
}
if (this.resizeHandler) {
window.removeEventListener("resize", this.resizeHandler);
this.resizeHandler = null;
}
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.drilling-time-chart-container { .drilling-time-chart-container {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
} }
.well-number-display { .well-number-display {
position: absolute; position: absolute;
top: 16px; top: 16px;
left: 16px; left: 16px;
z-index: 5; z-index: 5;
background: transparent; background: transparent;
padding: 8px 12px; padding: 8px 12px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
transition: all 0.3s ease; transition: all 0.3s ease;
opacity: 0.9; opacity: 0.9;
} }
.well-number-display:hover { .well-number-display:hover {
opacity: 1; opacity: 1;
} }
.well-label { .well-label {
color: #6b7280; color: #6b7280;
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
white-space: nowrap; white-space: nowrap;
} }
.well-number { .well-number {
color: #3b82f6; color: #3b82f6;
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
} }
.chart-wrapper { .chart-wrapper {
position: relative; position: relative;
width: 100%; width: 100%;
height: calc(100vh - 230px); height: calc(100vh - 230px);
min-height: 600px; min-height: 600px;
} }
.chart { .chart {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.export-btn { .export-btn {
position: absolute; position: absolute;
top: 10px; top: 10px;
right: 10px; right: 10px;
z-index: 100; z-index: 100;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
} }
</style> </style>
<template> <template>
<div class="chart-container"> <div class="chart-container">
<div v-if="jh" class="well-number-display"> <div v-if="jh" class="well-number-display">
<span class="well-label">井号:</span> <span class="well-label">井号:</span>
<span class="well-number">{{ jh }}</span> <span class="well-number">{{ jh }}</span>
</div> </div>
<div class="export-button-container"> <div class="export-button-container">
<el-button class="export-btn" @click="exportChart" :disabled="loading || !myChart" title="导出图表为图片" <el-button
icon="el-icon-download"> class="export-btn"
导出 @click="exportChart"
</el-button> :disabled="loading || !myChart"
</div> title="导出图表为图片"
<div class="chart-layout"> icon="el-icon-download"
<div id="mainzft" class="chart" ref="chartRef"></div> >
<aside v-if="legendItems && legendItems.length" class="strata-legend"> 导出
<div class="legend-header">层位图例</div> </el-button>
<div class="legend-list"> </div>
<div v-for="(item, index) in legendItems" :key="item.name || index" class="legend-item"> <div class="chart-layout">
<div class="legend-icon" :style="getLegendSwatchStyle(item)"></div> <div id="mainzft" class="chart" ref="chartRef"></div>
<span class="legend-label">{{ item.name || '-' }}</span> <aside v-if="legendItems && legendItems.length" class="strata-legend">
</div> <div class="legend-header">层位图例</div>
</div> <div class="legend-list">
</aside> <div
</div> v-for="(item, index) in legendItems"
<div v-if="loading" class="loading-overlay"> :key="item.name || index"
<div class="loading-spinner"></div> class="legend-item"
<span>加载中...</span> >
<div class="legend-icon" :style="getLegendSwatchStyle(item)"></div>
<span class="legend-label">{{ item.name || "-" }}</span>
</div>
</div> </div>
</aside>
</div>
<div v-if="loading" class="loading-overlay">
<div class="loading-spinner"></div>
<span>加载中...</span>
</div> </div>
</div>
</template> </template>
<script> <script>
...@@ -35,752 +44,809 @@ import { getZft } from "@/api/optimization/initialization"; ...@@ -35,752 +44,809 @@ import { getZft } from "@/api/optimization/initialization";
import { text } from "d3"; import { text } from "d3";
export default { export default {
name: "HistogramGraph", name: "HistogramGraph",
props: { props: {
jh: { jh: {
type: String, type: String,
default: "", default: "",
},
// 美化配置选项
theme: {
type: String,
default: "modern", // modern, elegant, vibrant
validator: value => ["modern", "elegant", "vibrant"].includes(value)
}
}, },
data() { // 美化配置选项
return { theme: {
mockData: {}, type: String,
legendItems: [], default: "modern", // modern, elegant, vibrant
myChart: null, validator: (value) => ["modern", "elegant", "vibrant"].includes(value),
initRetryCount: 0,
maxRetryCount: 5,
resizeObserver: null,
loading: false,
debounceTimer: null,
lastStackedAreas: null,
lastChartConfig: null,
lastXAxisLabels: null,
};
}, },
computed: { },
// 根据主题获取颜色配置 data() {
colorScheme() { return {
const schemes = { mockData: {},
modern: { legendItems: [],
primary: "#3B82F6", myChart: null,
secondary: "#10B981", initRetryCount: 0,
accent: "#F59E0B", maxRetryCount: 5,
background: "#F8FAFC", resizeObserver: null,
text: "#1F2937", loading: false,
border: "#E5E7EB", debounceTimer: null,
gradient: { lastStackedAreas: null,
start: "#3B82F6", lastChartConfig: null,
end: "#1D4ED8" lastXAxisLabels: null,
} };
}, },
elegant: { computed: {
primary: "#6366F1", // 根据主题获取颜色配置
secondary: "#8B5CF6", colorScheme() {
accent: "#EC4899", const schemes = {
background: "#FAFAFA", modern: {
text: "#374151", primary: "#3B82F6",
border: "#D1D5DB", secondary: "#10B981",
gradient: { accent: "#F59E0B",
start: "#6366F1", background: "#F8FAFC",
end: "#4F46E5" text: "#1F2937",
} border: "#E5E7EB",
}, gradient: {
vibrant: { start: "#3B82F6",
primary: "#EF4444", end: "#1D4ED8",
secondary: "#06B6D4", },
accent: "#F97316", },
background: "#FEFEFE", elegant: {
text: "#111827", primary: "#6366F1",
border: "#F3F4F6", secondary: "#8B5CF6",
gradient: { accent: "#EC4899",
start: "#EF4444", background: "#FAFAFA",
end: "#DC2626" text: "#374151",
} border: "#D1D5DB",
} gradient: {
}; start: "#6366F1",
return schemes[this.theme]; end: "#4F46E5",
},
}, },
legendPanelWidth() { vibrant: {
return this.legendItems && this.legendItems.length ? 260 : 0; primary: "#EF4444",
secondary: "#06B6D4",
accent: "#F97316",
background: "#FEFEFE",
text: "#111827",
border: "#F3F4F6",
gradient: {
start: "#EF4444",
end: "#DC2626",
},
},
};
return schemes[this.theme];
},
legendPanelWidth() {
return this.legendItems && this.legendItems.length ? 260 : 0;
},
},
watch: {
jh: {
handler(newVal) {
if (newVal) {
this.refreshChart();
} }
},
immediate: true,
}, },
watch: { legendItems: {
jh: { handler() {
handler(newVal) { this.$nextTick(() => {
if (newVal) { const chartDom = this.$refs.chartRef;
this.refreshChart(); if (chartDom) {
} this.setChartDimensions(chartDom);
}, this.performResize();
immediate: true }
}, });
legendItems: { },
handler() { deep: true,
this.$nextTick(() => { },
const chartDom = this.$refs.chartRef; },
if (chartDom) { mounted() {
this.setChartDimensions(chartDom); this.initChart();
this.performResize(); this.setupEventListeners();
} },
}); beforeDestroy() {
}, this.cleanup();
deep: true },
methods: {
// 初始化图表
async initChart() {
try {
this.loading = true;
const chartDom = this.$refs.chartRef;
if (!chartDom) {
throw new Error("未找到图表容器 DOM");
} }
this.setChartDimensions(chartDom);
// 重试机制优化
if (this.shouldRetry()) {
this.scheduleRetry();
return;
}
this.resetRetryCount();
this.disposeChart();
this.createChart(chartDom);
const mockData = await this.getList();
if (!this.hasValidData(mockData)) {
this.renderEmpty(chartDom);
return;
}
await this.renderRealData(mockData);
} catch (error) {
console.error("图表初始化失败:", error);
this.handleError(error);
} finally {
this.loading = false;
}
}, },
mounted() {
this.initChart(); // 设置图表尺寸
this.setupEventListeners(); setChartDimensions(chartDom) {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const containerWidth = this.$el?.clientWidth || viewportWidth;
const rect = this.$el?.getBoundingClientRect();
const topOffset = rect ? rect.top : 0;
const legendWidth = this.legendPanelWidth || 0;
const containerPadding = 20;
const panelGap = legendWidth ? 5 : 0;
const safetyMargin = 12;
const widthPadding =
containerPadding + panelGap + legendWidth + safetyMargin;
const availableWidth = Math.max(360, containerWidth - widthPadding);
const verticalPadding = 40;
const heightByViewport = viewportHeight - topOffset - verticalPadding;
const fallbackHeight = this.$el?.clientHeight || viewportHeight;
const availableHeight = Math.max(
360,
heightByViewport,
fallbackHeight - 20
);
chartDom.style.width = `${availableWidth}px`;
chartDom.style.height = `${availableHeight}px`;
chartDom.offsetHeight; // 强制重排
}, },
beforeDestroy() {
this.cleanup(); // 检查是否需要重试
shouldRetry() {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const availableHeight = viewportHeight - 120;
const availableWidth = viewportWidth - 80;
return (
(availableWidth <= 0 || availableHeight <= 0) &&
this.initRetryCount < this.maxRetryCount
);
}, },
methods: {
// 初始化图表
async initChart() {
try {
this.loading = true;
const chartDom = this.$refs.chartRef;
if (!chartDom) {
throw new Error("未找到图表容器 DOM");
}
this.setChartDimensions(chartDom);
// 重试机制优化
if (this.shouldRetry()) {
this.scheduleRetry();
return;
}
this.resetRetryCount();
this.disposeChart();
this.createChart(chartDom);
const mockData = await this.getList();
if (!this.hasValidData(mockData)) {
this.renderEmpty(chartDom);
return;
}
await this.renderRealData(mockData);
} catch (error) {
console.error("图表初始化失败:", error);
this.handleError(error);
} finally {
this.loading = false;
}
},
// 设置图表尺寸 // 安排重试
setChartDimensions(chartDom) { scheduleRetry() {
const viewportWidth = window.innerWidth; this.initRetryCount++;
const viewportHeight = window.innerHeight; setTimeout(() => this.initChart(), 500);
const containerWidth = this.$el?.clientWidth || viewportWidth; },
const rect = this.$el?.getBoundingClientRect();
const topOffset = rect ? rect.top : 0;
const legendWidth = this.legendPanelWidth || 0;
const containerPadding = 20;
const panelGap = legendWidth ? 5 : 0;
const safetyMargin = 12;
const widthPadding = containerPadding + panelGap + legendWidth + safetyMargin;
const availableWidth = Math.max(360, containerWidth - widthPadding);
const verticalPadding = 40;
const heightByViewport = viewportHeight - topOffset - verticalPadding;
const fallbackHeight = this.$el?.clientHeight || viewportHeight;
const availableHeight = Math.max(360, heightByViewport, fallbackHeight - 20);
chartDom.style.width = `${availableWidth}px`;
chartDom.style.height = `${availableHeight}px`;
chartDom.offsetHeight; // 强制重排
},
// 检查是否需要重试 // 重置重试计数
shouldRetry() { resetRetryCount() {
const viewportWidth = window.innerWidth; this.initRetryCount = 0;
const viewportHeight = window.innerHeight; },
const availableHeight = viewportHeight - 120;
const availableWidth = viewportWidth - 80;
return (availableWidth <= 0 || availableHeight <= 0) && this.initRetryCount < this.maxRetryCount;
},
// 安排重试 // 销毁图表
scheduleRetry() { disposeChart() {
this.initRetryCount++; if (this.myChart) {
setTimeout(() => this.initChart(), 500); this.myChart.dispose();
}, }
},
// 重置重试计数 // 创建图表实例
resetRetryCount() { createChart(chartDom) {
this.initRetryCount = 0; this.myChart = echarts.init(chartDom, null, {
}, renderer: "canvas",
useDirtyRect: true,
});
},
// 销毁图表 // 检查数据有效性
disposeChart() { hasValidData(mockData) {
if (this.myChart) { return (
this.myChart.dispose(); mockData &&
} mockData.wellData &&
}, mockData.wellData.depthLine &&
mockData.wellData.depthLine.length > 0
);
},
// 创建图表实例 // 设置事件监听器
createChart(chartDom) { setupEventListeners() {
this.myChart = echarts.init(chartDom, null, { window.addEventListener("resize", this.handleResize);
renderer: 'canvas', this.observeParentResize();
useDirtyRect: true },
});
},
// 检查数据有效性 // 清理资源
hasValidData(mockData) { cleanup() {
return mockData && window.removeEventListener("resize", this.handleResize);
mockData.wellData && if (this.myChart) {
mockData.wellData.depthLine && this.myChart.dispose();
mockData.wellData.depthLine.length > 0; }
}, if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
if (this.debounceTimer) {
clearTimeout(this.debounceTimer);
}
},
// 设置事件监听器 // 观察父容器大小变化
setupEventListeners() { observeParentResize() {
window.addEventListener("resize", this.handleResize); const parentDom = this.$refs.chartRef?.parentElement;
this.observeParentResize(); if (parentDom && window.ResizeObserver) {
}, this.resizeObserver = new ResizeObserver(() => {
this.debouncedResize();
});
this.resizeObserver.observe(parentDom);
}
},
// 清理资源 // 获取数据
cleanup() { async getList() {
window.removeEventListener("resize", this.handleResize); try {
if (this.myChart) { const res = await getZft({ jhe: this.jh });
this.myChart.dispose(); this.mockData = res?.mockData || {};
} const legendList = Array.isArray(res?.tlList)
if (this.resizeObserver) { ? res.tlList
this.resizeObserver.disconnect(); : Array.isArray(res?.mockData?.tlList)
} ? res.mockData.tlList
if (this.debounceTimer) { : [];
clearTimeout(this.debounceTimer); this.legendItems = legendList;
} return this.mockData;
}, } catch (error) {
console.error("获取数据失败:", error);
throw error;
}
},
// 观察父容器大小变化 // 处理窗口大小变化
observeParentResize() { handleResize() {
const parentDom = this.$refs.chartRef?.parentElement; this.debouncedResize();
if (parentDom && window.ResizeObserver) { },
this.resizeObserver = new ResizeObserver(() => {
this.debouncedResize();
});
this.resizeObserver.observe(parentDom);
}
},
// 获取数据 // 防抖处理resize
async getList() { debouncedResize() {
try { if (this.debounceTimer) {
const res = await getZft({ jhe: this.jh }); clearTimeout(this.debounceTimer);
this.mockData = res?.mockData || {}; }
const legendList = Array.isArray(res?.tlList) this.debounceTimer = setTimeout(() => {
? res.tlList this.performResize();
: Array.isArray(res?.mockData?.tlList) }, 200);
? res.mockData.tlList },
: [];
this.legendItems = legendList;
return this.mockData;
} catch (error) {
console.error("获取数据失败:", error);
throw error;
}
},
// 处理窗口大小变化 // 执行resize操作
handleResize() { performResize() {
this.debouncedResize(); if (this.myChart) {
}, const chartDom = this.$refs.chartRef;
if (chartDom) {
this.setChartDimensions(chartDom);
}
this.myChart.resize();
// 重新绘制地层标签,确保在缩放/尺寸变化后位置正确
if (
this.lastStackedAreas &&
this.lastChartConfig &&
this.lastXAxisLabels
) {
this.drawStratumLabels(
this.lastStackedAreas,
this.lastChartConfig,
this.lastXAxisLabels
);
}
}
},
// 防抖处理resize // 刷新图表
debouncedResize() { async refreshChart() {
if (this.debounceTimer) { if (this.myChart) {
clearTimeout(this.debounceTimer); await this.initChart();
} }
this.debounceTimer = setTimeout(() => { },
this.performResize();
}, 200);
},
// 执行resize操作 // 渲染真实数据
performResize() { async renderRealData(mockData) {
if (this.myChart) { try {
const chartDom = this.$refs.chartRef; const { wellData } = mockData;
if (chartDom) { // 调试信息:打印深度区间数据
this.setChartDimensions(chartDom); if (wellData.depthIntervals && wellData.depthIntervals.length > 0) {
} console.log(
this.myChart.resize(); "深度区间数据:",
// 重新绘制地层标签,确保在缩放/尺寸变化后位置正确 wellData.depthIntervals.map((item) => ({
if (this.lastStackedAreas && this.lastChartConfig && this.lastXAxisLabels) { 型号: item.x,
this.drawStratumLabels(this.lastStackedAreas, this.lastChartConfig, this.lastXAxisLabels); 尺寸: item.ztcc,
} 进尺: item.jc,
} 深度区间: item.interval,
}, 占位: item.placeholder,
计算高度:
item.jc !== undefined && item.jc !== null
? item.jc
: item.interval - item.placeholder,
}))
);
}
const xAxisLabels = wellData.depthLine.map((point) => point.x);
// 刷新图表 // 优化区域系列处理
async refreshChart() { const areaSeries = await this.processAreaSeries(wellData.stackedAreas);
if (this.myChart) {
await this.initChart();
}
},
// 渲染真实数据 const option = {
async renderRealData(mockData) { ...this.getDefaultChartOption(),
try { xAxis: this.createXAxis(xAxisLabels),
const { wellData } = mockData; yAxis: this.createYAxis(mockData.chartConfig),
// 调试信息:打印深度区间数据 series: this.createSeries(wellData, areaSeries),
if (wellData.depthIntervals && wellData.depthIntervals.length > 0) { };
console.log('深度区间数据:', wellData.depthIntervals.map(item => ({
型号: item.x,
尺寸: item.ztcc,
进尺: item.jc,
深度区间: item.interval,
占位: item.placeholder,
计算高度: item.jc !== undefined && item.jc !== null ? item.jc : (item.interval - item.placeholder)
})));
}
const xAxisLabels = wellData.depthLine.map((point) => point.x);
// 优化区域系列处理
const areaSeries = await this.processAreaSeries(wellData.stackedAreas);
const option = {
...this.getDefaultChartOption(),
xAxis: this.createXAxis(xAxisLabels),
yAxis: this.createYAxis(mockData.chartConfig),
series: this.createSeries(wellData, areaSeries)
};
this.myChart.setOption(option, true);
// 确保图表完全渲染后再resize
this.$nextTick(() => {
this.myChart.resize();
// 存储用于重绘的数据并绘制一次
this.lastStackedAreas = wellData.stackedAreas;
this.lastChartConfig = mockData.chartConfig;
this.lastXAxisLabels = xAxisLabels;
this.drawStratumLabels(this.lastStackedAreas, this.lastChartConfig, this.lastXAxisLabels);
});
} catch (error) {
console.error("渲染数据失败:", error);
this.handleError(error);
}
},
// 获取默认图表配置 this.myChart.setOption(option, true);
getDefaultChartOption() {
const colors = this.colorScheme; // 确保图表完全渲染后再resize
return { this.$nextTick(() => {
title: { text: "", subtext: "" }, this.myChart.resize();
aria: { enabled: false }, // 存储用于重绘的数据并绘制一次
backgroundColor: colors.background, this.lastStackedAreas = wellData.stackedAreas;
tooltip: { this.lastChartConfig = mockData.chartConfig;
trigger: "axis", this.lastXAxisLabels = xAxisLabels;
axisPointer: { this.drawStratumLabels(
type: "cross", this.lastStackedAreas,
label: { backgroundColor: colors.primary }, this.lastChartConfig,
crossStyle: { color: colors.border } this.lastXAxisLabels
}, );
backgroundColor: 'rgba(255,255,255,0.95)', });
borderColor: colors.border, } catch (error) {
borderWidth: 1, console.error("渲染数据失败:", error);
textStyle: { color: colors.text }, this.handleError(error);
extraCssText: 'box-shadow: 0 4px 12px rgba(0,0,0,0.15); border-radius: 8px;', }
formatter: (params) => { },
if (!params || params.length === 0) return '';
// 获取默认图表配置
const dataIndex = params[0].dataIndex; getDefaultChartOption() {
const depthInterval = this.mockData?.wellData?.depthIntervals?.[dataIndex]; const colors = this.colorScheme;
return {
if (!depthInterval) { title: { text: "", subtext: "" },
// 如果没有depthInterval数据,使用默认格式 aria: { enabled: false },
let result = `<div style="font-weight: 600; margin-bottom: 8px; color: ${colors.primary};">${params[0].axisValue}</div>`; backgroundColor: colors.background,
params.forEach(param => { tooltip: {
const color = param.color; trigger: "axis",
result += `<div style="margin: 4px 0;"> axisPointer: {
type: "cross",
label: { backgroundColor: colors.primary },
crossStyle: { color: colors.border },
},
backgroundColor: "rgba(255,255,255,0.95)",
borderColor: colors.border,
borderWidth: 1,
textStyle: { color: colors.text },
extraCssText:
"box-shadow: 0 4px 12px rgba(0,0,0,0.15); border-radius: 8px;",
formatter: (params) => {
if (!params || params.length === 0) return "";
const dataIndex = params[0].dataIndex;
const depthInterval =
this.mockData?.wellData?.depthIntervals?.[dataIndex];
if (!depthInterval) {
// 如果没有depthInterval数据,使用默认格式
let result = `<div style="font-weight: 600; margin-bottom: 8px; color: ${colors.primary};">${params[0].axisValue}</div>`;
params.forEach((param) => {
const color = param.color;
result += `<div style="margin: 4px 0;">
<span style="display: inline-block; width: 10px; height: 10px; background-color: ${color}; border-radius: 50%; margin-right: 8px;"></span> <span style="display: inline-block; width: 10px; height: 10px; background-color: ${color}; border-radius: 50%; margin-right: 8px;"></span>
<span style="font-weight: 500;">${param.seriesName}:</span> <span style="font-weight: 500;">${param.seriesName}:</span>
<span style="margin-left: 8px; font-weight: 600;">${param.value}</span> <span style="margin-left: 8px; font-weight: 600;">${param.value}</span>
</div>`; </div>`;
}); });
return result; return result;
} }
// 显示井号信息 // 显示井号信息
let result = `<div style="font-weight: 600; margin-bottom: 8px; color: ${colors.primary};">${depthInterval.jh || depthInterval.x}</div>`; let result = `<div style="font-weight: 600; margin-bottom: 8px; color: ${
colors.primary
// 计算深度区间显示 };">${depthInterval.jh || depthInterval.x}</div>`;
const startDepth = depthInterval.placeholder || 0;
const height = depthInterval.jc !== undefined && depthInterval.jc !== null // 计算深度区间显示
? depthInterval.jc const startDepth = depthInterval.placeholder || 0;
: (depthInterval.interval - depthInterval.placeholder); const height =
const endDepth = startDepth + height; depthInterval.jc !== undefined && depthInterval.jc !== null
const depthRange = `${startDepth}-${endDepth}`; ? depthInterval.jc
: depthInterval.interval - depthInterval.placeholder;
// 显示详细信息 const endDepth = startDepth + height;
result += `<div style="margin: 4px 0; padding: 4px 0; border-top: 1px solid ${colors.border};"> const depthRange = `${startDepth}-${endDepth}`;
<div style="margin: 2px 0;"><span style="font-weight: 500;">型号:</span> <span style="margin-left: 8px; font-weight: 600;">${depthInterval.x || '-'}</span></div>
<div style="margin: 2px 0;"><span style="font-weight: 500;">尺寸:</span> <span style="margin-left: 8px; font-weight: 600;">${depthInterval.ztcc || '-'}</span></div> // 显示详细信息
<div style="margin: 2px 0;"><span style="font-weight: 500;">进尺m:</span> <span style="margin-left: 8px; font-weight: 600;">${depthInterval.jc !== undefined && depthInterval.jc !== null ? depthInterval.jc : (depthInterval.interval - depthInterval.placeholder)}</span></div> result += `<div style="margin: 4px 0; padding: 4px 0; border-top: 1px solid ${
colors.border
};">
<div style="margin: 2px 0;"><span style="font-weight: 500;">型号:</span> <span style="margin-left: 8px; font-weight: 600;">${
depthInterval.x || "-"
}</span></div>
<div style="margin: 2px 0;"><span style="font-weight: 500;">尺寸:</span> <span style="margin-left: 8px; font-weight: 600;">${
depthInterval.ztcc || "-"
}</span></div>
<div style="margin: 2px 0;"><span style="font-weight: 500;">进尺m:</span> <span style="margin-left: 8px; font-weight: 600;">${
depthInterval.jc !== undefined &&
depthInterval.jc !== null
? depthInterval.jc
: depthInterval.interval -
depthInterval.placeholder
}</span></div>
<div style="margin: 2px 0;"><span style="font-weight: 500;">深度区间:</span> <span style="margin-left: 8px; font-weight: 600;">${depthRange}</span></div> <div style="margin: 2px 0;"><span style="font-weight: 500;">深度区间:</span> <span style="margin-left: 8px; font-weight: 600;">${depthRange}</span></div>
</div>`; </div>`;
return result; return result;
} },
},
grid: {
top: 10,
left: "4%",
right: "4%",
bottom: "5%",
containLabel: true,
show: false,
},
animation: true,
animationDuration: 1500,
animationEasing: 'cubicOut',
animationDelay: function (idx) {
return idx * 100;
}
};
}, },
grid: {
// 处理区域系列 top: 10,
async processAreaSeries(stackedAreas) { left: "4%",
return await Promise.all( right: "4%",
stackedAreas bottom: "5%",
.filter(area => area.svg !== null) containLabel: true,
.map(async (area) => { show: false,
let areaStyle = { opacity: 0.6 };
if (area.svg) {
try {
const svgImage = await this.createSvgImage(area.svg);
areaStyle.color = { image: svgImage, repeat: "repeat" };
} catch (error) {
console.error('SVG转换失败:', error);
}
}
return {
name: area.name,
type: "line",
xAxisIndex: 1,
z: -1,
stack: "总量",
symbol: "none",
showSymbol: false,
areaStyle: areaStyle,
lineStyle: { width: 0 },
data: area.points.map((point) => point.y),
};
})
);
}, },
animation: true,
// 创建X轴配置 animationDuration: 1500,
createXAxis(xAxisLabels) { animationEasing: "cubicOut",
const colors = this.colorScheme; animationDelay: function (idx) {
return [ return idx * 100;
{
type: "category",
boundaryGap: true,
position: "top",
data: xAxisLabels,
axisLabel: {
interval: 0,
rotate: 0,
margin: 12,
align: "center",
fontSize: 13,
color: colors.text,
fontWeight: 500
},
axisTick: {
alignWithLabel: true,
length: 6,
lineStyle: { color: colors.border }
},
splitLine: { show: false },
axisLine: {
show: true,
lineStyle: {
color: colors.border,
width: 2
}
}
},
{
type: "category",
boundaryGap: false,
position: "top",
show: false,
data: xAxisLabels
},
];
}, },
};
},
// 创建Y轴配置 // 处理区域系列
createYAxis(chartConfig) { async processAreaSeries(stackedAreas) {
const colors = this.colorScheme; return await Promise.all(
return [ stackedAreas
{ .filter((area) => area.svg !== null)
type: "value", .map(async (area) => {
name: "井深(m)", let areaStyle = { opacity: 0.6 };
nameTextStyle: { if (area.svg) {
color: colors.text, try {
fontSize: 13, const svgImage = await this.createSvgImage(area.svg);
fontWeight: 600, areaStyle.color = { image: svgImage, repeat: "repeat" };
padding: [0, 0, 0, 8] } catch (error) {
}, console.error("SVG转换失败:", error);
min: chartConfig.yAxis.min, }
max: chartConfig.yAxis.max, }
interval: chartConfig.yAxis.interval, return {
inverse: true, name: area.name,
axisLabel: { type: "line",
formatter: "{value}", xAxisIndex: 1,
fontSize: 12, z: -1,
color: colors.text, stack: "总量",
fontWeight: 500 symbol: "none",
}, showSymbol: false,
splitLine: { areaStyle: areaStyle,
show: true, lineStyle: { width: 0 },
lineStyle: { data: area.points.map((point) => point.y),
color: colors.border, };
type: 'dashed', })
opacity: 0.3 );
} },
},
axisLine: { // 创建X轴配置
show: true, createXAxis(xAxisLabels) {
lineStyle: { const colors = this.colorScheme;
color: colors.border, return [
width: 2 {
} type: "category",
}, boundaryGap: true,
axisTick: { position: "top",
show: true, data: xAxisLabels,
lineStyle: { color: colors.border } axisLabel: {
} interval: 0,
}, rotate: 0,
{ margin: 12,
type: "value", align: "center",
name: "深度(m)", fontSize: 13,
nameTextStyle: { color: colors.text,
color: colors.text, fontWeight: 500,
fontSize: 13, },
fontWeight: 600, axisTick: {
padding: [0, 8, 0, 0] alignWithLabel: true,
}, length: 6,
min: chartConfig.yAxis2.min, lineStyle: { color: colors.border },
max: chartConfig.yAxis2.max, },
interval: chartConfig.yAxis2.interval, splitLine: { show: false },
position: "right", axisLine: {
axisLabel: { show: true,
formatter: "{value}", lineStyle: {
fontSize: 12, color: colors.border,
color: colors.text, width: 2,
fontWeight: 500 },
}, },
splitLine: { show: false },
axisLine: {
show: true,
lineStyle: {
color: colors.border,
width: 2
}
},
axisTick: {
show: true,
lineStyle: { color: colors.border }
}
},
];
}, },
{
type: "category",
boundaryGap: false,
position: "top",
show: false,
data: xAxisLabels,
},
];
},
// 创建系列配置 // 创建Y轴配置
createSeries(wellData, areaSeries) { createYAxis(chartConfig) {
const colors = this.colorScheme; const colors = this.colorScheme;
return [
return [ {
{ type: "value",
name: "井深数据", name: "井深(m)",
type: "line", nameTextStyle: {
yAxisIndex: 1, color: colors.text,
symbol: "circle", fontSize: 13,
symbolSize: 10, fontWeight: 600,
itemStyle: { padding: [0, 0, 0, 8],
color: colors.accent, },
borderColor: '#fff', min: chartConfig.yAxis.min,
borderWidth: 3, max: chartConfig.yAxis.max,
shadowColor: 'rgba(0,0,0,0.2)', interval: chartConfig.yAxis.interval,
shadowBlur: 8 inverse: true,
}, axisLabel: {
lineStyle: { formatter: "{value}",
color: colors.accent, fontSize: 12,
width: 3, color: colors.text,
type: 'solid', fontWeight: 500,
shadowColor: 'rgba(0,0,0,0.1)', },
shadowBlur: 4 splitLine: {
}, show: true,
label: { lineStyle: {
show: true, color: colors.border,
position: "top", type: "dashed",
formatter: "{c}", opacity: 0.3,
fontSize: 13, },
fontWeight: 600, },
color: colors.accent, axisLine: {
backgroundColor: "rgba(255,255,255,0.95)", show: true,
padding: [6, 8], lineStyle: {
borderRadius: 6, color: colors.border,
borderColor: colors.accent, width: 2,
borderWidth: 2, },
shadowColor: 'rgba(0,0,0,0.1)', },
shadowBlur: 4 axisTick: {
}, show: true,
data: wellData.depthLine.map((point) => point.y), lineStyle: { color: colors.border },
}, },
{
name: "占位",
type: "bar",
stack: "total",
silent: true,
barWidth: "8%",
itemStyle: {
borderColor: "transparent",
color: "transparent"
},
emphasis: {
itemStyle: {
borderColor: "transparent",
color: "transparent"
}
},
data: wellData.depthIntervals.map((item) => item.placeholder),
},
{
name: "深度区间",
type: "bar",
stack: "total",
barWidth: "6%",
label: {
show: true,
position: 'insideTop',
color: '#fff',
fontSize: 11,
fontWeight: 600,
backgroundColor: "rgba(0,0,0,0.1)",
padding: [2, 3],
borderRadius: 4,
borderColor: 'rgba(255,255,255,0.3)',
borderWidth: 1,
formatter: (params) => {
const di = wellData.depthIntervals[params.dataIndex];
return di && di.ztcc !== undefined ? di.ztcc : params.data;
}
},
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: colors.gradient.start },
{ offset: 1, color: colors.gradient.end }
]
},
borderRadius: [4, 4, 0, 0],
shadowColor: 'rgba(0,0,0,0.2)',
shadowBlur: 8,
shadowOffsetY: 2
},
emphasis: {
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: this.adjustColor(colors.gradient.start, 1.2) },
{ offset: 1, color: this.adjustColor(colors.gradient.end, 1.2) }
]
},
shadowBlur: 12,
shadowOffsetY: 4
}
},
// 使用 jc(进尺)作为柱子的高度,如果没有 jc 则使用 interval - placeholder 计算
data: wellData.depthIntervals.map((item) => {
// 优先使用 jc(进尺),如果没有则计算 interval - placeholder
return item.jc !== undefined && item.jc !== null ? item.jc : (item.interval - item.placeholder);
}),
},
// 底部显示 placeholder 数值的透明条,用于放置底部标签
{
name: '深度区间-底部标签',
type: 'bar',
barGap: '-100%',
barWidth: '6%',
itemStyle: { color: 'transparent', borderColor: 'transparent' },
emphasis: { itemStyle: { color: 'transparent', borderColor: 'transparent' } },
label: {
show: true,
position: 'insideBottom',
color: '#fff',
fontSize: 11,
fontWeight: 600,
backgroundColor: 'rgba(0,0,0,0.1)',
padding: [2, 3],
borderRadius: 4,
borderColor: 'rgba(255,255,255,0.3)',
borderWidth: 1,
distance: 0,
formatter: (params) => {
const di = wellData.depthIntervals[params.dataIndex];
// 显示深度区间,格式为:开始深度-结束深度 或 interval值
if (di && di.placeholder !== undefined && di.jc !== undefined) {
const endDepth = di.placeholder + di.jc;
return `${di.placeholder}-${endDepth}`;
}
return di && di.interval !== undefined ? di.interval : params.data;
}
},
// 使用相同的计算方式,确保标签位置正确
data: wellData.depthIntervals.map((item) => {
return item.jc !== undefined && item.jc !== null ? item.jc : (item.interval - item.placeholder);
})
},
...areaSeries,
];
}, },
{
type: "value",
name: "深度(m)",
nameTextStyle: {
color: colors.text,
fontSize: 13,
fontWeight: 600,
padding: [0, 8, 0, 0],
},
min: chartConfig.yAxis2.min,
max: chartConfig.yAxis2.max,
interval: chartConfig.yAxis2.interval,
position: "right",
axisLabel: {
formatter: "{value}",
fontSize: 12,
color: colors.text,
fontWeight: 500,
},
splitLine: { show: false },
axisLine: {
show: true,
lineStyle: {
color: colors.border,
width: 2,
},
},
axisTick: {
show: true,
lineStyle: { color: colors.border },
},
},
];
},
// 颜色调整工具方法 // 创建系列配置
adjustColor(color, factor) { createSeries(wellData, areaSeries) {
if (color.startsWith('#')) { const colors = this.colorScheme;
const r = parseInt(color.slice(1, 3), 16);
const g = parseInt(color.slice(3, 5), 16); return [
const b = parseInt(color.slice(5, 7), 16); {
const newR = Math.min(255, Math.round(r * factor)); name: "井深数据",
const newG = Math.min(255, Math.round(g * factor)); type: "line",
const newB = Math.min(255, Math.round(b * factor)); yAxisIndex: 1,
return `rgb(${newR}, ${newG}, ${newB})`; symbol: "circle",
} symbolSize: 10,
return color; itemStyle: {
color: colors.accent,
borderColor: "#fff",
borderWidth: 3,
shadowColor: "rgba(0,0,0,0.2)",
shadowBlur: 8,
},
lineStyle: {
color: colors.accent,
width: 3,
type: "solid",
shadowColor: "rgba(0,0,0,0.1)",
shadowBlur: 4,
},
label: {
show: true,
position: "top",
formatter: "{c}",
fontSize: 13,
fontWeight: 600,
color: colors.accent,
backgroundColor: "rgba(255,255,255,0.95)",
padding: [6, 8],
borderRadius: 6,
borderColor: colors.accent,
borderWidth: 2,
shadowColor: "rgba(0,0,0,0.1)",
shadowBlur: 4,
},
data: wellData.depthLine.map((point) => point.y),
}, },
{
name: "占位",
type: "bar",
stack: "total",
silent: true,
barWidth: "8%",
itemStyle: {
borderColor: "transparent",
color: "transparent",
},
emphasis: {
itemStyle: {
borderColor: "transparent",
color: "transparent",
},
},
data: wellData.depthIntervals.map((item) => item.placeholder),
},
{
name: "深度区间",
type: "bar",
stack: "total",
barWidth: "6%",
label: {
show: true,
position: "insideTop",
color: "#fff",
fontSize: 11,
fontWeight: 600,
backgroundColor: "rgba(0,0,0,0.1)",
padding: [2, 3],
borderRadius: 4,
borderColor: "rgba(255,255,255,0.3)",
borderWidth: 1,
formatter: (params) => {
const di = wellData.depthIntervals[params.dataIndex];
return di && di.ztcc !== undefined ? di.ztcc : params.data;
},
},
itemStyle: {
color: {
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: colors.gradient.start },
{ offset: 1, color: colors.gradient.end },
],
},
borderRadius: [4, 4, 0, 0],
shadowColor: "rgba(0,0,0,0.2)",
shadowBlur: 8,
shadowOffsetY: 2,
},
emphasis: {
itemStyle: {
color: {
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: this.adjustColor(colors.gradient.start, 1.2),
},
{
offset: 1,
color: this.adjustColor(colors.gradient.end, 1.2),
},
],
},
shadowBlur: 12,
shadowOffsetY: 4,
},
},
// 使用 jc(进尺)作为柱子的高度,如果没有 jc 则使用 interval - placeholder 计算
data: wellData.depthIntervals.map((item) => {
// 优先使用 jc(进尺),如果没有则计算 interval - placeholder
return item.jc !== undefined && item.jc !== null
? item.jc
: item.interval - item.placeholder;
}),
},
// 底部显示 placeholder 数值的透明条,用于放置底部标签
{
name: "深度区间-底部标签",
type: "bar",
barGap: "-100%",
barWidth: "6%",
itemStyle: { color: "transparent", borderColor: "transparent" },
emphasis: {
itemStyle: { color: "transparent", borderColor: "transparent" },
},
label: {
show: true,
position: "insideBottom",
color: "#fff",
fontSize: 11,
fontWeight: 600,
backgroundColor: "rgba(0,0,0,0.1)",
padding: [2, 3],
borderRadius: 4,
borderColor: "rgba(255,255,255,0.3)",
borderWidth: 1,
distance: 0,
formatter: (params) => {
const di = wellData.depthIntervals[params.dataIndex];
// 显示深度区间,格式为:开始深度-结束深度 或 interval值
if (di && di.placeholder !== undefined && di.jc !== undefined) {
const endDepth = di.placeholder + di.jc;
return `${di.placeholder}-${endDepth}`;
}
return di && di.interval !== undefined
? di.interval
: params.data;
},
},
// 使用相同的计算方式,确保标签位置正确
data: wellData.depthIntervals.map((item) => {
return item.jc !== undefined && item.jc !== null
? item.jc
: item.interval - item.placeholder;
}),
},
...areaSeries,
];
},
// 渲染空状态 // 颜色调整工具方法
renderEmpty(chartDom) { adjustColor(color, factor) {
const colors = this.colorScheme; if (color.startsWith("#")) {
chartDom.innerHTML = ` const r = parseInt(color.slice(1, 3), 16);
const g = parseInt(color.slice(3, 5), 16);
const b = parseInt(color.slice(5, 7), 16);
const newR = Math.min(255, Math.round(r * factor));
const newG = Math.min(255, Math.round(g * factor));
const newB = Math.min(255, Math.round(b * factor));
return `rgb(${newR}, ${newG}, ${newB})`;
}
return color;
},
// 渲染空状态
renderEmpty(chartDom) {
const colors = this.colorScheme;
chartDom.innerHTML = `
<div class="empty-state" style=" <div class="empty-state" style="
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -813,363 +879,380 @@ export default { ...@@ -813,363 +879,380 @@ export default {
">请检查数据配置</div> ">请检查数据配置</div>
</div> </div>
`; `;
}, },
// 处理错误 // 处理错误
handleError(error) { handleError(error) {
console.error("图表错误:", error); console.error("图表错误:", error);
}, },
// 创建SVG图片 // 创建SVG图片
createSvgImage(svgString) { createSvgImage(svgString) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const svgBlob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' }); const svgBlob = new Blob([svgString], {
const svgUrl = URL.createObjectURL(svgBlob); type: "image/svg+xml;charset=utf-8",
const img = new Image(); });
img.onload = () => { const svgUrl = URL.createObjectURL(svgBlob);
URL.revokeObjectURL(svgUrl); const img = new Image();
resolve(img); img.onload = () => {
}; URL.revokeObjectURL(svgUrl);
img.onerror = (e) => { resolve(img);
URL.revokeObjectURL(svgUrl); };
reject(e); img.onerror = (e) => {
}; URL.revokeObjectURL(svgUrl);
img.src = svgUrl; reject(e);
}); };
}, img.src = svgUrl;
// 为右侧图例生成纹理样式 });
getLegendSwatchStyle(item = {}) { },
const baseStyle = { // 为右侧图例生成纹理样式
backgroundColor: '#d1d5db', getLegendSwatchStyle(item = {}) {
backgroundRepeat: 'repeat', const baseStyle = {
backgroundSize: '36px 36px' backgroundColor: "#d1d5db",
}; backgroundRepeat: "repeat",
if (!item?.svg) return baseStyle; backgroundSize: "36px 36px",
const dataUrl = this.getSvgDataUrl(item.svg); };
if (!dataUrl) return baseStyle; if (!item?.svg) return baseStyle;
return { const dataUrl = this.getSvgDataUrl(item.svg);
backgroundImage: `url("${dataUrl}")`, if (!dataUrl) return baseStyle;
backgroundRepeat: 'repeat', return {
backgroundSize: '36px 36px', backgroundImage: `url("${dataUrl}")`,
backgroundColor: 'transparent' backgroundRepeat: "repeat",
}; backgroundSize: "36px 36px",
}, backgroundColor: "transparent",
getSvgDataUrl(svgString) { };
if (!svgString || typeof svgString !== 'string') return ''; },
try { getSvgDataUrl(svgString) {
const compact = svgString.replace(/\s+/g, ' ').trim(); if (!svgString || typeof svgString !== "string") return "";
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(compact)}`; try {
} catch (e) { const compact = svgString.replace(/\s+/g, " ").trim();
console.warn('SVG 编码失败:', e); return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(
return ''; compact
} )}`;
}, } catch (e) {
console.warn("SVG 编码失败:", e);
return "";
}
},
// 图内层位名称整体去除,仅清理旧元素 // 图内层位名称整体去除,仅清理旧元素
drawStratumLabels() { drawStratumLabels() {
if (!this.myChart) return; if (!this.myChart) return;
this.myChart.setOption({ graphic: { elements: [] } }); this.myChart.setOption({ graphic: { elements: [] } });
} },
exportChart() {
if (!this.myChart) return;
const img = this.myChart.getDataURL({
type: "png",
pixelRatio: 2,
backgroundColor: "#fff",
});
const a = document.createElement("a");
a.href = img;
a.download = this.jh + "直方图.png";
a.click();
}, },
},
}; };
</script> </script>
<style scoped> <style scoped>
/* 容器样式优化 */ /* 容器样式优化 */
.chart-container { .chart-container {
width: 100%; width: 100%;
min-height: calc(100vh - 140px); min-height: calc(100vh - 140px);
padding: 0 10px; padding: 0 10px;
margin: 0; margin: 0;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
overflow: auto; overflow: auto;
animation: fadeIn 0.6s ease-out; animation: fadeIn 0.6s ease-out;
} }
.chart-layout { .chart-layout {
display: flex; display: flex;
flex: 1; flex: 1;
width: 100%; width: 100%;
gap: 5px; gap: 5px;
align-items: stretch; align-items: stretch;
justify-content: flex-start; justify-content: flex-start;
overflow: hidden; overflow: hidden;
} }
.strata-legend { .strata-legend {
width: 240px; width: 240px;
min-width: 240px; min-width: 240px;
max-height: 100%; max-height: 100%;
padding: 16px; padding: 16px;
border-radius: 16px; border-radius: 16px;
background: #fff; background: #fff;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
border: 1px solid rgba(15, 23, 42, 0.06); border: 1px solid rgba(15, 23, 42, 0.06);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
} }
.legend-header { .legend-header {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: #1f2937; color: #1f2937;
border-bottom: 1px solid rgba(15, 23, 42, 0.08); border-bottom: 1px solid rgba(15, 23, 42, 0.08);
padding-bottom: 8px; padding-bottom: 8px;
} }
.legend-list { .legend-list {
overflow-y: auto; overflow-y: auto;
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1px; gap: 1px;
padding-right: 4px; padding-right: 4px;
} }
.legend-item { .legend-item {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
padding: 0 4px; padding: 0 4px;
border-radius: 8px; border-radius: 8px;
transition: background 0.2s ease; transition: background 0.2s ease;
} }
.legend-item:hover { .legend-item:hover {
background-color: rgba(59, 130, 246, 0.08); background-color: rgba(59, 130, 246, 0.08);
} }
.legend-icon { .legend-icon {
width: 40px; width: 40px;
height: 24px; height: 24px;
border-radius: 6px; border-radius: 6px;
border: 1px solid rgba(15, 23, 42, 0.15); border: 1px solid rgba(15, 23, 42, 0.15);
background-color: #d1d5db; background-color: #d1d5db;
background-size: cover !important; background-size: cover !important;
} }
.legend-label { .legend-label {
flex: 1; flex: 1;
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
color: #1f2937; color: #1f2937;
} }
/* 井号显示样式 */ /* 井号显示样式 */
.well-number-display { .well-number-display {
position: absolute; position: absolute;
top: 16px; top: 16px;
left: 16px; left: 16px;
z-index: 5; z-index: 5;
background: transparent; background: transparent;
padding: 8px 12px; padding: 8px 12px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
transition: all 0.3s ease; transition: all 0.3s ease;
opacity: 0.9; opacity: 0.9;
} }
.well-number-display:hover { .well-number-display:hover {
opacity: 1; opacity: 1;
} }
.well-label { .well-label {
color: #6b7280; color: #6b7280;
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
white-space: nowrap; white-space: nowrap;
} }
.well-number { .well-number {
color: #3b82f6; color: #3b82f6;
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
} }
/* 图表容器样式 */ /* 图表容器样式 */
.chart { .chart {
flex: 1; flex: 1;
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
height: 100%; height: 100%;
min-height: 400px; min-height: 400px;
display: block; display: block;
border-radius: 16px; border-radius: 16px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border: 1px solid rgba(255, 255, 255, 0.8); border: 1px solid rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.chart:hover { .chart:hover {
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
/* transform: translateY(-2px); */ /* transform: translateY(-2px); */
} }
/* 加载状态样式 */ /* 加载状态样式 */
.loading-overlay { .loading-overlay {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.95);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 10; z-index: 10;
border-radius: 16px; border-radius: 16px;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
} }
.loading-spinner { .loading-spinner {
width: 48px; width: 48px;
height: 48px; height: 48px;
border: 4px solid #f3f4f6; border: 4px solid #f3f4f6;
border-top: 4px solid #3b82f6; border-top: 4px solid #3b82f6;
border-radius: 50%; border-radius: 50%;
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
margin-bottom: 16px; margin-bottom: 16px;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2); box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
} }
@keyframes spin { @keyframes spin {
0% { 0% {
transform: rotate(0deg); transform: rotate(0deg);
} }
100% { 100% {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
/* 响应式优化 */ /* 响应式优化 */
@media (max-width: 768px) { @media (max-width: 768px) {
.chart-container { .chart-container {
padding: 0 10px; padding: 0 10px;
} }
.chart { .chart {
min-height: 300px; min-height: 300px;
border-radius: 12px; border-radius: 12px;
} }
.well-number-display { .well-number-display {
top: 8px; top: 8px;
left: 8px; left: 8px;
padding: 6px 10px; padding: 6px 10px;
} }
.well-label { .well-label {
font-size: 11px; font-size: 11px;
} }
.well-number { .well-number {
font-size: 12px; font-size: 12px;
} }
} }
/* 深色模式支持 */ /* 深色模式支持 */
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.chart { .chart {
/* background: linear-gradient(135deg, #1f2937 0%, #111827 100%); */ /* background: linear-gradient(135deg, #1f2937 0%, #111827 100%); */
/* box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); */ /* box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); */
border: 1px solid rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.1);
} }
/* /*
.chart:hover { .chart:hover {
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
} */ } */
.loading-overlay { .loading-overlay {
background: rgba(17, 24, 39, 0.95); background: rgba(17, 24, 39, 0.95);
} }
.well-number-display { .well-number-display {
background: transparent; background: transparent;
} }
.well-number-display:hover { .well-number-display:hover {
background: transparent; background: transparent;
} }
.well-label { .well-label {
color: #6b7280; color: #6b7280;
} }
.well-number { .well-number {
color: #3b82f6; color: #3b82f6;
} }
} }
/* 动画效果 */ /* 动画效果 */
@keyframes fadeIn { @keyframes fadeIn {
from { from {
opacity: 0; opacity: 0;
transform: translateY(20px); transform: translateY(20px);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateY(0); transform: translateY(0);
} }
} }
/* 导出按钮样式 */ /* 导出按钮样式 */
.export-button-container { .export-button-container {
position: absolute; position: absolute;
top: 16px; top: 16px;
right: 16px; right: 16px;
z-index: 5; z-index: 5;
} }
.export-btn { .export-btn {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
padding: 8px 16px; padding: 8px 16px;
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
color: #fff; color: #fff;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3); box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
} }
.export-btn:hover:not(:disabled) { .export-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%); background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4); box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
transform: translateY(-1px); transform: translateY(-1px);
} }
.export-btn:active:not(:disabled) { .export-btn:active:not(:disabled) {
transform: translateY(0); transform: translateY(0);
box-shadow: 0 2px 6px rgba(59, 130, 246, 0.3); box-shadow: 0 2px 6px rgba(59, 130, 246, 0.3);
} }
.export-btn:disabled { .export-btn:disabled {
background: #9ca3af; background: #9ca3af;
cursor: not-allowed; cursor: not-allowed;
opacity: 0.6; opacity: 0.6;
box-shadow: none; box-shadow: none;
} }
.export-btn svg { .export-btn svg {
width: 16px; width: 16px;
height: 16px; height: 16px;
stroke: currentColor; stroke: currentColor;
} }
</style> </style>
\ No newline at end of file
<template> <template>
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"> <el-form
<el-form-item prop="jh"> :model="queryParams"
<el-input v-model="queryParams.jh" placeholder="请输入井号" clearable @keyup.enter.native="handleQuery" /> ref="queryForm"
</el-form-item> size="small"
<el-form-item> :inline="true"
v-show="showSearch"
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> >
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> <el-form-item prop="jh">
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd">新建井</el-button> <el-input
</el-form-item> v-model="queryParams.jh"
</el-form> placeholder="请输入井号"
<div class="card-container"> clearable
<el-card class="box-card" v-for="item in ytLists" :key="item.id" @keyup.enter.native="handleQuery"
style="margin: 10px 10px 0 0; width: calc(20% - 10px);"> />
<div slot="header" class="clearfix header"> </el-form-item>
<div class="header-content"> <el-form-item>
<div class="left"> <el-button
<img style="width: 80px;height: 80px" :src="wellImg" class="sidebar-logo" /> type="primary"
</div> icon="el-icon-search"
<div class="right"> size="mini"
<div> <!-- 使用v-for指令循环渲染每一条数据 --> @click="handleQuery"
<div style="font-size: 24px;color: #1c84c6;font-weight: bold;cursor: pointer" >搜索</el-button
@click="toDesignScheme(item)">{{ item.jh }} <!-- 在循环中使用当前数据项的属性 --> >
</div> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
<div> >重置</el-button
<i style="color: #909399" class="el-icon-user-solid"></i> >
<span style="color: #909399">{{ item.jd }}</span> <!-- 在循环中使用当前数据项的属性 --> <el-button
</div> type="primary"
</div> icon="el-icon-plus"
</div> size="mini"
</div> @click="handleAdd"
<div class="text item" style="display: flex; justify-content: flex-end;margin: 10px;"> >新建井</el-button
<el-button size="large" icon="el-icon-edit-outline" style="margin-right: 10px" type="text" >
@click="handleEdit(item)"></el-button> </el-form-item>
<el-button size="medium" icon="el-icon-delete" style="margin-right: 10px" type="text" </el-form>
@click="handleDelete(item)"></el-button> <div class="card-container">
<el-button size="medium" icon="el-icon-lock" style="margin-right: 10px" type="text" <el-card
@click="handleLock(item)"></el-button> class="box-card"
v-for="item in ytLists"
<!-- <el-button size="medium" icon="el-icon-unlock" style=" margin-right: 10px" type="text" @click="handleFinalize"></el-button> --> :key="item.id"
</div> style="margin: 10px 10px 0 0; width: calc(20% - 10px)"
>
<div slot="header" class="clearfix header">
<div class="header-content">
<div class="left">
<img
style="width: 80px; height: 80px"
:src="wellImg"
class="sidebar-logo"
/>
</div>
<div class="right">
<div>
<!-- 使用v-for指令循环渲染每一条数据 -->
<div
style="
font-size: 24px;
color: #1c84c6;
font-weight: bold;
cursor: pointer;
"
@click="toDesignScheme(item)"
>
{{ item.jh }}
<!-- 在循环中使用当前数据项的属性 -->
</div> </div>
</el-card> <div>
<i style="color: #909399" class="el-icon-user-solid"></i>
<span style="color: #909399">{{ item.jd }}</span>
</div>
</div>
</div>
</div>
<div
class="text item"
style="display: flex; justify-content: flex-end; margin: 10px"
>
<el-button
size="large"
icon="el-icon-edit-outline"
style="margin-right: 10px"
type="text"
@click="handleEdit(item)"
></el-button>
<el-button
size="medium"
icon="el-icon-delete"
style="margin-right: 10px"
type="text"
@click="handleDelete(item)"
></el-button>
<el-button
size="medium"
icon="el-icon-lock"
style="margin-right: 10px"
type="text"
@click="handleLock(item)"
></el-button>
<!-- <el-button size="medium" icon="el-icon-unlock" style=" margin-right: 10px" type="text" @click="handleFinalize"></el-button> -->
</div>
</div> </div>
</el-card>
</div>
<!-- 添加或修改菜单对话框 --> <!-- 添加或修改菜单对话框 -->
<el-dialog :title="title" :visible.sync="open" width="70%" append-to-body> <el-dialog :title="title" :visible.sync="open" width="70%" append-to-body>
<div
<div class="jsjStyle" class="jsjStyle"
style="background-color: #f4f8fe;border-radius: 10px;margin: -30px -10px -30px;padding: 10px;"> style="
<el-form ref="form" :model="form" :rules="rules" label-width="150px"> background-color: #f4f8fe;
<div class="cardStyle" style="border: 1px solid #eee"> border-radius: 10px;
<div style="font-size: 14px;padding: 10px;border-bottom: 1px solid #eee;margin-top: -10px;">基本信息 margin: -30px -10px -30px;
</div> padding: 10px;
<el-row style="margin-top: 10px"> "
<el-col :span="8"> >
<el-form-item label="钻井公司" prop="zjgs"> <el-form ref="form" :model="form" :rules="rules" label-width="150px">
<el-input style="width: 200px" v-model="form.zjgs" clearable /> <div class="cardStyle" style="border: 1px solid #eee">
</el-form-item> <div
</el-col> style="
<el-col :span="8"> font-size: 14px;
<el-form-item label="区块名称" prop="qkmc"> padding: 10px;
<el-select v-model="form.qkmc" placeholder="请选择区块名称" clearable filterable border-bottom: 1px solid #eee;
style="width: 200px;"> margin-top: -10px;
<el-option v-for="opt in filteredBlockOptions" :key="opt.value" "
:label="opt.label" :value="opt.value" /> >
</el-select> 基本信息
</el-form-item> </div>
</el-col> <el-row style="margin-top: 10px">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="油田公司" prop="ytgs"> <el-form-item label="钻井公司" prop="zjgs">
<el-input style="width: 200px" v-model="form.ytgs" clearable /> <el-input
</el-form-item> style="width: 200px"
</el-col> v-model="form.zjgs"
</el-row> clearable
<el-row> />
</el-form-item>
<el-col :span="8"> </el-col>
<el-form-item label="井号" prop="jh"> <el-col :span="8">
<el-input style="width: 200px" v-model="form.jh" clearable /> <el-form-item label="区块名称" prop="qkmc">
</el-form-item> <el-select
</el-col> v-model="form.qkmc"
<el-col :span="8"> placeholder="请选择区块名称"
<el-form-item label="井型" prop="jx"> clearable
<el-input style="width: 200px" v-model="form.jx" clearable /> filterable
</el-form-item> style="width: 200px"
</el-col> >
<el-col :span="8"> <el-option
<el-form-item label="井队" prop="jd"> v-for="opt in filteredBlockOptions"
<el-input style="width: 200px" v-model="form.jd" clearable /> :key="opt.value"
</el-form-item> :label="opt.label"
</el-col> :value="opt.value"
</el-row> />
<el-row> </el-select>
</el-form-item>
<el-col :span="8"> </el-col>
<el-form-item label="设计作业周期" prop="sjzyzq"> <el-col :span="8">
<el-input style="width: 200px" v-model="form.sjzyzq" clearable <el-form-item label="油田公司" prop="ytgs">
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')" <el-input
@blur="form.sjzyzq = $event.target.value" /> style="width: 200px"
</el-form-item> v-model="form.ytgs"
</el-col> clearable
<el-col :span="8"> />
<el-form-item label="井别" prop="jb"> </el-form-item>
<el-input style="width: 200px" v-model="form.jb" clearable /> </el-col>
</el-form-item> </el-row>
</el-col> <el-row>
</el-row> <el-col :span="8">
<el-form-item label="井号" prop="jh">
</div> <el-input style="width: 200px" v-model="form.jh" clearable />
<div class="cardStyle" style="border: 1px solid #eee;margin-top: 10px"> </el-form-item>
<div style="font-size: 14px;padding: 10px;border-bottom: 1px solid #eee;margin-top: -10px;">地质信息 </el-col>
</div> <el-col :span="8">
<el-row style="margin-top: 10px"> <el-form-item label="井型" prop="jx">
<el-col :span="8"> <el-input style="width: 200px" v-model="form.jx" clearable />
<el-form-item label="井口坐标(X)" prop="jkzbx"> </el-form-item>
<el-input style="width: 200px" v-model="form.jkzbx" clearable </el-col>
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')" <el-col :span="8">
@blur="form.jkzbx = $event.target.value" /> <el-form-item label="井队" prop="jd">
</el-form-item> <el-input style="width: 200px" v-model="form.jd" clearable />
</el-col> </el-form-item>
<el-col :span="8"> </el-col>
<el-form-item label="井口坐标(Y)" prop="jkzby"> </el-row>
<el-input style="width: 200px" v-model="form.jkzby" clearable <el-row>
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')" <el-col :span="8">
@blur="form.jkzby = $event.target.value" /> <el-form-item label="设计作业周期" prop="sjzyzq">
</el-form-item> <el-input
</el-col> style="width: 200px"
v-model="form.sjzyzq"
<el-col :span="8"> clearable
<el-form-item label="入靶点A坐标(X)" prop="rbdazb"> oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
<el-input style="width: 180px" v-model="form.rbdazb" clearable @blur="form.sjzyzq = $event.target.value"
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')" />
@blur="form.rbdazb = $event.target.value" /> </el-form-item>
</el-form-item> </el-col>
</el-col> <el-col :span="8">
</el-row> <el-form-item label="井别" prop="jb">
<el-row> <el-input style="width: 200px" v-model="form.jb" clearable />
<el-col :span="8"> </el-form-item>
<el-form-item label="入靶点A坐标(Y)" prop="rbdbzb"> </el-col>
<el-input style="width: 200px" v-model="form.rbdbzb" clearable </el-row>
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')" </div>
@blur="form.rbdbzb = $event.target.value" /> <div
</el-form-item> class="cardStyle"
</el-col> style="border: 1px solid #eee; margin-top: 10px"
<el-col :span="8"> >
<el-form-item label="地面海拔" prop="dmhb"> <div
<el-input style="width: 200px" v-model="form.dmhb" clearable style="
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')" font-size: 14px;
@blur="form.dmhb = $event.target.value" /> padding: 10px;
</el-form-item> border-bottom: 1px solid #eee;
</el-col> margin-top: -10px;
<el-col :span="8"> "
<el-form-item label="完井方式" prop="wjfs"> >
<el-input style="width: 180px" v-model="form.wjfs" clearable /> 地质信息
</el-form-item> </div>
</el-col> <el-row style="margin-top: 10px">
</el-row> <el-col :span="8">
<el-row> <el-form-item label="井口坐标(X)" prop="jkzbx">
<el-col :span="8"> <el-input
<el-form-item label="定稿设计井深m" prop="dgsjjs" style="width: 200px"> style="width: 200px"
<el-input style="width: 200px" v-model="form.dgsjjs" clearable v-model="form.jkzbx"
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')" clearable
@blur="form.dgsjjs = $event.target.value" /> oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
</el-form-item> @blur="form.jkzbx = $event.target.value"
</el-col> />
<el-col :span="8"> </el-form-item>
<el-form-item label="深度零点" prop="sdld"> </el-col>
<el-input style="width: 200px" v-model="form.sdld" clearable /> <el-col :span="8">
</el-form-item> <el-form-item label="井口坐标(Y)" prop="jkzby">
</el-col> <el-input
<el-col :span="8"> style="width: 200px"
<el-form-item label="定稿设计水平段长度" prop="dgsjspdcd"> v-model="form.jkzby"
<el-input style="width: 200px" v-model="form.dgsjspdcd" clearable clearable
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')" oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
@blur="form.dgsjspdcd = $event.target.value" /> @blur="form.jkzby = $event.target.value"
</el-form-item> />
</el-col> </el-form-item>
<!-- <el-col :span="8"> </el-col>
<el-col :span="8">
<el-form-item label="入靶点A坐标(X)" prop="rbdazb">
<el-input
style="width: 180px"
v-model="form.rbdazb"
clearable
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
@blur="form.rbdazb = $event.target.value"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="入靶点A坐标(Y)" prop="rbdbzb">
<el-input
style="width: 200px"
v-model="form.rbdbzb"
clearable
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
@blur="form.rbdbzb = $event.target.value"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="地面海拔" prop="dmhb">
<el-input
style="width: 200px"
v-model="form.dmhb"
clearable
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
@blur="form.dmhb = $event.target.value"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="完井方式" prop="wjfs">
<el-input
style="width: 180px"
v-model="form.wjfs"
clearable
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item
label="定稿设计井深m"
prop="dgsjjs"
style="width: 200px"
>
<el-input
style="width: 200px"
v-model="form.dgsjjs"
clearable
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
@blur="form.dgsjjs = $event.target.value"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="深度零点" prop="sdld">
<el-input
style="width: 200px"
v-model="form.sdld"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="定稿设计水平段长度" prop="dgsjspdcd">
<el-input
style="width: 200px"
v-model="form.dgsjspdcd"
clearable
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
@blur="form.dgsjspdcd = $event.target.value"
/>
</el-form-item>
</el-col>
<!-- <el-col :span="8">
<el-form-item label="目的层" prop="mdc"> <el-form-item label="目的层" prop="mdc">
<el-input style="width: 180px" v-if="!showCascader" v-model="form.mdc" readonly> <el-input style="width: 180px" v-if="!showCascader" v-model="form.mdc" readonly>
<template slot="append"> <template slot="append">
...@@ -189,20 +331,23 @@ ...@@ -189,20 +331,23 @@
<el-cascader v-if="showCascader" ref="cascader" v-model="form.mdc" @change="change" :props="mdc"></el-cascader> <el-cascader v-if="showCascader" ref="cascader" v-model="form.mdc" @change="change" :props="mdc"></el-cascader>
</el-form-item> </el-form-item>
</el-col> --> </el-col> -->
</el-row> </el-row>
<!-- <el-row> <!-- <el-row>
</el-row> --> </el-row> -->
</div> </div>
</el-form>
</el-form> <div
<div style="display: flex;justify-content: center;margin: 10px;" slot="footer" class="dialog-footer"> style="display: flex; justify-content: center; margin: 10px"
<el-button type="primary" @click="submitForm">确 定</el-button> slot="footer"
<el-button @click="cancel">取 消</el-button> class="dialog-footer"
</div> >
</div> <el-button type="primary" @click="submitForm">确 定</el-button>
</el-dialog> <el-button @click="cancel">取 消</el-button>
<!-- <el-dialog title="定稿" :visible.sync="openFinalize" width="300px" append-to-body> </div>
</div>
</el-dialog>
<!-- <el-dialog title="定稿" :visible.sync="openFinalize" width="300px" append-to-body>
<el-form ref="form" :model="finalizeForm" :rules="rules" label-width="90px"> <el-form ref="form" :model="finalizeForm" :rules="rules" label-width="90px">
<el-form-item label="井设计方案" prop="menuType"> <el-form-item label="井设计方案" prop="menuType">
<el-select v-model="finalizeForm.wellDesignScheme" placeholder="请选择"> <el-select v-model="finalizeForm.wellDesignScheme" placeholder="请选择">
...@@ -217,474 +362,509 @@ ...@@ -217,474 +362,509 @@
<el-button @click="cancelFinalizeForm">取 消</el-button> <el-button @click="cancelFinalizeForm">取 消</el-button>
</div> </div>
</el-dialog> --> </el-dialog> -->
</div>
<el-dialog title="算法选择" width="20%" :visible.sync="dialogFormVisibleAr">
<el-form :model="formAr">
<el-form-item label="" label-width="80px">
<el-radio-group
size="medium"
v-model="formAr.radio"
@change="handleChangeAr"
>
<el-radio label="3">固定算法</el-radio>
<el-radio label="6">智能算法</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<!-- <div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
</div> -->
</el-dialog>
</div>
</template> </template>
<script> <script>
import { getQkxl } from "@/api/system/jsaa"; import { getQkxl } from "@/api/system/jsaa";
import wellImg from '../../assets/images/home/well.png' import wellImg from "../../assets/images/home/well.png";
import { listFayh, deptTreeSelect, getBasinSelect, getBlockByPdid, deleteJtDjjc, addJtDjjc, getJtDjjc, updateJtDjjc, getMdcByQkid, LockJtDjjc } from "@/api/scientificDrill/schemeOptimization"; import {
import { blob } from 'd3'; listFayh,
deptTreeSelect,
getBasinSelect,
getBlockByPdid,
deleteJtDjjc,
addJtDjjc,
getJtDjjc,
updateJtDjjc,
getMdcByQkid,
LockJtDjjc,
} from "@/api/scientificDrill/schemeOptimization";
import { blob } from "d3";
import Treeselect from "@riophae/vue-treeselect"; import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css"; import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default { export default {
name: "tabsCard", name: "tabsCard",
components: { Treeselect }, components: { Treeselect },
dicts: ['jt_ytgs', 'jt_jx', 'jt_jb', 'jt_wjfs', 'jt_sdld', 'jt_qklx'], dicts: ["jt_ytgs", "jt_jx", "jt_jb", "jt_wjfs", "jt_sdld", "jt_qklx"],
props: { props: {
tabsId: { tabsId: {
type: String, type: String,
default: '' default: "",
},
}, },
},
watch: {
watch: { tabsId: {
tabsId: { handler(val) {
handler(val) { if (val) {
if (val) { this.getList();
this.getList();
}
},
immediate: true,
},
},
data() {
return {
blockOptions: [],
showCascader: false,
mdcOptions: [],
zjgs: "",
jh: "",
jhdm: "",
wellImg: wellImg,
ytLists: [],
// 显示搜索条件
showSearch: true,
// 查询参数
queryParams: {
jh: undefined,
pageNum: 1,
pageSize: 10
},
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
openFinalize: false,
// 表单参数
form: {
remark: "",
jh: "",
pd: "",
ytgs: "",
zjgs: "",
jd: "",
qkdm: "",
qkmc: "",
qkid: "",
jx: "",
sjzyzq: "",
jb: "",
jkzbx: "",
jkzby: "",
rbdazb: "",
rbdbzb: "",
dmhb: "",
wjfs: "",
dgsjjs: "",
sdld: "",
dgsjspdcd: "",
zjgs: "",
mdcid: "",
mdc: "",
},
finalizeForm: {},
// 表单校验
rules: {
jh: [
{ required: true, message: "井号不能为空", trigger: "blur" }
],
ytgs: [
{ required: true, message: "油田公司不能为空", trigger: "blur" }
],
zjgs: [
{ required: true, message: "钻井公司不能为空", trigger: "blur" }
],
jd: [
{ required: true, message: "井队不能为空", trigger: "blur" }
],
},
basinValue: "",
selectedBasinId: "",
basinOptions: [],
deptOptions: undefined,
blockOptions: [],
oilOptions: [],
wellTypeOptions: [],
wellClassifyOptions: [],
completionMethodOptions: [],
zeroDepthOptions: [],
wellDesignSchemeOptions: [],
pd: "",
mdc: {
lazy: true,
lazyLoad: this.loadTreeNode // 直接将 loadTreeNode 方法作为 lazyLoad 属性传递
},
selectedBasinId: null, // 添加一个用于存储选中盆地的变量
oilOptions: [] // 用于存储 MDC 数据
} }
},
immediate: true,
}, },
},
computed: { data() {
filteredBlockOptions() { return {
if (!Array.isArray(this.blockOptions)) return [] blockOptions: [],
return this.blockOptions showCascader: false,
.filter(item => item && (item.qk || item.qkmc)) mdcOptions: [],
.map(item => ({ label: item.qk || item.qkmc, value: item.qk || item.qkmc })) zjgs: "",
} jh: "",
jhdm: "",
wellImg: wellImg,
ytLists: [],
// 显示搜索条件
showSearch: true,
// 查询参数
queryParams: {
jh: undefined,
pageNum: 1,
pageSize: 10,
},
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
openFinalize: false,
// 表单参数
form: {
remark: "",
jh: "",
pd: "",
ytgs: "",
zjgs: "",
jd: "",
qkdm: "",
qkmc: "",
qkid: "",
jx: "",
sjzyzq: "",
jb: "",
jkzbx: "",
jkzby: "",
rbdazb: "",
rbdbzb: "",
dmhb: "",
wjfs: "",
dgsjjs: "",
sdld: "",
dgsjspdcd: "",
zjgs: "",
mdcid: "",
mdc: "",
},
finalizeForm: {},
// 表单校验
rules: {
jh: [{ required: true, message: "井号不能为空", trigger: "blur" }],
ytgs: [
{ required: true, message: "油田公司不能为空", trigger: "blur" },
],
zjgs: [
{ required: true, message: "钻井公司不能为空", trigger: "blur" },
],
jd: [{ required: true, message: "井队不能为空", trigger: "blur" }],
},
basinValue: "",
selectedBasinId: "",
basinOptions: [],
deptOptions: undefined,
blockOptions: [],
oilOptions: [],
wellTypeOptions: [],
wellClassifyOptions: [],
completionMethodOptions: [],
zeroDepthOptions: [],
wellDesignSchemeOptions: [],
pd: "",
mdc: {
lazy: true,
lazyLoad: this.loadTreeNode, // 直接将 loadTreeNode 方法作为 lazyLoad 属性传递
},
dialogFormVisibleAr: false,
formAr: {
radio: "",
},
sfcs: {},
selectedBasinId: null, // 添加一个用于存储选中盆地的变量
oilOptions: [], // 用于存储 MDC 数据
};
},
computed: {
filteredBlockOptions() {
if (!Array.isArray(this.blockOptions)) return [];
return this.blockOptions
.filter((item) => item && (item.qk || item.qkmc))
.map((item) => ({
label: item.qk || item.qkmc,
value: item.qk || item.qkmc,
}));
}, },
},
mounted() {
console.log('this.tabsId', this.tabsId); mounted() {
this.getList(); console.log("this.tabsId", this.tabsId);
this.getDeptTree(); this.getList();
this.getBlockOptions(); this.getDeptTree();
this.getBasinSelect(); this.getBlockOptions();
this.getBasinSelect();
},
methods: {
onBlockChange(val) {
this.form.qkmc = val || "";
},
/** 获取区块下拉选项 */
getBlockOptions() {
getQkxl().then((response) => {
// 过滤掉无效的选项
this.blockOptions = response.data.filter((item) => item && item.qk);
console.log(this.blockOptions, " this.blockOptions ");
});
},
/** 搜索按钮操作 */
handleQuery() {
this.getList();
}, },
methods: { /** 重置按钮操作 */
onBlockChange(val) { resetQuery() {
this.form.qkmc = val || ''; this.queryParams.jh = "";
}, this.resetForm("queryForm");
/** 获取区块下拉选项 */ this.queryParams.pageNum = 1;
getBlockOptions() { this.getList();
getQkxl().then(response => { },
// 过滤掉无效的选项 openCascader() {
this.blockOptions = response.data.filter(item => item && item.qk); this.showCascader = true;
console.log(this.blockOptions, ' this.blockOptions '); },
}); change(val) {
}, this.showCascader = false; // 选择后关闭级联选择器
/** 搜索按钮操作 */ let nodesObj = this.$refs["cascader"].getCheckedNodes();
handleQuery() { console.log(nodesObj, "nodesObj");
this.getList(); if (nodesObj && nodesObj.length > 0) {
}, const lastNode = nodesObj[nodesObj.length - 1];
this.form.mdc = lastNode.label; // 设置选中层的 mc 到表单中
/** 重置按钮操作 */ console.log(this.form.mdc, "this.form.mdc ");
resetQuery() { this.form.mdcid = lastNode.value; // 设置选中层的 id 到表单中
this.queryParams.jh = '' console.log(this.form.mdcid, "this.form.mdcid ");
this.resetForm("queryForm"); }
this.queryParams.pageNum = 1; },
this.getList()
},
openCascader() {
this.showCascader = true;
},
change(val) {
this.showCascader = false; // 选择后关闭级联选择器
let nodesObj = this.$refs['cascader'].getCheckedNodes();
console.log(nodesObj, 'nodesObj');
if (nodesObj && nodesObj.length > 0) {
const lastNode = nodesObj[nodesObj.length - 1];
this.form.mdc = lastNode.label; // 设置选中层的 mc 到表单中
console.log(this.form.mdc, 'this.form.mdc ');
this.form.mdcid = lastNode.value; // 设置选中层的 id 到表单中
console.log(this.form.mdcid, 'this.form.mdcid ');
}
},
// 查询
getList() {
// 创建一个本地变量,初始值为 props 中的 tabsId
let localTabsId = this.tabsId;
// 如果 localTabsId 为 "所有油田",则将其设置为空字符串
if (localTabsId === "所有油田") {
localTabsId = "";
}
const val = {
ytgs: localTabsId,
jh: this.queryParams.jh
}
listFayh(val).then(response => {
console.log('this.tabsId', localTabsId);
console.log('查询');
this.ytLists = response.rows;
console.log(this.ytLists, 'this.ytLists');
});
},
/** 查询部门树结构 */ // 查询
getDeptTree() { getList() {
deptTreeSelect({ level: "3" }).then(response => { // 创建一个本地变量,初始值为 props 中的 tabsId
this.deptOptions = response.data; let localTabsId = this.tabsId;
console.log(this.deptOptions, 'this.deptOptions'); // 如果 localTabsId 为 "所有油田",则将其设置为空字符串
}); if (localTabsId === "所有油田") {
}, localTabsId = "";
// 查询盆地结构 }
getBasinSelect() {
getBasinSelect().then(response => { const val = {
this.basinOptions = response; ytgs: localTabsId,
jh: this.queryParams.jh,
};
listFayh(val).then((response) => {
console.log("this.tabsId", localTabsId);
console.log("查询");
this.ytLists = response.rows;
console.log(this.ytLists, "this.ytLists");
});
},
console.log(this.basinOptions, ' this.basinOptions'); /** 查询部门树结构 */
// 不要在这里清空或覆盖 blockOptions,避免和区块下拉数据竞争 getDeptTree() {
// 仅在用户选择盆地后,再通过 basinChange 主动更新区块选项 deptTreeSelect({ level: "3" }).then((response) => {
this.deptOptions = response.data;
console.log(this.deptOptions, "this.deptOptions");
});
},
// 查询盆地结构
getBasinSelect() {
getBasinSelect().then((response) => {
this.basinOptions = response;
console.log(this.basinOptions, " this.basinOptions");
// 不要在这里清空或覆盖 blockOptions,避免和区块下拉数据竞争
// 仅在用户选择盆地后,再通过 basinChange 主动更新区块选项
});
},
// 盆地选项改变时触发 区块
basinChange() {
const selectedBasinName = this.form.pd; // 获取选中的盆地名称
console.log(selectedBasinName, "selectedBasinName");
const selectedBasin = this.basinOptions.find(
(item) => item.pdmc === selectedBasinName
); // 根据名称获取相应的盆地对象
console.log(selectedBasin, "selectedBasin");
if (selectedBasin) {
getBlockByPdid(selectedBasin.id).then((response) => {
// 使用盆地对象的ID来获取区块
this.blockOptions = response.rows;
});
}
},
}); // 获取 MDC 数据
}, // getMdc() {
// const params = {
// 盆地选项改变时触发 区块 // dclevel: '1',
basinChange() { // pid: ''
const selectedBasinName = this.form.pd; // 获取选中的盆地名称 // };
console.log(selectedBasinName, 'selectedBasinName'); // getMdcByQkid(params).then(response => {
const selectedBasin = this.basinOptions.find(item => item.pdmc === selectedBasinName); // 根据名称获取相应的盆地对象 // const oilOptions = response.data; // 保存获取到的数据
console.log(selectedBasin, 'selectedBasin'); // console.log(oilOptions, 'this.oilOptions');
if (selectedBasin) { // this.oilOptions = oilOptions; // 将数据存储到组件的属性中
getBlockByPdid(selectedBasin.id).then(response => { // 使用盆地对象的ID来获取区块 // this.loadTreeNode((nodes) => {
this.blockOptions = response.rows; // this.mdcOptions = nodes;
}); // }, oilOptions);
} // }).catch(error => {
}, // console.error('Error fetching MDC data:', error);
// });
// 获取 MDC 数据 // },
// getMdc() { loadTreeNode(node, resolve) {
// const params = { console.log(node, "node");
// dclevel: '1', console.log(resolve, "resolve");
// pid: '' const params = {
// }; dclevel: node.level + 1,
// getMdcByQkid(params).then(response => { pid: node.value,
// const oilOptions = response.data; // 保存获取到的数据 };
// console.log(oilOptions, 'this.oilOptions'); console.log(params, "params");
// this.oilOptions = oilOptions; // 将数据存储到组件的属性中 // 发起请求获取节点数据
// this.loadTreeNode((nodes) => { getMdcByQkid(params)
// this.mdcOptions = nodes; .then((response) => {
// }, oilOptions); const oilOptions = response.data; // 获取到的数据
// }).catch(error => { console.log(oilOptions, "this.oilOptions");
// console.error('Error fetching MDC data:', error); if (Array.isArray(oilOptions)) {
// }); // 将数据转换为符合 Cascader 要求的格式
// }, const nodes = oilOptions.map((item) => ({
loadTreeNode(node, resolve) { value: item.id,
console.log(node, 'node'); label: item.mc,
console.log(resolve, 'resolve'); leaf: item.dclevel >= 5,
const params = { }));
dclevel: node.level + 1, console.log(nodes, "nodes");
pid: node.value, resolve(nodes);
}; } else {
console.log(params, 'params'); // 处理异常情况
// 发起请求获取节点数据 }
getMdcByQkid(params).then(response => { })
const oilOptions = response.data; // 获取到的数据 .catch((error) => {
console.log(oilOptions, 'this.oilOptions'); // 处理请求失败的情况
if (Array.isArray(oilOptions)) { });
// 将数据转换为符合 Cascader 要求的格式 },
const nodes = oilOptions.map(item => ({
value: item.id,
label: item.mc,
leaf: item.dclevel >= 5
}));
console.log(nodes, 'nodes');
resolve(nodes);
} else {
// 处理异常情况
}
}).catch(error => {
// 处理请求失败的情况
//点击卡片
toDesignScheme(item) {
this.sfcs = item;
this.$store.dispatch("setJh", item.jh);
this.$store.dispatch("setJhdm", item.jhdm);
this.dialogFormVisibleAr = true;
},
//新建井
handleAdd() {
this.reset();
this.open = true;
this.title = "新增井设计";
},
//编辑
handleEdit(item) {
this.reset();
this.open = true;
console.log(item, "item");
const jhdm = item.jhdm;
getJtDjjc(jhdm).then((response) => {
this.form = response.data;
// this.form.mdc = response.data.mdcid;
console.log(response, "response");
this.open = true;
this.title = "修改";
});
this.title = "修改井设计";
},
/** 提交按钮 */
submitForm() {
// const pd = this.form.pd
// this.form.pdid = pd == '' ? '' : this.basinOptions.find(item => item.pdmc == pd).pdid
// const qkmc = this.form.qkmc
// this.form.qkdm = qkmc == '' ? '' : this.blockOptions.find(item => item.qkmc == qkmc).qkdm
this.$refs["form"].validate((valid) => {
if (valid) {
if (this.form.jhdm != null && this.form.jhdm !== "") {
// 如果 jhdm 存在且不为空,则执行更新操作
updateJtDjjc(this.form).then((response) => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
}); });
}, } else {
// 否则执行新增操作
//点击卡片 addJtDjjc(this.form).then((response) => {
toDesignScheme(item) { this.$modal.msgSuccess("新增成功");
this.$store.dispatch('setJh', item.jh); this.open = false;
this.$store.dispatch('setJhdm', item.jhdm); this.getList();
console.log(item, 'item');
this.$router.push({
path: "/wellDesign",
query: {
jhdm: item.jhdm,
jh: item.jh,
qkmc: item.qkmc
}
})
},
//新建井
handleAdd() {
this.reset();
this.open = true;
this.title = "新增井设计"
},
//编辑
handleEdit(item) {
this.reset();
this.open = true;
console.log(item, 'item');
const jhdm = item.jhdm;
getJtDjjc(jhdm).then(response => {
this.form = response.data;
// this.form.mdc = response.data.mdcid;
console.log(response, 'response');
this.open = true;
this.title = "修改";
});
this.title = "修改井设计"
},
/** 提交按钮 */
submitForm() {
// const pd = this.form.pd
// this.form.pdid = pd == '' ? '' : this.basinOptions.find(item => item.pdmc == pd).pdid
// const qkmc = this.form.qkmc
// this.form.qkdm = qkmc == '' ? '' : this.blockOptions.find(item => item.qkmc == qkmc).qkdm
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.jhdm != null && this.form.jhdm !== '') {
// 如果 jhdm 存在且不为空,则执行更新操作
updateJtDjjc(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
// 否则执行新增操作
addJtDjjc(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
}); });
}, }
}
// 表单重置 });
reset() { },
this.form = {
};
this.resetForm("form");
},
cancel() {
this.open = false;
},
/** 删除按钮操作 */
handleDelete(item) {
const jhdms = item.jhdm;
this.$modal.confirm('是否确认删除编号为"' + jhdms + '"的数据项?').then(function () {
return deleteJtDjjc(jhdms);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
/** 锁定按钮操作 */ // 表单重置
handleLock(item) { reset() {
const jhdm = item.jhdm; this.form = {};
this.$modal.confirm('是否确认锁定?').then(function () { this.resetForm("form");
return LockJtDjjc(jhdm); },
}).then(() => {
this.getList();
this.$modal.msgSuccess("已锁定");
}).catch(() => { });
},
cancel() {
this.open = false;
},
submitFinalizeForm() { /** 删除按钮操作 */
this.openFinalize = false handleDelete(item) {
const jhdms = item.jhdm;
this.$modal
.confirm('是否确认删除编号为"' + jhdms + '"的数据项?')
.then(function () {
return deleteJtDjjc(jhdms);
})
.then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
})
.catch(() => {});
},
}, /** 锁定按钮操作 */
cancelFinalizeForm() { handleLock(item) {
this.openFinalize = false const jhdm = item.jhdm;
this.$modal
.confirm("是否确认锁定?")
.then(function () {
return LockJtDjjc(jhdm);
})
.then(() => {
this.getList();
this.$modal.msgSuccess("已锁定");
})
.catch(() => {});
},
}, submitFinalizeForm() {
this.openFinalize = false;
},
cancelFinalizeForm() {
this.openFinalize = false;
},
} handleChangeAr() {
} if (this.radio == "3") {
var item = this.sfcs;
console.log(item, "item");
this.$router.push({
path: "/wellDesign",
query: {
jhdm: item.jhdm,
jh: item.jh,
qkmc: item.qkmc,
},
});
} else if (this.radio == "6") {
this.$message({
message: "正在开发中!",
type: "warning",
});
}
},
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.cardStyle { .cardStyle {
// border: 1px solid red; // border: 1px solid red;
border: 1px solid #dedede; border: 1px solid #dedede;
background-color: #fff; background-color: #fff;
border-radius: 5px; border-radius: 5px;
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.1);
padding: 12px; padding: 12px;
} }
.cardStyle:hover { .cardStyle:hover {
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2); box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2);
} }
::v-deep .el-form-item { ::v-deep .el-form-item {
margin-bottom: 10px; margin-bottom: 10px;
} }
.text { .text {
font-size: 14px; font-size: 14px;
} }
.clearfix:before, .clearfix:before,
.clearfix:after { .clearfix:after {
display: table; display: table;
content: ""; content: "";
} }
.clearfix:after { .clearfix:after {
clear: both clear: both;
} }
.card-container { .card-container {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
/* 当空间不足时换行 */ /* 当空间不足时换行 */
justify-content: flex-start; justify-content: flex-start;
/* 卡片从左向右排列 */ /* 卡片从左向右排列 */
align-items: flex-start; align-items: flex-start;
/* 卡片顶部对齐 */ /* 卡片顶部对齐 */
margin-top: -10px; margin-top: -10px;
} }
.box-card { .box-card {
flex: 0 0 auto; flex: 0 0 auto;
width: 300px; width: 300px;
} }
.header-content { .header-content {
display: flex; display: flex;
align-items: center; align-items: center;
/* 垂直居中对齐 */ /* 垂直居中对齐 */
} }
.left { .left {
margin-right: 10px; margin-right: 10px;
/* 左右间距 */ /* 左右间距 */
} }
.right { .right {
flex-grow: 1; flex-grow: 1;
/* 右侧内容占据剩余空间 */ /* 右侧内容占据剩余空间 */
} }
::v-deep .el-card__body { ::v-deep .el-card__body {
padding: 0 !important; padding: 0 !important;
} }
</style> </style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment