Commit 1548c99c by zhaopanyu

zpy

parent 93f8a227
......@@ -624,6 +624,15 @@ export function getXsView(query) {
});
}
// 井眼轨迹-斜深(系统接口)
export function getXsViews(query) {
return request({
url: "/system/jskd/jygjEw_xs",
method: "get",
params: query,
});
}
//井身结构图
// export function jsjgImg (query) {
// return request({
......@@ -660,8 +669,6 @@ export function addKcax(query) {
});
}
//多井钻头曲线图
export function getDjztqxt(query) {
return request({
......@@ -669,4 +676,4 @@ export function getDjztqxt(query) {
method: "get",
params: query,
});
}
\ No newline at end of file
}
......@@ -4,30 +4,28 @@
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
<el-button size="mini" circle icon="el-icon-search" @click="toggleSearch()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
<el-tooltip class="item" effect="dark" content="刷新" placement="top" v-if="showRefresh">
<el-button size="mini" circle icon="el-icon-refresh" @click="refresh()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" v-if="showColumnsType == 'transfer'" />
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px"
v-if="showColumnsType == 'checkbox'" :append-to-body="true">
<el-button size="mini" circle icon="el-icon-menu" />
<el-dropdown-menu slot="dropdown">
<template v-for="item in columns">
<el-dropdown-item :key="item.key">
<el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
</el-dropdown-item>
</template>
<el-dropdown-menu slot="dropdown" class="columns-dropdown">
<el-scrollbar wrap-class="columns-scroll-wrap" view-class="columns-scroll-view">
<template v-for="(item, idx) in columns">
<el-dropdown-item :key="item.key != null ? item.key : idx">
<el-checkbox :checked="item.visible" @change="checkboxChange($event, idx)" :label="item.label" />
</el-dropdown-item>
</template>
</el-scrollbar>
</el-dropdown-menu>
</el-dropdown>
</el-tooltip>
</el-row>
<el-dialog :title="title" :visible.sync="open" append-to-body>
<el-transfer
:titles="['显示', '隐藏']"
v-model="value"
:data="columns"
@change="dataChange"
></el-transfer>
<el-transfer :titles="['显示', '隐藏']" v-model="value" :data="columns" @change="dataChange"></el-transfer>
</el-dialog>
</div>
</template>
......@@ -54,6 +52,11 @@ export default {
columns: {
type: Array,
},
/* 是否显示刷新按钮 */
showRefresh: {
type: Boolean,
default: true,
},
/* 是否显示检索图标 */
search: {
type: Boolean,
......@@ -109,9 +112,9 @@ export default {
showColumn() {
this.open = true;
},
// 勾选
checkboxChange(event, label) {
this.columns.filter(item => item.label == label)[0].visible = event;
// 勾选 -> 抛事件给父组件,由父组件修改 columns
checkboxChange(visible, index) {
this.$emit('columns-change', { index, visible });
}
},
};
......@@ -123,7 +126,36 @@ export default {
display: block;
margin-left: 0px;
}
::v-deep .el-transfer__button:first-child {
margin-bottom: 10px;
}
</style>
<style>
/* 让列显隐下拉可滚动,支持两列展示,并追加到 body 防止裁剪 */
.columns-dropdown {
padding: 6px 0;
min-width: 420px;
}
.columns-scroll-wrap {
max-height: 60vh;
}
.columns-scroll-view {
padding-right: 6px;
column-count: 2;
column-gap: 16px;
column-fill: auto;
}
.columns-scroll-view .el-dropdown-item {
break-inside: avoid;
-webkit-column-break-inside: avoid;
}
.columns-scroll-view .el-dropdown-menu__item {
white-space: nowrap;
}
</style>
......@@ -377,7 +377,7 @@ export const dynamicRoutes = [
permissions: ["efficiencyAnalysis:djxx:list"],
children: [
{
path: "index/:jh/:famc/:qk/:jhs",
path: "index",
component: () => import("@/views/efficiencyAnalysis/djxx/detail"),
name: "DjxxDetail",
meta: {
......
<template>
<div class="drilling-chart-container">
<div id="drillingEfficiencyChartdj" class="chart"></div>
<!-- 最优值显示区域 - 图表内部左上角 -->
<div class="optimal-values">
<div class="optimal-item">
<span class="label">机速最优:</span>
<span class="value">{{ (chartData && chartData.optimalValues && chartData.optimalValues.jszy) || '--'
}}</span>
</div>
<div class="optimal-item">
<span class="label">进尺最优:</span>
<span class="value">{{ (chartData && chartData.optimalValues && chartData.optimalValues.jczy) || '--'
}}</span>
</div>
<div class="optimal-item">
<span class="label">综合最优:</span>
<span class="value">{{ (chartData && chartData.optimalValues && chartData.optimalValues.zhzy) || '--'
}}</span>
</div>
</div>
</div>
</template>
......@@ -31,10 +49,14 @@ export default {
};
},
mounted() {
this.getList();
// 不在挂载时自动请求数据,等待父组件触发 loadData()
this.$once('hook:beforeDestroy', this.cleanup);
},
methods: {
// 供父组件调用
loadData() {
this.getList();
},
getList() {
const params = {
zbid: this.id,
......@@ -255,10 +277,108 @@ export default {
.drilling-chart-container {
width: 100%;
height: 100%;
/* padding: 20px; */
position: relative;
padding: 20px;
box-sizing: border-box;
}
.optimal-values {
position: absolute;
top: 20px;
left: 20px;
z-index: 10;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(248, 250, 252, 0.95) 100%);
border: 1px solid rgba(64, 158, 255, 0.15);
border-radius: 6px;
padding: 6px 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.6);
backdrop-filter: blur(8px);
min-width: 140px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.optimal-values:hover {
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12), 0 3px 8px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);
transform: translateY(-2px);
border-color: rgba(64, 158, 255, 0.25);
}
.optimal-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
font-size: 11px;
padding: 2px 6px;
border-radius: 4px;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
}
.optimal-item:last-child {
margin-bottom: 0;
}
.optimal-item:hover {
background: linear-gradient(135deg, rgba(64, 158, 255, 0.08) 0%, rgba(103, 194, 58, 0.08) 100%);
transform: translateX(2px);
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
}
.optimal-item::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 0;
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
border-radius: 2px;
transition: height 0.2s ease;
}
.optimal-item:hover::before {
height: 60%;
}
.optimal-item .label {
color: #5a5e66;
font-weight: 500;
margin-right: 8px;
font-size: 11px;
letter-spacing: 0.2px;
opacity: 0.9;
}
.optimal-item .value {
color: #2c3e50;
font-weight: 700;
font-size: 11px;
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
position: relative;
}
.optimal-item .value::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
height: 1px;
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
opacity: 0;
transition: opacity 0.2s ease;
}
.optimal-item:hover .value::after {
opacity: 0.3;
}
.chart {
width: 100%;
height: calc(100vh - 220px);
......
......@@ -737,10 +737,10 @@ export default {
handler(newJh) {
if (newJh) {
this.queryParams.jh = newJh;
this.getList();
// 不自动请求,等待父级主动调用 loadData()
}
},
immediate: true
immediate: false
},
selectedRow: {
handler(val) {
......
......@@ -40,6 +40,11 @@ export default {
resizeObserver: null,
loading: false,
debounceTimer: null,
lastStackedAreas: null,
lastChartConfig: null,
lastXAxisLabels: null,
lastDepthIntervals: null,
currentGraphicElements: [],
};
},
computed: {
......@@ -89,14 +94,13 @@ export default {
watch: {
jh: {
handler(newVal) {
if (newVal) {
this.refreshChart();
}
// 不自动刷新,等待父级触发 loadData
},
immediate: true
immediate: false
}
},
mounted() {
// 初始化空图表,不拉数据;等待父组件触发 loadData
this.initChart();
this.setupEventListeners();
},
......@@ -241,6 +245,10 @@ export default {
throw error;
}
},
// 供父组件在切换到本tab时调用
async loadData() {
await this.initChart();
},
// 处理窗口大小变化
handleResize() {
......@@ -265,6 +273,12 @@ export default {
this.setChartDimensions(chartDom);
}
this.myChart.resize();
if (this.lastStackedAreas && this.lastChartConfig && this.lastXAxisLabels) {
this.drawStratumLabels(this.lastStackedAreas, this.lastChartConfig, this.lastXAxisLabels);
}
if (this.lastDepthIntervals && this.lastXAxisLabels && this.lastChartConfig) {
this.drawJhSeparators(this.lastDepthIntervals, this.lastXAxisLabels, this.lastChartConfig);
}
}
},
......@@ -296,6 +310,12 @@ export default {
// 确保图表完全渲染后再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);
this.lastDepthIntervals = wellData.depthIntervals || [];
this.drawJhSeparators(this.lastDepthIntervals, this.lastXAxisLabels, this.lastChartConfig);
});
} catch (error) {
......@@ -327,8 +347,8 @@ export default {
grid: {
top: 10,
left: "4%",
right: "3%",
bottom: "6%",
right: "4%",
bottom: "10%",
containLabel: true,
show: false,
},
......@@ -559,15 +579,19 @@ export default {
barWidth: "6%",
label: {
show: true,
position: (params) => params.data <= 200 ? "top" : "inside",
position: 'insideTop',
color: '#fff',
fontSize: 11,
fontWeight: 600,
backgroundColor: "rgba(0,0,0,0.1)",
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: {
......@@ -605,6 +629,37 @@ export default {
},
data: wellData.depthIntervals.map((item) => item.interval),
},
// 底部显示 interval 数值的透明条,用于放置底部标签
{
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];
return di && di.interval !== undefined ? di.interval : params.data;
}
},
data: wellData.depthIntervals.map((item) => item.interval)
},
...areaSeries,
];
},
......@@ -682,6 +737,209 @@ export default {
};
img.src = svgUrl;
});
},
// 使用像素坐标绘制地层标签(透明背景)
drawStratumLabels(stackedAreas, chartConfig, xAxisLabels) {
if (!this.myChart || !stackedAreas || stackedAreas.length === 0) return;
const totalDepth = chartConfig.yAxis.max - chartConfig.yAxis.min;
let currentDepth = chartConfig.yAxis.min;
this.myChart.setOption({ graphic: { elements: [] } });
const graphics = [];
const lastXIndex = Math.max(0, (xAxisLabels?.length || 1) - 1);
const xOffset = 16;
stackedAreas.forEach((area) => {
if (!area.points || area.points.length === 0) return;
const thickness = area.points[0].y;
const centerDepth = currentDepth + thickness / 2;
const pixelRight = this.myChart.convertToPixel({ xAxisIndex: 0, yAxisIndex: 0 }, [lastXIndex, centerDepth]);
if (!pixelRight || !Array.isArray(pixelRight) || pixelRight.length < 2) {
console.warn('convertToPixel 返回无效值:', pixelRight);
return;
}
const [pxRight, py] = pixelRight;
graphics.push({
type: 'text',
position: [pxRight + xOffset, py],
style: {
text: area.name,
fontSize: 12,
fontWeight: 600,
fill: '#333',
backgroundColor: 'transparent',
padding: [0, 0],
borderRadius: 0,
lineWidth: 0,
stroke: 'transparent',
align: 'left',
verticalAlign: 'middle'
},
origin: [0, 0],
z: 10,
bounding: 'raw',
silent: true
});
currentDepth += thickness;
});
this.currentGraphicElements = graphics;
this.myChart.setOption({ graphic: { elements: graphics } });
},
// 根据 depthIntervals 的顺序按 jh 分段,绘制虚线与顶部井号
drawJhSeparators(depthIntervals, xAxisLabels, chartConfig) {
if (!this.myChart || !depthIntervals || depthIntervals.length === 0) return;
// 扫描 depthIntervals 顺序,形成段
const segments = [];
let currentJh = null;
let currentMin = Infinity;
let currentMax = -Infinity;
let started = false;
for (let i = 0; i < depthIntervals.length; i++) {
// 直接使用在 depthIntervals 中的顺序索引作为 x 轴位置,
// 以避免相同 x 标签造成的覆盖与错位
const { jh } = depthIntervals[i];
const idx = i;
if (!started) {
currentJh = jh;
currentMin = currentMax = idx;
started = true;
continue;
}
if (jh === currentJh) {
currentMin = Math.min(currentMin, idx);
currentMax = Math.max(currentMax, idx);
} else {
segments.push({ jh: currentJh, startIdx: Math.min(currentMin, currentMax), endIdx: Math.max(currentMin, currentMax) });
// 开始新段
currentJh = jh;
currentMin = currentMax = idx;
}
}
if (started) {
segments.push({ jh: currentJh, startIdx: Math.min(currentMin, currentMax), endIdx: Math.max(currentMin, currentMax) });
}
const graphics = [];
// 垂直范围(像素)——考虑 y 轴 inverse,取像素最小为顶部、最大为底部
const yMinPx = this.myChart.convertToPixel({ yAxisIndex: 0 }, chartConfig.yAxis.min);
const yMaxPx = this.myChart.convertToPixel({ yAxisIndex: 0 }, chartConfig.yAxis.max);
if (yMinPx === null || yMaxPx === null) {
console.warn('convertToPixel 返回 null 值,跳过绘制井号分隔符');
return;
}
const yTopPx = Math.min(yMinPx, yMaxPx);
const yBottomPx = Math.max(yMinPx, yMaxPx);
// 边界虚线(段与段之间),黑色虚线
for (let i = 0; i < segments.length - 1; i++) {
const leftEnd = segments[i].endIdx;
const rightStart = segments[i + 1].startIdx;
const pxLeft = this.myChart.convertToPixel({ xAxisIndex: 0 }, leftEnd);
const pxRight = this.myChart.convertToPixel({ xAxisIndex: 0 }, rightStart);
if (pxLeft === null || pxRight === null) {
console.warn('convertToPixel 返回 null 值,跳过绘制边界虚线');
continue;
}
const midX = (pxLeft + pxRight) / 2;
graphics.push({
type: 'line',
shape: { x1: midX, y1: yTopPx, x2: midX, y2: yBottomPx },
style: { stroke: '#000', lineDash: [6, 6], lineWidth: 1 },
silent: true,
z: 100
});
}
// 每段顶部显示 jh(居中),带背景标签并做简单防重叠处理
let lastLabelRight = -Infinity;
let rowShift = 0; // 逐行上移避免覆盖
segments.forEach((seg, idx) => {
if (!seg.jh) return;
const pxStart = this.myChart.convertToPixel({ xAxisIndex: 0 }, seg.startIdx);
const pxEnd = this.myChart.convertToPixel({ xAxisIndex: 0 }, seg.endIdx);
if (pxStart === null || pxEnd === null) {
console.warn('convertToPixel 返回 null 值,跳过绘制井号标签');
return;
}
const midX = (pxStart + pxEnd) / 2;
let topY = yTopPx - 30; // 再上移,避免遮挡
// 估算标签宽度用于避免与上一个重叠
const fontSize = 12;
const paddingH = 6;
const estimatedWidth = seg.jh.length * fontSize * 0.6 + paddingH * 2 + 10;
const labelLeft = midX - estimatedWidth / 2;
const labelRight = midX + estimatedWidth / 2;
if (labelLeft < lastLabelRight) {
rowShift += 18; // 叠一行
} else {
rowShift = 0;
}
lastLabelRight = Math.max(lastLabelRight, labelRight);
topY -= rowShift;
const labelWidth = Math.max(estimatedWidth, 80);
const labelHeight = 22;
const pointerSize = 6;
graphics.push({
type: 'group',
x: midX - labelWidth / 2,
y: topY - labelHeight,
z: 101,
silent: true,
children: [
{
type: 'rect',
shape: { x: 0, y: 0, width: labelWidth, height: labelHeight, r: 8 },
style: {
fill: '#2563eb',
stroke: '#1e40af',
lineWidth: 1.5,
shadowBlur: 6,
shadowColor: 'rgba(37,99,235,0.35)'
}
},
{
type: 'polygon',
shape: {
points: [
[labelWidth / 2 - pointerSize, labelHeight],
[labelWidth / 2 + pointerSize, labelHeight],
[labelWidth / 2, labelHeight + pointerSize]
]
},
style: { fill: '#2563eb', stroke: '#1e40af' }
},
{
type: 'text',
style: {
x: labelWidth / 2,
y: labelHeight / 2,
text: seg.jh,
fill: '#fff',
fontSize: fontSize + 1,
fontWeight: 700,
align: 'center',
verticalAlign: 'middle'
}
}
]
});
});
// 叠加到现有 graphic 上(保留原有 graphic)
const prevElements = Array.isArray(this.currentGraphicElements) ? this.currentGraphicElements : [];
const merged = prevElements.concat(graphics);
this.currentGraphicElements = merged;
this.myChart.setOption({ graphic: { elements: merged } });
}
},
};
......@@ -697,7 +955,40 @@ export default {
box-sizing: border-box;
display: flex;
flex-direction: column;
/* position: relative; */
position: relative;
}
/* 井号显示样式 */
.well-number-display {
position: absolute;
top: 16px;
left: 16px;
z-index: 5;
background: transparent;
padding: 8px 12px;
display: flex;
align-items: center;
gap: 6px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
transition: all 0.3s ease;
opacity: 0.9;
}
.well-number-display:hover {
opacity: 1;
}
.well-label {
color: #6b7280;
font-size: 13px;
font-weight: 500;
white-space: nowrap;
}
.well-number {
color: #3b82f6;
font-size: 14px;
font-weight: 600;
}
/* 图表容器样式 */
......
......@@ -54,26 +54,111 @@ export default {
};
},
created() {
// 获取路由参数
// 获取路由参数(使用query)
console.log('详情页面created,完整路由对象:', this.$route);
const { id, jh, famc, qk, jhs } = this.$route.params;
console.log('路由参数:', { id, jh, famc, qk, jhs });
let { id, jh, famc, qk, jhs } = this.$route.query || {};
console.log('路由query参数:', { id, jh, famc, qk, jhs });
// 优先使用query,其次使用sessionStorage
if (!jh || jh === 'undefined' || jh === 'null') {
try {
const cached = JSON.parse(sessionStorage.getItem('djxxDetailParams') || '{}');
({ id, jh, famc, qk, jhs } = { ...cached, ...this.$route.query });
console.log('使用缓存参数:', { id, jh, famc, qk, jhs });
} catch (e) {
console.warn('读取缓存失败', e);
}
}
// 检查参数是否为空或undefined
if (jh && jh !== 'undefined' && jh !== 'null') {
this.queryParams.id = id;
this.queryParams.jh = jh;
this.queryParams.id = id || '';
this.queryParams.jh = jh || '';
this.queryParams.famc = famc || '';
this.queryParams.qk = qk || '';
this.queryParams.jhs = jhs || '';
console.log('设置 queryParams:', this.queryParams);
// 缓存参数,便于刷新后保留
sessionStorage.setItem('djxxDetailParams', JSON.stringify(this.queryParams));
console.log('设置并缓存 queryParams:', this.queryParams);
// 进入页面后,按当前激活的tab触发一次加载
this.$nextTick(() => this.refreshActiveTab());
} else {
console.warn('未获取到有效的路由参数 jh,当前参数:', { id, jh, famc, qk, jhs });
console.warn('未获取到有效的参数 jh');
}
// 确保第一次进入页面时不会自动初始化组件
console.log('页面初始化完成,等待用户点击tab');
},
activated() {
// 组件被 keep-alive 缓存时,返回本页会触发此钩子
const { id, jh, famc, qk, jhs } = this.$route.query || {};
if (jh && jh !== 'undefined' && jh !== 'null') {
this.queryParams.id = id || '';
this.queryParams.jh = jh || '';
this.queryParams.famc = famc || '';
this.queryParams.qk = qk || '';
this.queryParams.jhs = jhs || '';
sessionStorage.setItem('djxxDetailParams', JSON.stringify(this.queryParams));
this.$nextTick(() => this.refreshActiveTab());
} else {
try {
const cached = JSON.parse(sessionStorage.getItem('djxxDetailParams') || '{}');
if (cached && cached.jh) {
this.queryParams = { ...this.queryParams, ...cached };
this.$nextTick(() => this.refreshActiveTab());
}
} catch (e) { }
}
},
beforeRouteUpdate(to, from, next) {
// 同一路由切换不同 query 时触发
const { id, jh, famc, qk, jhs } = to.query || {};
if (jh && jh !== 'undefined' && jh !== 'null') {
this.queryParams.id = id || '';
this.queryParams.jh = jh || '';
this.queryParams.famc = famc || '';
this.queryParams.qk = qk || '';
this.queryParams.jhs = jhs || '';
sessionStorage.setItem('djxxDetailParams', JSON.stringify(this.queryParams));
this.$nextTick(() => this.refreshActiveTab());
}
next();
},
watch: {
// 当从列表页连续点击"查看"跳转到同一路由时,组件会复用,这里监听路由变化并刷新当前激活tab
'$route.query'(to) {
const { id, jh, famc, qk, jhs } = to || {};
if (jh && jh !== 'undefined' && jh !== 'null') {
this.queryParams.id = id || '';
this.queryParams.jh = jh || '';
this.queryParams.famc = famc || '';
this.queryParams.qk = qk || '';
this.queryParams.jhs = jhs || '';
sessionStorage.setItem('djxxDetailParams', JSON.stringify(this.queryParams));
// 当前就在某个tab上,路由参数变更后需要立刻刷新该tab的数据
this.$nextTick(() => {
if (this.activeTab === 'dataTable') {
this.$refs.dataTableRef && this.$refs.dataTableRef.loadData && this.$refs.dataTableRef.loadData();
} else if (this.activeTab === 'curveGraph') {
this.$refs.curveGraphRef && this.$refs.curveGraphRef.loadData && this.$refs.curveGraphRef.loadData();
} else if (this.activeTab === 'histogramGraph') {
this.$refs.histogramGraphRef && this.$refs.histogramGraphRef.loadData && this.$refs.histogramGraphRef.loadData();
}
});
}
}
},
methods: {
refreshActiveTab() {
// 每次调用都执行loadData
if (this.activeTab === 'dataTable') {
this.$refs.dataTableRef && this.$refs.dataTableRef.loadData && this.$refs.dataTableRef.loadData();
} else if (this.activeTab === 'curveGraph') {
this.$refs.curveGraphRef && this.$refs.curveGraphRef.loadData && this.$refs.curveGraphRef.loadData();
} else if (this.activeTab === 'histogramGraph') {
this.$refs.histogramGraphRef && this.$refs.histogramGraphRef.loadData && this.$refs.histogramGraphRef.loadData();
}
},
/** 搜索按钮操作 */
handleQuery() {
this.loading = true;
......@@ -105,6 +190,8 @@ export default {
console.log('切换到tab:', tab.name);
// 标记已经不是第一次加载
this.isFirstLoad = false;
// 每次点击tab都调用loadData
if (tab.name === 'dataTable') {
this.$refs.dataTableRef && this.$refs.dataTableRef.loadData && this.$refs.dataTableRef.loadData();
} else if (tab.name === 'curveGraph') {
......
......@@ -137,7 +137,7 @@
<template slot="footer">
<span class="dialog-footer">
<el-button @click="handleWellCancel">取 消</el-button>
<el-button type="primary" @click="handleWellSubmit">提 交</el-button>
<el-button type="primary" @click="handleWellSubmit">确 定</el-button>
</span>
</template>
</el-dialog>
......@@ -322,7 +322,7 @@ export default {
this.$router.push({
name: 'DjxxDetail',
params: params
query: params
}).then(() => {
console.log('路由跳转成功');
}).catch(err => {
......@@ -380,6 +380,25 @@ export default {
// 选择邻井
handleSelectAdjacentWell() {
console.log('点击选择邻井输入框')
// 验证必要参数是否已填写
if (!this.formData.qk) {
this.$message.warning('请先选择区块')
return
}
if (!this.formData.jh) {
this.$message.warning('请先输入井号')
return
}
if (!this.formData.jl) {
this.$message.warning('请先输入距离')
return
}
if (!this.formData.wjsjks || !this.formData.wjsjjs) {
this.$message.warning('请先选择完井时间')
return
}
this.dialogVisible = false
this.wellDialogVisible = true
......
<template>
<div class="drilling-chart-container" v-loading="loading">
<!-- 最优值显示区域 - 左上角紧凑版 -->
<div class="optimal-values">
<div class="optimal-item">
<span class="label">机速最优:</span>
<span class="value">{{ (chartData && chartData.optimalValues && chartData.optimalValues.jszy) || '--'
}}</span>
</div>
<div class="optimal-item">
<span class="label">进尺最优:</span>
<span class="value">{{ (chartData && chartData.optimalValues && chartData.optimalValues.jczy) || '--'
}}</span>
</div>
<div class="optimal-item">
<span class="label">综合最优:</span>
<span class="value">{{ (chartData && chartData.optimalValues && chartData.optimalValues.zhzy) || '--'
}}</span>
</div>
</div>
<div id="drillingEfficiencyChartDJ" class="chart"></div>
</div>
</template>
......@@ -53,7 +71,7 @@ export default {
},
getList() {
const params = { zbid: this.zbid,ztccs:this.ztcc ,qk:this.qk};
const params = { zbid: this.zbid, ztccs: this.ztcc, qk: this.qk };
this.loading = true;
getDjztqxt(params)
.then(res => {
......@@ -231,10 +249,107 @@ export default {
.drilling-chart-container {
width: 100%;
height: 100%;
position: relative;
padding: 20px;
box-sizing: border-box;
}
.optimal-values {
position: absolute;
top: 15px;
left: 15px;
z-index: 10;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(248, 250, 252, 0.95) 100%);
border: 1px solid rgba(64, 158, 255, 0.15);
border-radius: 4px;
padding: 6px 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.6);
backdrop-filter: blur(8px);
min-width: 120px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.optimal-values:hover {
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);
border-color: rgba(64, 158, 255, 0.25);
}
.optimal-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 3px;
font-size: 10px;
padding: 2px 4px;
border-radius: 3px;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
}
.optimal-item:last-child {
margin-bottom: 0;
}
.optimal-item:hover {
background: linear-gradient(135deg, rgba(64, 158, 255, 0.08) 0%, rgba(103, 194, 58, 0.08) 100%);
transform: translateX(2px);
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
}
.optimal-item::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 0;
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
border-radius: 2px;
transition: height 0.2s ease;
}
.optimal-item:hover::before {
height: 60%;
}
.optimal-item .label {
color: #5a5e66;
font-weight: 500;
margin-right: 6px;
font-size: 9px;
letter-spacing: 0.1px;
opacity: 0.9;
}
.optimal-item .value {
color: #2c3e50;
font-weight: 700;
font-size: 10px;
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
position: relative;
}
.optimal-item .value::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
height: 1px;
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
opacity: 0;
transition: opacity 0.2s ease;
}
.optimal-item:hover .value::after {
opacity: 0.3;
}
.chart {
width: 100%;
height: calc(100vh - 220px);
......
......@@ -3,15 +3,13 @@
<div class="app-container">
<el-tabs v-model="activeTab" type="card" @tab-click="handleTabClick" style="margin-top: -10px;">
<el-tab-pane label="数据表格" name="dataTable">
<DataTable ref="dataTableRef" :jh="queryParams.jh" :famc="queryParams.famc" :qk="queryParams.qk" :ztcc="queryParams.ztcc"
:jhs="queryParams.jhs" />
<DataTable ref="dataTableRef" :jh="queryParams.jh" :famc="queryParams.famc" :qk="queryParams.qk"
:ztcc="queryParams.ztcc" :jhs="queryParams.jhs" />
</el-tab-pane>
<el-tab-pane label="曲线图形" name="curveGraph">
<CurveGraph ref="curveGraphRef" :jh="queryParams.jh" :famc="queryParams.famc" :qk="queryParams.qk"
:jhs="queryParams.jhs" :zbid="queryParams.zbid" :ztcc="queryParams.ztcc" />
<CurveGraph ref="curveGraphRef" :jh="queryParams.jh" :famc="queryParams.famc" :qk="queryParams.qk"
:jhs="queryParams.jhs" :zbid="queryParams.zbid" :ztcc="queryParams.ztcc" />
</el-tab-pane>
</el-tabs>
</div>
</template>
......@@ -42,8 +40,8 @@ export default {
famc: '',
qk: '',
jhs: '',
ztcc:'',
zbid:''
ztcc: '',
zbid: ''
},
// 标记是否是第一次进入页面
isFirstLoad: true
......@@ -52,18 +50,18 @@ export default {
created() {
// 获取路由参数
console.log('详情页面created,完整路由对象:', this.$route);
const { jh, famc, qk, jhs ,ztcc,zbid} = this.$route.params;
console.log('路由参数:', { jh, famc, qk, jhs,ztcc ,zbid});
const { jh, famc, qk, jhs, ztcc, zbid } = this.$route.params;
console.log('路由参数:', { jh, famc, qk, jhs, ztcc, zbid });
// 检查参数是否为空或undefined
// if (jh && jh !== 'undefined' && jh !== 'null') {
this.queryParams.jh = jh;
this.queryParams.famc = famc || '';
this.queryParams.qk = qk || '';
this.queryParams.jhs = jhs || '';
this.queryParams.ztcc= ztcc||''
this.queryParams.zbid= zbid||''
console.log('设置 queryParams:', this.queryParams);
this.queryParams.jh = jh;
this.queryParams.famc = famc || '';
this.queryParams.qk = qk || '';
this.queryParams.jhs = jhs || '';
this.queryParams.ztcc = ztcc || ''
this.queryParams.zbid = zbid || ''
console.log('设置 queryParams:', this.queryParams);
// } else {
// console.warn('未获取到有效的路由参数 jh,当前参数:', { jh, famc, qk, jhs,ztcc });
// }
......@@ -113,14 +111,12 @@ export default {
console.log('切换到tab:', tab.name);
// 标记已经不是第一次加载
this.isFirstLoad = false;
// 只有在用户主动点击tab时才加载数据,避免初始化时的自动加载
// 每次点击tab都调用loadData
if (tab.name === 'dataTable') {
this.$refs.dataTableRef && this.$refs.dataTableRef.loadData && this.$refs.dataTableRef.loadData();
} else if (tab.name === 'curveGraph') {
this.$refs.curveGraphRef && this.$refs.curveGraphRef.loadData && this.$refs.curveGraphRef.loadData();
} else if (tab.name === 'histogramGraph') {
this.$refs.histogramGraphRef && this.$refs.histogramGraphRef.loadData && this.$refs.histogramGraphRef.loadData();
}
}
}
......
......@@ -23,19 +23,20 @@
<!-- 表格区域 -->
<el-table :data="tableData" border stripe v-loading="loading" style="width: 100%; height: calc(100vh - 230px)">
<el-table-column prop="qk" label="区块" min-width="120"></el-table-column>
<el-table-column prop="famc" label="方案名称" min-width="200" align="center" show-overflow-tooltip sortable />
<el-table-column prop="ztcc" label="钻头尺寸" min-width="150"></el-table-column>
<el-table-column prop="dc" label="地层" min-width="150"></el-table-column>
<el-table-column prop="yxzt" label="优选钻头" min-width="200" align="center" show-overflow-tooltip sortable />
<el-table-column prop="bz" label="备注" min-width="200" show-overflow-tooltip></el-table-column>
<el-table-column prop="qk" label="区块" align="center" min-width="120"></el-table-column>
<el-table-column prop="famc" label="方案名称" align="center" min-width="200" show-overflow-tooltip sortable />
<el-table-column prop="ztcc" label="钻头尺寸" align="center" min-width="150"></el-table-column>
<el-table-column prop="dc" label="地层" align="center" min-width="150"></el-table-column>
<el-table-column prop="yxzt" label="优选钻头" align="center" min-width="200" show-overflow-tooltip sortable />
<el-table-column prop="bz" label="备注" align="center" min-width="200"
show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">查看</el-button>
<el-button type="text" size="mini" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="mini" @click="handleDelete(scope.row)">删除</el-button>
<el-button type="text" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
......@@ -47,43 +48,64 @@
@current-change="handleCurrentChange" background />
<!-- 新增/编辑对话框 -->
<el-dialog :title="dialogTitle" width="600px" :close-on-click-modal="false" :visible.sync="dialogVisible">
<el-dialog :title="dialogTitle" width="900px" :close-on-click-modal="false" :visible.sync="dialogVisible">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
<el-form-item label="方案名称" prop="famc">
<el-input v-model="formData.famc" placeholder="请输入方案名称" />
</el-form-item>
<el-form-item label="区块" prop="qk">
<el-select v-model="formData.qk" placeholder="请选择区块名称" clearable filterable style="width: 100%;">
<el-option v-for="item in blockOptions" :key="item.qk" :label="item.qk" :value="item.qk"
v-if="item && item.qk" style="width: 100%;" />
</el-select>
</el-form-item>
<el-form-item label="钻头尺寸" prop="ztcc">
<el-select v-model="formData.ztcc" multiple filterable allow-create default-first-option
placeholder="请选择或输入钻头尺寸" style="width: 100%" :loading="ztxxLoading">
<el-option v-for="item in ztxxOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="层位" prop="cw">
<el-input v-if="!showCascader" v-model="formData.cw" readonly style="width: 100%;">
<template slot="append">
<el-button @click="openCascader">选择</el-button>
</template>
</el-input>
<!-- 使用v-if来条件渲染el-cascader -->
<el-cascader v-if="showCascader" ref="cascader" v-model="cascaderValue" @change="change"
@click="handleCascaderClick" :options="mdcOptions" :props="mdc" placeholder="请选择层位"
style="width: 200px;" popper-class="cascader-popper">
</el-cascader>
</el-form-item>
<el-form-item label="备注" prop="bz">
<el-input v-model="formData.bz" type="textarea" :rows="3" placeholder="请输入备注信息"></el-input>
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="方案名称" prop="famc">
<el-input v-model="formData.famc" placeholder="请输入方案名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="区块" prop="qk">
<el-select v-model="formData.qk" placeholder="请选择区块名称" clearable filterable
style="width: 100%;">
<el-option v-for="item in blockOptions" :key="item.qk" :label="item.qk" :value="item.qk"
v-if="item && item.qk" style="width: 100%;" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="完井时间" prop="wjsjks" required>
<el-date-picker v-model="formData.wjsjks" type="year" placeholder="选择年份" value-format="yyyy"
style="width: 50%;" />
<el-date-picker v-model="formData.wjsjjs" type="year" placeholder="选择年份" value-format="yyyy"
style="width: 50%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="层位" prop="cw">
<el-input v-if="!showCascader" v-model="formData.cw" readonly style="width: 100%;">
<template slot="append">
<el-button @click="openCascader">选择</el-button>
</template>
</el-input>
<!-- 使用v-if来条件渲染el-cascader -->
<el-cascader v-if="showCascader" ref="cascader" v-model="cascaderValue" @change="change"
@click="handleCascaderClick" :options="mdcOptions" :props="mdc" placeholder="请选择层位"
style="width: 200px;" popper-class="cascader-popper">
</el-cascader>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="钻头尺寸" prop="ztcc">
<el-select v-model="formData.ztcc" multiple filterable allow-create default-first-option
placeholder="请选择或输入钻头尺寸" style="width: 100%" :loading="ztxxLoading">
<el-option v-for="item in ztxxOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注" prop="bz">
<el-input v-model="formData.bz" type="textarea" :rows="3" placeholder="请输入备注信息"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
......@@ -144,6 +166,8 @@ export default {
cwid: null, // 层位ID
mdcid: null, // 目的层ID
dzsdmc: '', // 地质时代名称
wjsjks: (new Date().getFullYear() - 4).toString(), // 完井开始年份默认近五年
wjsjjs: new Date().getFullYear().toString(), // 完井结束年份默认当前年
bz: ''
},
// 表单验证规则
......@@ -158,20 +182,30 @@ export default {
ztxxLoading: false,
}
},
watch: {
'formData.qk'() {
this.updateZtccOptionsIfReady()
},
'formData.wjsjks'() {
this.updateZtccOptionsIfReady()
},
'formData.wjsjjs'() {
this.updateZtccOptionsIfReady()
}
},
created() {
this.getBlockOptions();
this.getZtxxOptions();
this.getList();
},
mounted() {
this.getTableData()
},
methods: {
/** 查看按钮操作 */
async handleView(row) {
/** 查看按钮操作 */
async handleView(row) {
try {
const res = await getDjzt(row.id)
console.log(res,55555);
console.log(res, 55555);
if (res.code === 200) {
// 将获取到的详细信息传递给详情页面
const params = {
......@@ -197,27 +231,27 @@ export default {
this.$message.error('获取详情失败')
}
},
/** 初始化目的层选项 */
initMdcOptions() {
// 加载根节点
const params = {
dclevel: 1,
pid: null,
};
getMdcByQkid(params).then(response => {
const options = response.data.map(item => ({
value: item.id,
label: item.mc,
leaf: false,
level: 1 // 设置根节点层级
}));
this.mdcOptions = options;
}).catch(error => {
console.error('初始化目的层选项失败:', error);
this.mdcOptions = [];
});
},
/** 初始化目的层选项 */
initMdcOptions() {
// 加载根节点
const params = {
dclevel: 1,
pid: null,
};
getMdcByQkid(params).then(response => {
const options = response.data.map(item => ({
value: item.id,
label: item.mc,
leaf: false,
level: 1 // 设置根节点层级
}));
this.mdcOptions = options;
}).catch(error => {
console.error('初始化目的层选项失败:', error);
this.mdcOptions = [];
});
},
// 目的层相关方法
openCascader() {
this.showCascader = true;
......@@ -254,6 +288,8 @@ export default {
this.showCascader = false;
// 强制更新视图
this.$forceUpdate();
// 层位选择完成后尝试加载钻头尺寸
this.updateZtccOptionsIfReady();
}, 100);
} else {
this.showCascader = false;
......@@ -382,15 +418,16 @@ export default {
});
},
/** 获取钻头信息下拉选项 */
getZtxxOptions() {
getZtxxOptions(queryParams) {
this.ztxxLoading = true; // 开始加载
selectZtccList().then(response => {
console.log(response,22222);
selectZtccList(queryParams).then(response => {
if (response.code === 200 && response.data && Array.isArray(response.data)) {
this.ztxxOptions = response.data.map(item => ({
value: item,
label: item
}))
} else {
this.ztxxOptions = []
}
}).catch(error => {
console.error('获取钻头信息选项失败:', error)
......@@ -399,6 +436,14 @@ export default {
this.ztxxLoading = false; // 加载完成
});
},
// 条件满足后再请求钻头尺寸
updateZtccOptionsIfReady() {
const { qk, wjsjks, wjsjjs, cwid, mdcid } = this.formData;
if (qk && wjsjks && wjsjjs && (cwid || mdcid)) {
const params = { qk, wjsjks, wjsjjs, cwid: cwid || undefined, mdcid: mdcid || undefined };
this.getZtxxOptions(params);
}
},
// 获取表格数据
async getTableData() {
this.loading = true
......@@ -410,7 +455,7 @@ export default {
}
const res = await listDjzt(params)
if (res.code === 200) {
this.tableData = res.rows || []
this.pagination.total = res.total || 0
......@@ -448,7 +493,7 @@ export default {
this.resetForm("formData");
this.showCascader = false;
this.cascaderValue = []; // 重置 cascaderValue
this.getZtxxOptions(); // 重新获取钻头信息选项
this.ztxxOptions = []; // 清空钻头信息选项,等待条件满足后再加载
},
// 新增
......@@ -462,9 +507,13 @@ export default {
cwid: null, // 层位ID
mdcid: null, // 目的层ID
dzsdmc: '', // 地质时代名称
wjsjks: (new Date().getFullYear() - 4).toString(),
wjsjjs: new Date().getFullYear().toString(),
bz: ''
}
this.dialogVisible = true
// 新增打开时尝试基于默认年份加载(需等区块与层位选择后)
this.ztxxOptions = []
},
// 编辑
......@@ -478,10 +527,14 @@ export default {
cwid: row.cwid || null, // 层位ID
mdcid: row.mdcid || null, // 目的层ID
dzsdmc: row.dzsdmc || '', // 地质时代名称
wjsjks: row.wjsjks || (new Date().getFullYear() - 4).toString(),
wjsjjs: row.wjsjjs || new Date().getFullYear().toString(),
bz: row.bz,
famc:row.famc
famc: row.famc
}
this.dialogVisible = true
// 编辑时若条件已满足,立即加载钻头尺寸
this.updateZtccOptionsIfReady()
},
// 删除
......@@ -493,7 +546,7 @@ export default {
}).then(async () => {
try {
const res = await delDjzt(row.id)
if (res.code === 200) {
this.$message.success('删除成功')
this.getTableData()
......
<template>
<div class="drilling-chart-container">
<div id="drillingEfficiencyChart" class="chart"></div>
<!-- 最优值显示区域 - 图表内部左上角 -->
<div class="optimal-values">
<div class="optimal-item">
<span class="label">机速最优:</span>
<span class="value">{{ (chartData && chartData.optimalValues && chartData.optimalValues.jszy) || '--'
}}</span>
</div>
<div class="optimal-item">
<span class="label">进尺最优:</span>
<span class="value">{{ (chartData && chartData.optimalValues && chartData.optimalValues.jczy) || '--'
}}</span>
</div>
<div class="optimal-item">
<span class="label">综合最优:</span>
<span class="value">{{ (chartData && chartData.optimalValues && chartData.optimalValues.zhzy) || '--'
}}</span>
</div>
</div>
</div>
</template>
......@@ -204,7 +222,7 @@ export default {
type: 'scatter',
data: scatter.filter(item => item[2] === type),
symbolSize: 12,
itemStyle: {
itemStyle: {
color: 'blue',
borderColor: 'red',
borderWidth: 3,
......@@ -246,10 +264,107 @@ export default {
.drilling-chart-container {
width: 100%;
height: 100%;
/* padding: 20px; */
position: relative;
box-sizing: border-box;
}
.optimal-values {
position: absolute;
top: 5px;
left: 5px;
z-index: 10;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(248, 250, 252, 0.95) 100%);
border: 1px solid rgba(64, 158, 255, 0.15);
border-radius: 4px;
padding: 4px 8px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.6);
backdrop-filter: blur(6px);
min-width: 120px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.optimal-values:hover {
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12), 0 3px 8px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);
transform: translateY(-2px);
border-color: rgba(64, 158, 255, 0.25);
}
.optimal-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2px;
font-size: 10px;
padding: 1px 4px;
border-radius: 3px;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
}
.optimal-item:last-child {
margin-bottom: 0;
}
.optimal-item:hover {
background: linear-gradient(135deg, rgba(64, 158, 255, 0.08) 0%, rgba(103, 194, 58, 0.08) 100%);
transform: translateX(2px);
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
}
.optimal-item::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 0;
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
border-radius: 2px;
transition: height 0.2s ease;
}
.optimal-item:hover::before {
height: 60%;
}
.optimal-item .label {
color: #5a5e66;
font-weight: 500;
margin-right: 6px;
font-size: 10px;
letter-spacing: 0.1px;
opacity: 0.9;
}
.optimal-item .value {
color: #2c3e50;
font-weight: 700;
font-size: 10px;
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
position: relative;
}
.optimal-item .value::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
height: 1px;
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
opacity: 0;
transition: opacity 0.2s ease;
}
.optimal-item:hover .value::after {
opacity: 0.3;
}
.chart {
width: 100%;
height: calc(100vh - 220px);
......
<template>
<div class="chart-container">
<!-- 井号显示 -->
<div v-if="jh" class="well-number-display">
<span class="well-label">井号:</span>
<span class="well-number">{{ jh }}</span>
</div>
<div id="mainzft" class="chart" ref="chartRef"></div>
<div v-if="loading" class="loading-overlay">
<div class="loading-spinner"></div>
......@@ -44,7 +50,7 @@ export default {
const schemes = {
modern: {
primary: "#3B82F6",
secondary: "#10B981",
secondary: "#10B981",
accent: "#F59E0B",
background: "#F8FAFC",
text: "#1F2937",
......@@ -104,14 +110,14 @@ export default {
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();
......@@ -121,7 +127,7 @@ export default {
this.resetRetryCount();
this.disposeChart();
this.createChart(chartDom);
const mockData = await this.getList();
if (!this.hasValidData(mockData)) {
this.renderEmpty(chartDom);
......@@ -129,7 +135,7 @@ export default {
}
await this.renderRealData(mockData);
} catch (error) {
console.error("图表初始化失败:", error);
this.handleError(error);
......@@ -144,7 +150,7 @@ export default {
const viewportHeight = window.innerHeight;
const availableHeight = viewportHeight - 120;
const availableWidth = viewportWidth - 80;
chartDom.style.width = `${availableWidth}px`;
chartDom.style.height = `${availableHeight}px`;
chartDom.offsetHeight; // 强制重排
......@@ -187,10 +193,10 @@ export default {
// 检查数据有效性
hasValidData(mockData) {
return mockData &&
mockData.wellData &&
mockData.wellData.depthLine &&
mockData.wellData.depthLine.length > 0;
return mockData &&
mockData.wellData &&
mockData.wellData.depthLine &&
mockData.wellData.depthLine.length > 0;
},
// 设置事件监听器
......@@ -259,6 +265,10 @@ export default {
this.setChartDimensions(chartDom);
}
this.myChart.resize();
// 重新绘制地层标签,确保在缩放/尺寸变化后位置正确
if (this.lastStackedAreas && this.lastChartConfig && this.lastXAxisLabels) {
this.drawStratumLabels(this.lastStackedAreas, this.lastChartConfig, this.lastXAxisLabels);
}
}
},
......@@ -274,10 +284,10 @@ export default {
try {
const { wellData } = mockData;
const xAxisLabels = wellData.depthLine.map((point) => point.x);
// 优化区域系列处理
const areaSeries = await this.processAreaSeries(wellData.stackedAreas);
const option = {
...this.getDefaultChartOption(),
xAxis: this.createXAxis(xAxisLabels),
......@@ -286,12 +296,17 @@ export default {
};
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);
......@@ -307,8 +322,8 @@ export default {
backgroundColor: colors.background,
tooltip: {
trigger: "axis",
axisPointer: {
type: "cross",
axisPointer: {
type: "cross",
label: { backgroundColor: colors.primary },
crossStyle: { color: colors.border }
},
......@@ -319,10 +334,10 @@ export default {
extraCssText: 'box-shadow: 0 4px 12px rgba(0,0,0,0.15); border-radius: 8px;'
},
grid: {
top: 30,
left: "2%",
right: "2%",
bottom: "2%",
top: 10,
left: "4%",
right: "4%",
bottom: "5%",
containLabel: true,
show: false,
},
......@@ -375,35 +390,35 @@ export default {
boundaryGap: true,
position: "top",
data: xAxisLabels,
axisLabel: {
interval: 0,
rotate: 0,
margin: 12,
align: "center",
axisLabel: {
interval: 0,
rotate: 0,
margin: 12,
align: "center",
fontSize: 13,
color: colors.text,
fontWeight: 500
},
axisTick: {
alignWithLabel: true,
axisTick: {
alignWithLabel: true,
length: 6,
lineStyle: { color: colors.border }
},
splitLine: { show: false },
axisLine: {
show: true,
lineStyle: {
axisLine: {
show: true,
lineStyle: {
color: colors.border,
width: 2
}
}
}
},
{
type: "category",
boundaryGap: false,
position: "top",
show: false,
data: xAxisLabels
{
type: "category",
boundaryGap: false,
position: "top",
show: false,
data: xAxisLabels
},
];
},
......@@ -415,8 +430,8 @@ export default {
{
type: "value",
name: "井深(m)",
nameTextStyle: {
color: colors.text,
nameTextStyle: {
color: colors.text,
fontSize: 13,
fontWeight: 600,
padding: [0, 0, 0, 8]
......@@ -425,13 +440,13 @@ export default {
max: chartConfig.yAxis.max,
interval: chartConfig.yAxis.interval,
inverse: true,
axisLabel: {
formatter: "{value}",
axisLabel: {
formatter: "{value}",
fontSize: 12,
color: colors.text,
fontWeight: 500
},
splitLine: {
splitLine: {
show: true,
lineStyle: {
color: colors.border,
......@@ -439,14 +454,14 @@ export default {
opacity: 0.3
}
},
axisLine: {
show: true,
lineStyle: {
axisLine: {
show: true,
lineStyle: {
color: colors.border,
width: 2
}
}
},
axisTick: {
axisTick: {
show: true,
lineStyle: { color: colors.border }
}
......@@ -454,8 +469,8 @@ export default {
{
type: "value",
name: "深度(m)",
nameTextStyle: {
color: colors.text,
nameTextStyle: {
color: colors.text,
fontSize: 13,
fontWeight: 600,
padding: [0, 8, 0, 0]
......@@ -464,21 +479,21 @@ export default {
max: chartConfig.yAxis2.max,
interval: chartConfig.yAxis2.interval,
position: "right",
axisLabel: {
formatter: "{value}",
axisLabel: {
formatter: "{value}",
fontSize: 12,
color: colors.text,
fontWeight: 500
},
splitLine: { show: false },
axisLine: {
show: true,
lineStyle: {
axisLine: {
show: true,
lineStyle: {
color: colors.border,
width: 2
}
}
},
axisTick: {
axisTick: {
show: true,
lineStyle: { color: colors.border }
}
......@@ -489,7 +504,7 @@ export default {
// 创建系列配置
createSeries(wellData, areaSeries) {
const colors = this.colorScheme;
return [
{
name: "井深数据",
......@@ -497,14 +512,14 @@ export default {
yAxisIndex: 1,
symbol: "circle",
symbolSize: 10,
itemStyle: {
itemStyle: {
color: colors.accent,
borderColor: '#fff',
borderWidth: 3,
shadowColor: 'rgba(0,0,0,0.2)',
shadowBlur: 8
},
lineStyle: {
lineStyle: {
color: colors.accent,
width: 3,
type: 'solid',
......@@ -534,15 +549,15 @@ export default {
stack: "total",
silent: true,
barWidth: "8%",
itemStyle: {
borderColor: "transparent",
color: "transparent"
itemStyle: {
borderColor: "transparent",
color: "transparent"
},
emphasis: {
itemStyle: {
borderColor: "transparent",
color: "transparent"
}
emphasis: {
itemStyle: {
borderColor: "transparent",
color: "transparent"
}
},
data: wellData.depthIntervals.map((item) => item.placeholder),
},
......@@ -553,7 +568,7 @@ export default {
barWidth: "6%",
label: {
show: true,
position: (params) => params.data <= 200 ? "top" : "inside",
position: 'insideTop',
color: '#fff',
fontSize: 11,
fontWeight: 600,
......@@ -562,8 +577,12 @@ export default {
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: {
itemStyle: {
color: {
type: 'linear',
x: 0,
......@@ -599,6 +618,33 @@ export default {
},
data: wellData.depthIntervals.map((item) => item.interval),
},
// 底部显示 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];
return di && di.interval !== undefined ? di.interval : params.data;
}
},
data: wellData.depthIntervals.map((item) => item.interval)
},
...areaSeries,
];
},
......@@ -676,6 +722,64 @@ export default {
};
img.src = svgUrl;
});
},
// 使用像素坐标绘制地层标签,精确对齐
drawStratumLabels(stackedAreas, chartConfig, xAxisLabels) {
if (!this.myChart || !stackedAreas || stackedAreas.length === 0) return;
const totalDepth = chartConfig.yAxis.max - chartConfig.yAxis.min;
let currentDepth = chartConfig.yAxis.min;
// 清理旧的 graphic
this.myChart.setOption({ graphic: [] });
const graphics = [];
const lastXIndex = Math.max(0, (xAxisLabels?.length || 1) - 1);
const xOffset = 16; // 与图形右边缘的间距
stackedAreas.forEach((area) => {
if (!area.points || area.points.length === 0) return;
const thickness = area.points[0].y;
const centerDepth = currentDepth + thickness / 2;
// 将值坐标转换为像素坐标(使用最右一个类目,确保靠近右侧)
const pixelRight = this.myChart.convertToPixel({ xAxisIndex: 0, yAxisIndex: 0 }, [lastXIndex, centerDepth]);
if (!pixelRight || !Array.isArray(pixelRight) || pixelRight.length < 2) {
console.warn('convertToPixel 返回无效值:', pixelRight);
return;
}
const [pxRight, py] = pixelRight;
graphics.push({
type: 'text',
position: [pxRight + xOffset, py],
style: {
text: area.name,
fontSize: 12,
fontWeight: 600,
fill: '#333',
backgroundColor: 'transparent',
padding: [0, 0],
borderRadius: 0,
lineWidth: 0,
stroke: 'transparent',
align: 'left',
verticalAlign: 'middle'
},
// anchor 让文本垂直居中
origin: [0, 0],
z: 10,
// 使用 position 计算,避免随缩放偏移
bounding: 'raw',
silent: true
});
currentDepth += thickness;
});
this.myChart.setOption({ graphic: graphics });
}
},
};
......@@ -694,11 +798,44 @@ export default {
position: relative;
}
/* 井号显示样式 */
.well-number-display {
position: absolute;
top: 16px;
left: 16px;
z-index: 5;
background: transparent;
padding: 8px 12px;
display: flex;
align-items: center;
gap: 6px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
transition: all 0.3s ease;
opacity: 0.9;
}
.well-number-display:hover {
opacity: 1;
}
.well-label {
color: #6b7280;
font-size: 13px;
font-weight: 500;
white-space: nowrap;
}
.well-number {
color: #3b82f6;
font-size: 14px;
font-weight: 600;
}
/* 图表容器样式 */
.chart {
flex: 1;
width: 100%;
height: 100%;
min-height: 400px;
display: block;
......@@ -744,8 +881,13 @@ export default {
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* 响应式优化 */
......@@ -753,11 +895,25 @@ export default {
.chart-container {
padding: 0 10px;
}
.chart {
min-height: 300px;
border-radius: 12px;
}
.well-number-display {
top: 8px;
left: 8px;
padding: 6px 10px;
}
.well-label {
font-size: 11px;
}
.well-number {
font-size: 12px;
}
}
/* 深色模式支持 */
......@@ -767,20 +923,44 @@ export default {
/* box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); */
border: 1px solid rgba(255, 255, 255, 0.1);
}
/*
/*
.chart:hover {
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
} */
.loading-overlay {
background: rgba(17, 24, 39, 0.95);
}
.well-number-display {
background: transparent;
}
.well-number-display:hover {
background: transparent;
}
.well-label {
color: #6b7280;
}
.well-number {
color: #3b82f6;
}
}
/* 动画效果 */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.chart-container {
......
......@@ -13,7 +13,7 @@
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button type="primary" @click="handleImport2">导入las文件</el-button>
<el-button type="primary" @click="handleExport">导出</el-button>
<el-button type="primary" @click="handleImport">导入</el-button>
<el-button type="primary" @click="handleImport">导入Excel</el-button>
<el-button type="primary" @click="cjsz">设置</el-button>
<el-button type="primary" @click="jscs">计算参数</el-button>
<el-button type="primary" @click="js">计算</el-button>
......@@ -21,7 +21,12 @@
<!-- <el-button type="warning" plain icon="el-icon-download" size="mini"
@click="handleExport">导出</el-button> -->
</el-form-item>
<right-toolbar :search="false" :show-refresh="false" :showSearch.sync="showSearch"
@queryTable="handleCjData" :columns="columns" @columns-change="onColumnsChange" />
</el-form>
<div style="background-color: #fff;">
<!-- <el-form style="background-color: #fff" ref="queryForm" size="small" :inline="true" v-show="showSearch"
label-width="68px">
......@@ -37,8 +42,9 @@
}" :header-cell-class-name="'table-header'" :cell-style="{
padding: '1px 0'
}" :tooltip-effect="'dark'" header-overflow-tooltip v-loading="loading">
<el-table-column v-for="(col, index) in tableColumns" :key="index" :label="col.label" :prop="col.prop"
:width="col.width || 120" align="center" show-overflow-tooltip :render-header="renderTableHeader" />
<el-table-column v-for="(col, index) in filteredTableColumns" :key="index" :label="col.label"
:prop="col.prop" :width="col.width || 120" align="center" show-overflow-tooltip
:render-header="renderTableHeader" />
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="cjqueryParams.pageNum"
:limit.sync="cjqueryParams.pageSize" @pagination="handleCjData" />
......@@ -269,6 +275,8 @@ export default {
},
openjscs: false,
showSearch: true,
// 显隐列配置(配合 RightToolbar)
columns: [],
total: 0,
cjsjLasList: [],
form: {},
......@@ -389,6 +397,13 @@ export default {
]
};
},
computed: {
// 过滤出可见列供模板渲染,避免 v-for 搭配 v-if
filteredTableColumns() {
if (!Array.isArray(this.columns) || this.columns.length === 0) return this.tableColumns;
return this.tableColumns.filter((_, idx) => this.columns[idx] ? this.columns[idx].visible !== false : true);
}
},
mounted() {
this.cjqueryParams = {
pageNum: 1,
......@@ -396,6 +411,8 @@ export default {
jh: this.jh || this.$route.query.jh
}
this.handleCjData();
// 初始化显隐列(默认全部显示)
this.columns = this.tableColumns.map((col, idx) => ({ key: idx, label: col.label, visible: true }));
},
watch: {
activeTab: {
......@@ -417,6 +434,11 @@ export default {
}
},
methods: {
onColumnsChange({ index, visible }) {
if (Array.isArray(this.columns) && this.columns[index]) {
this.$set(this.columns[index], 'visible', visible);
}
},
TableHeight() {
const windowHeight = window.innerHeight;
const topBarHeight = 130;
......@@ -735,4 +757,12 @@ export default {
overflow: hidden !important;
text-overflow: ellipsis !important;
}
/* 顶部右侧容器,放置列设置按钮 */
.columns-top-right {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 0 10px 6px 10px;
}
</style>
......@@ -10,6 +10,18 @@
<svg class="trackSvg2"></svg>
</div>
</div>
<div class="svgContainer">
<div style="display: flex; width: 100%; justify-content: space-around">
<div style="display: flex; flex-direction: column; margin: 10px 0 0 0">
<svg class="trackSvgXs1"></svg>
<div class="svgTitle2">全角变化率</div>
</div>
<div style="display: flex; flex-direction: column; margin: 10px 0 0 0">
<svg class="trackSvgXs3"></svg>
<div class="svgTitle2">井斜</div>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="三维视图" name="2" style="height: calc(100vh - 250px)">
<div style="position: relative">
......@@ -91,11 +103,9 @@
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize" :page-sizes="[20, 50, 80, 100]" @pagination="getList"
class="pagination-fixed" />
<!-- 添加或修改定向井测斜数据计算数据对话框 -->
<el-dialog :title="title" :visible.sync="open" width="90%" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
......@@ -235,7 +245,7 @@ import 'echarts-gl'
import * as echarts from 'echarts'
import { listJskd, getJskd, delJskd, addJskd, updateJskd } from "@/api/system/jskd";
import { getBlockName, getQkxl } from "@/api/system/jsaa";
import { getTwoViews, getThreeViews } from '@/api/optimization/initialization'
import { getTwoViews, getThreeViews, getXsViews } from '@/api/optimization/initialization'
import * as d3 from 'd3'
export default {
......@@ -321,6 +331,7 @@ export default {
mounted() {
this.getJygjEw();
this.getJygjSw();
this.getJygjXs();
window.addEventListener('resize', this.adjustChartSize);
this.adjustChartSize();
},
......@@ -336,6 +347,7 @@ export default {
this.getList();
this.getJygjEw();
this.getJygjSw();
this.getJygjXs();
}
},
immediate: true
......@@ -351,6 +363,127 @@ export default {
windowHeight - topBarHeight - componentMargin - tabsMargin
return tableHeight
},
getJygjXs() {
// 获取斜深相关小图(全角变化率/井斜)
if (this.jh) {
getXsViews({ jh: this.jh })
.then((res) => {
if (res && res.code == 200) {
if (res.qjbhlmap && Array.isArray(res.qjbhlmap.data)) {
this.renderSmallCurve(
'.trackSvgXs1',
res.qjbhlmap.data,
res.qjbhlmap.xmin,
res.qjbhlmap.xmax,
res.qjbhlmap.ymax,
res.qjbhlmap.ymin
)
} else {
d3.select('.trackSvgXs1').selectAll('*').remove()
}
if (res.jxmap && Array.isArray(res.jxmap.data)) {
this.renderSmallCurve(
'.trackSvgXs3',
res.jxmap.data,
res.jxmap.xmin,
res.jxmap.xmax,
res.jxmap.ymax,
res.jxmap.ymin
)
} else {
d3.select('.trackSvgXs3').selectAll('*').remove()
}
}
})
.catch((err) => {
// 后端数据存在空值等校验失败时,避免未捕获异常
this.$modal && this.$modal.msgWarning((err && (err.msg || err.message)) || '小图数据获取失败')
d3.select('.trackSvgXs1').selectAll('*').remove()
d3.select('.trackSvgXs3').selectAll('*').remove()
})
}
},
// 通用小曲线渲染
renderSmallCurve(selector, curveData, xmin, xmax, ymax, ymin) {
if (!Array.isArray(curveData) || curveData.length === 0) {
d3.select(selector).selectAll('*').remove()
return
}
// 过滤掉空值点
const safeData = curveData
.filter((d) => d && typeof d.x === 'number' && typeof d.y === 'number' && !isNaN(d.x) && !isNaN(d.y))
if (safeData.length === 0) {
d3.select(selector).selectAll('*').remove()
return
}
xmin = typeof xmin === 'number' ? xmin : d3.min(safeData, (d) => d.x)
xmax = typeof xmax === 'number' ? xmax : d3.max(safeData, (d) => d.x)
ymin = typeof ymin === 'number' ? ymin : d3.min(safeData, (d) => d.y)
ymax = typeof ymax === 'number' ? ymax : d3.max(safeData, (d) => d.y)
const width = this.width
const height = this.height
const marginLeft = this.marginLeft
const marginBottom = this.marginBottom
const marginTop = this.marginTop
d3.select(selector).selectAll('*').remove()
const x = d3.scaleLinear([xmin, xmax], [marginLeft, width])
const y = d3.scaleLinear([ymax, ymin], [height - marginBottom, marginTop])
const svg = d3
.select(selector)
.attr('viewBox', [0, 0, this.svgWidth, height])
.attr('width', '100%')
.attr('height', height)
.attr('style', 'max-width: 100%; height: auto; height: intrinsic; font: 14px sans-serif;')
svg
.append('g')
.attr('transform', `translate(0,${height - marginBottom})`)
.call(
d3.axisBottom(x)
.ticks(25)
.tickSizeOuter(0)
.tickFormat((d, i) => (i % 5 === 0 || d == 0 ? d : ''))
)
.call((g) => g.selectAll('.tick line').clone().attr('y2', -(height - 25)).attr('stroke', (d, i) => (i % 5 === 0 ? '#000' : '#ccc')))
svg
.append('g')
.attr('transform', `translate(${width},0)`)
.call(d3.axisLeft(y).ticks(6).tickSizeOuter(0))
svg
.append('g')
.attr('transform', `translate(${marginLeft},0)`)
.call(
d3.axisLeft(y)
.ticks(25)
.tickSizeOuter(0)
.tickFormat((d, i) => (i % 5 === 0 || d == 0 ? d : ''))
)
.call((g) => g.selectAll('.tick line').clone().attr('x2', width - marginLeft).attr('stroke', (d, i) => (i % 5 === 0 ? '#000' : '#ccc')))
const line = d3
.line()
.x((d) => x(d.x))
.y((d) => y(d.y))
const y0Data = [
{ x: 0, y: ymax },
{ x: 0, y: ymin },
]
const x0Data = [
{ x: xmin, y: 0 },
{ x: xmax, y: 0 },
]
svg.append('path').attr('fill', 'none').attr('stroke', '#000').attr('stroke-width', 1).attr('d', line(x0Data))
svg.append('path').attr('fill', 'none').attr('stroke', '#000').attr('stroke-width', 1).attr('d', line(y0Data))
svg.append('path').attr('fill', 'none').attr('stroke', 'rgb(239,83,83)').attr('stroke-width', 3).attr('d', line(safeData))
},
adjustChartSize() {
// 调整图表大小的方法
},
......
<!-- 井基础信息详情 -->
<template>
<div class="app-container">
<el-tabs v-model="activeTab" type="card" @tab-click="handleTabClick" style="margin-top: -10px;">
<el-tab-pane label="数据表格" name="dataTable">
<DataTable :jh="queryParams.jh" />
<DataTable v-if="activeTab === 'dataTable'" :jh="queryParams.jh" :key="`dataTable-${queryParams.jh}`" />
</el-tab-pane>
<el-tab-pane label="曲线图形" name="curveGraph">
<CurveGraph :jh="queryParams.jh" />
<CurveGraph v-if="activeTab === 'curveGraph'" :jh="queryParams.jh"
:key="`curveGraph-${queryParams.jh}`" />
</el-tab-pane>
<el-tab-pane label="直方图形" name="histogramGraph">
<HistogramGraph :jh="queryParams.jh" />
<!-- <HistogramGraph :jh="wellId" theme="elegant" />
<HistogramGraph :jh="wellId" theme="vibrant" /> -->
<HistogramGraph v-if="activeTab === 'histogramGraph'" :jh="queryParams.jh"
:key="`histogramGraph-${queryParams.jh}`" />
</el-tab-pane>
<el-tab-pane label="测井数据" name="loggingData">
<LoggingData :jh="queryParams.jh" />
<LoggingData v-if="activeTab === 'loggingData'" :jh="queryParams.jh"
:key="`loggingData-${queryParams.jh}`" />
</el-tab-pane>
<el-tab-pane label="测井曲线" name="loggingCurve">
<keep-alive>
<LoggingCurve ref="loggingCurveComponent" :jh="queryParams.jh" />
<LoggingCurve v-if="activeTab === 'loggingCurve'" ref="loggingCurveComponent" :jh="queryParams.jh"
:key="`loggingCurve-${queryParams.jh}`" />
</keep-alive>
</el-tab-pane>
<el-tab-pane label="录井数据" name="drillingData">
<DrillingData :jh="queryParams.jh" />
<DrillingData v-if="activeTab === 'drillingData'" :jh="queryParams.jh"
:key="`drillingData-${queryParams.jh}`" />
</el-tab-pane>
<el-tab-pane label="录井曲线" name="drillingCurve">
<keep-alive>
<DrillingCurve ref="drillingCurveComponent" :jh="queryParams.jh" />
<DrillingCurve v-if="activeTab === 'drillingCurve'" ref="drillingCurveComponent"
:jh="queryParams.jh" :key="`drillingCurve-${queryParams.jh}`" />
</keep-alive>
</el-tab-pane>
<el-tab-pane label="井眼轨迹" name="wellboreTrajectory">
<WellboreTrajectory :jh="queryParams.jh" />
<WellboreTrajectory v-if="activeTab === 'wellboreTrajectory'" :jh="queryParams.jh"
:key="`wellboreTrajectory-${queryParams.jh}`" />
</el-tab-pane>
<el-tab-pane label="井身结构" name="wellDepthStructure">
<WellDepthStructure :jh="queryParams.jh" />
<WellDepthStructure v-if="activeTab === 'wellDepthStructure'" :jh="queryParams.jh"
:key="`wellDepthStructure-${queryParams.jh}`" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import DataTable from './components/DataTable.vue';
import CurveGraph from './components/CurveGraph.vue';
......@@ -85,53 +81,21 @@ export default {
queryParams: {
jh: ''
},
// 标记是否是第一次进入页面
isFirstLoad: true,
// 表格数据
tableData: [
{
qk: '克深',
jh: '克深1102',
ztcc: '241.3',
ztxh: 'GT56S',
qsjd: '500',
jsjd: '2735',
xrcw: '',
qccw: '',
jc: '2235',
jxzs: '23.75',
ztcj: 'DBS',
ztbh: '2'
},
{
qk: '克深',
jh: '克深1102',
ztcc: '165.3',
ztxh: 'MDI513',
qsjd: '3586',
jsjd: '4068',
xrcw: '',
qccw: '',
jc: '482',
jxzs: '5.59',
ztcj: '史密斯',
ztbh: '7'
}
]
};
},
created() {
// 获取路由参数
const { jh } = this.$route.params;
console.log('路由参数 jh:', jh);
if (jh) {
this.queryParams.jh = jh;
console.log('设置 queryParams.jh:', this.queryParams.jh);
} else {
console.warn('未获取到路由参数 jh');
}
// 确保第一次进入页面时不会自动初始化测井曲线组件
console.log('页面初始化完成,等待用户点击tab');
},
methods: {
/** 搜索按钮操作 */
......@@ -150,7 +114,6 @@ export default {
return;
}
this.$modal.confirm('是否确认移除选中的记录?').then(() => {
// 这里可以调用API进行删除操作
this.$modal.msgSuccess("移除成功");
}).catch(() => { });
},
......@@ -163,26 +126,7 @@ export default {
/** tab切换事件 */
handleTabClick(tab) {
console.log('切换到tab:', tab.name);
// 当切换到测井曲线tab时,触发子组件的重新初始化
if (tab.name === 'loggingCurve') {
this.$nextTick(() => {
if (this.$refs.loggingCurveComponent) {
console.log('触发测井曲线组件重新初始化');
this.$refs.loggingCurveComponent.refreshChart();
}
});
}
// 当切换到录井曲线tab时,触发子组件的重新初始化
if (tab.name === 'drillingCurve') {
this.$nextTick(() => {
if (this.$refs.drillingCurveComponent) {
console.log('触发录井曲线组件重新初始化');
this.$refs.drillingCurveComponent.refreshChart();
}
});
}
// 标记已经不是第一次加载
this.isFirstLoad = false;
}
}
};
......
......@@ -20,6 +20,9 @@
<el-button type="primary" @click="jscs">计算参数</el-button>
<el-button type="primary" @click="js">计算</el-button> -->
</el-form-item>
<right-toolbar :search="false" :show-refresh="false" :showSearch.sync="showSearch"
@queryTable="handleCjData" :columns="columns" @columns-change="onColumnsChange" />
</el-form>
<div style="background-color: #fff;">
<!-- <el-form style="background-color: #fff" ref="queryForm" size="small" :inline="true" v-show="showSearch"
......@@ -36,8 +39,9 @@
}" :header-cell-class-name="'table-header'" :cell-style="{
padding: '1px 0'
}" :tooltip-effect="'dark'" header-overflow-tooltip v-loading="loading">
<el-table-column v-for="(col, index) in tableColumns" :key="index" :label="col.label" :prop="col.prop"
:width="col.width || 120" align="center" show-overflow-tooltip :render-header="renderTableHeader" />
<el-table-column v-for="(col, index) in filteredTableColumns" :key="index" :label="col.label"
:prop="col.prop" :width="col.width || 120" align="center" show-overflow-tooltip
:render-header="renderTableHeader" />
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="cjqueryParams.pageNum"
:limit.sync="cjqueryParams.pageSize" @pagination="handleCjData" />
......@@ -264,6 +268,8 @@ export default {
},
openjscs: false,
showSearch: true,
// 显隐列配置(配合 RightToolbar)
columns: [],
total: 0,
cjsjLasList: [],
form: {},
......@@ -384,6 +390,13 @@ export default {
]
};
},
computed: {
// 过滤出可见列供模板渲染,避免 v-for 搭配 v-if
filteredTableColumns() {
if (!Array.isArray(this.columns) || this.columns.length === 0) return this.tableColumns;
return this.tableColumns.filter((_, idx) => this.columns[idx] ? this.columns[idx].visible !== false : true);
}
},
mounted() {
this.cjqueryParams = {
pageNum: 1,
......@@ -391,6 +404,8 @@ export default {
jh: this.$route.query.jh
}
this.handleCjData();
// 初始化显隐列(默认全部显示)
this.columns = this.tableColumns.map((col, idx) => ({ key: idx, label: col.label, visible: true }));
},
watch: {
activeTab: {
......@@ -403,6 +418,11 @@ export default {
}
},
methods: {
onColumnsChange({ index, visible }) {
if (Array.isArray(this.columns) && this.columns[index]) {
this.$set(this.columns[index], 'visible', visible);
}
},
TableHeight() {
const windowHeight = window.innerHeight;
const topBarHeight = 100;
......
......@@ -475,7 +475,7 @@
<el-form-item style="margin: 5px 0 5px 10px;">
<el-button type="primary" @click="handleImport2">导入las文件</el-button>
<el-button type="primary" @click="handleExport">导出</el-button>
<el-button type="primary" @click="handleImport">导入</el-button>
<el-button type="primary" @click="handleImport">导入Excel</el-button>
<el-button type="primary" @click="cjsz">设置</el-button>
<el-button type="primary" @click="jscs">计算参数</el-button>
<el-button type="primary" @click="js">计算</el-button>
......
<!-- 钻头基础信息 basicInformationOfDrillBit -->
<template>
<div class="app-container">
<!-- 查询表单 -->
......@@ -24,8 +22,6 @@
<el-button @click="onReset" size="mini">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="onAdd"
......@@ -43,9 +39,12 @@
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
v-hasPermi="['system:ztxxJcxx:export']">导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport"
v-hasPermi="['system:user:import']">导入</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="onSearch"></right-toolbar>
</el-row>
<!-- 数据表格 -->
<el-table :data="tableData" style="width: 100%;margin: 10px 0;" stripe highlight-current-row border
v-loading="loading" @selection-change="handleSelectionChange">
......@@ -75,8 +74,6 @@
</template>
</el-table-column>
</el-table>
<!-- 新增弹窗 -->
<el-dialog title="新增钻头" :visible.sync="dialogVisible" width="90%" @close="handleDialogClose">
<div class="dialog-card">
......@@ -124,8 +121,6 @@
</el-radio-group>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="布齿密度" name="bcmd">
<el-form-item prop="tzdm02">
<el-radio-group v-model="form.tzdm02" style="width:100%">
......@@ -204,7 +199,6 @@
</span>
</div>
</el-dialog>
<!-- 详情弹窗 -->
<el-dialog title="查看" :visible.sync="detailVisible" width="95%" @close="detailVisible = false">
<div class="detail-container">
......@@ -221,7 +215,6 @@
<span class="info-value">{{ detailData.ztbh }}</span>
</div>
</div>
<!-- 现代化特征代码卡片区 -->
<div class="feature-code-cards">
<div class="code-card" v-for="(item, i) in featureMap" :key="i">
......@@ -230,7 +223,6 @@
<!-- <div class="code-value">{{ detailData[item.codeField + '_value'] || (detailData[item.codeField] || '--') }}</div> -->
</div>
</div>
<!-- 特征参数 -->
<div class="feature-params">
<div class="params-header">
......@@ -259,6 +251,27 @@
:show-close="true" center>
<img :src="imgPreviewUrl" style="max-width:80vw;max-height:80vh;border-radius:18px;" />
</el-dialog>
<!-- 用户导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading"
:on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" /> 是否更新已经存在的用户数据
</div>
<span>仅允许导入xls、xlsx格式文件。</span>
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;"
@click="importTemplate">下载模板</el-link>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm">确 定</el-button>
<el-button @click="upload.open = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
......@@ -266,10 +279,25 @@
<script>
import { listTzsj } from "@/api/system/jsha";
import { listZtxxJcxx, addZtxxJcxx, updateZtxxJcxx, delZtxxJcxx, getZtxxJcxx } from '@/api/ztxx';
import { getToken } from "@/utils/auth";
export default {
data() {
return {
// 用户导入参数
upload: {
// 是否显示弹出层(用户导入)
open: false,
// 弹出层标题(用户导入)
title: "",
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的用户数据
updateSupport: 0,
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getToken() },
// 上传的地址
url: process.env.VUE_APP_BASE_API + "/system/ztxxJcxx/importData"
},
// 遮罩层
loading: true,
// 选中数组
......@@ -416,6 +444,32 @@ export default {
this.onSearch();
},
methods: {
/** 导入按钮操作 */
handleImport() {
this.upload.title = "用户导入";
this.upload.open = true;
},
/** 下载模板操作 */
importTemplate() {
this.download('system/ztxxJcxx/downdrmb', {
}, `钻头基本信息_${new Date().getTime()}.xlsx`)
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
this.getList();
},
// 提交上传文件
submitFileForm() {
this.$refs.upload.submit();
},
onSearch() {
// 查询接口
listZtxxJcxx(this.queryParams).then(res => {
......@@ -531,11 +585,11 @@ export default {
// 编辑
onEdit(row) {
this.editType = 'edit';
this.onReset();
const id = row.id || this.ids
getZtxxJcxx(id).then(response => {
this.form = response.data;
console.log(this.form, 'this.form');
this.openDialog();
this.title = "修改钻头基础信息";
});
......@@ -558,6 +612,7 @@ export default {
const selectedOption = this[mapping.options].find(opt => opt.id === idValue);
if (selectedOption) {
this.form[mapping.valueField] = selectedOption.value;
console.log(this.form[mapping.valueField], 'this.form[mapping.valueField]');
}
}
});
......
<!-- 测井数据 -->
<!-- 测井数据 -->
<template>
<div class="app-containercj">
<!-- <el-tabs v-model="activeTab" v-loading="loading">
<el-tab-pane label="测井数据" name="loggingData"> -->
<el-form :model="cjqueryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
label-width="100px">
<el-form-item label="井号" prop="jh">
<el-form-item label="井号11" prop="jh">
<el-input v-model="cjqueryParams.jh" placeholder="请输入井号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
......@@ -13,7 +13,7 @@
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button type="primary" @click="handleImport2">导入las文件</el-button>
<el-button type="primary" @click="handleExport">导出</el-button>
<el-button type="primary" @click="handleImport">导入</el-button>
<el-button type="primary" @click="handleImport">导入Excel</el-button>
<el-button type="primary" @click="cjsz">设置</el-button>
<el-button type="primary" @click="jscs">计算参数</el-button>
<el-button type="primary" @click="js">计算</el-button>
......@@ -22,6 +22,9 @@
<!-- <el-button type="warning" plain icon="el-icon-download" size="mini"
@click="handleExport">导出</el-button> -->
</el-form-item>
<right-toolbar :search="false" :show-refresh="false" :showSearch.sync="showSearch"
@queryTable="handleCjData" :columns="columns" @columns-change="onColumnsChange" />
</el-form>
<div style="background-color: #fff;">
<!-- <el-form style="background-color: #fff" ref="queryForm" size="small" :inline="true" v-show="showSearch"
......@@ -38,8 +41,9 @@
}" :header-cell-class-name="'table-header'" :cell-style="{
padding: '1px 0'
}" :tooltip-effect="'dark'" header-overflow-tooltip v-loading="loading">
<el-table-column v-for="(col, index) in tableColumns" :key="index" :label="col.label" :prop="col.prop"
:width="col.width || 120" align="center" show-overflow-tooltip :render-header="renderTableHeader" />
<el-table-column v-for="(col, index) in filteredTableColumns" :key="index" :label="col.label"
:prop="col.prop" :width="col.width || 120" align="center" show-overflow-tooltip
:render-header="renderTableHeader" />
</el-table>
<pagination :total="total" :page.sync="cjqueryParams.pageNum" :limit.sync="cjqueryParams.pageSize"
@pagination="handleCjData" />
......@@ -292,7 +296,7 @@
<template slot="footer">
<span class="dialog-footer">
<el-button @click="handleWellCancel">取 消</el-button>
<el-button type="primary" @click="handleWellSubmit">提 交</el-button>
<el-button type="primary" @click="handleWellSubmit">确 定</el-button>
</span>
</template>
</el-dialog>
......@@ -340,6 +344,8 @@ export default {
},
openjscs: false,
showSearch: true,
// 显隐列配置(配合 RightToolbar)
columns: [],
total: 0,
cjsjLasList: [],
form: {},
......@@ -494,6 +500,11 @@ export default {
b1: this.formjscs?.b1 || '',
b2: this.formjscs?.b2 || ''
};
},
// 过滤出可见列供模板渲染,避免 v-for 搭配 v-if
filteredTableColumns() {
if (!Array.isArray(this.columns) || this.columns.length === 0) return this.tableColumns;
return this.tableColumns.filter((_, idx) => this.columns[idx] ? this.columns[idx].visible !== false : true);
}
},
mounted() {
......@@ -505,6 +516,8 @@ export default {
jh: this.wellInfo.jh || this.$route.query.jh
}
this.handleCjData();
// 初始化显隐列(默认全部显示)
this.columns = this.tableColumns.map((col, idx) => ({ key: idx, label: col.label, visible: true }));
this.getBlockOptions().then(() => {
// 区块选项加载完成后,设置默认区块值
if (this.qkmc) {
......@@ -533,6 +546,11 @@ export default {
}
},
methods: {
onColumnsChange({ index, visible }) {
if (Array.isArray(this.columns) && this.columns[index]) {
this.$set(this.columns[index], 'visible', visible);
}
},
TableHeight() {
const windowHeight = window.innerHeight;
const topBarHeight = 130;
......
......@@ -14,7 +14,6 @@
<el-table-column prop="ztxh" label="钻头型号" min-width="150" align="center" />
<el-table-column prop="ztlb" label="钻头类别" min-width="120" align="center" />
<el-table-column prop="sccj" label="生产厂家" min-width="120" align="center" />
<el-table-column label="操作" min-width="100" align="center">
<template slot-scope="scope">
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
......
......@@ -26,7 +26,6 @@ import AdjacentWell from './components/AdjacentWell.vue'
import FormationAnalysis from './components/FormationAnalysis.vue'
import BitDesign from './components/BitDesign.vue'
import FormationMaintenance from './components/FormationMaintenance.vue'
export default {
name: 'WellDesign',
components: {
......@@ -49,7 +48,6 @@ export default {
}
},
watch: {
// 监听路由变化,确保参数正确传递
$route: {
handler(to, from) {
this.getWellInfo();
......@@ -58,28 +56,23 @@ export default {
}
},
created() {
// 从路由参数或store获取井信息
this.getWellInfo()
// 确保默认选中井身结构标签页
this.$nextTick(() => {
this.activeTab = 'wellStructure';
});
},
beforeRouteUpdate(to, from, next) {
// 当路由参数变化时,重新获取井信息
this.getWellInfo();
next();
},
methods: {
getWellInfo() {
// 从路由参数获取井信息
const { jh, jhdm, qkmc } = this.$route.query
console.log('wellDesign getWellInfo - route query:', this.$route.query);
console.log('wellDesign getWellInfo - qkmc from route:', qkmc);
if (jh && jhdm) {
this.wellInfo.jh = jh
this.wellInfo.jhdm = jhdm
// 可以从store获取更多井信息
this.wellInfo.jd = this.$store.state.app.jd || ''
this.wellInfo.ytgs = this.$store.state.app.ytgs || ''
}
......@@ -88,12 +81,10 @@ export default {
console.log('wellDesign getWellInfo - qkmc set to:', this.qkmc);
}
// 强制更新子组件
this.reloadComponents();
},
reloadComponents() {
// 通过重置tab的方式强制刷新子组件
const currentTab = this.activeTab;
this.activeTab = '';
this.$nextTick(() => {
......
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