Commit a8a4c625 by cat

zd 首页样式修改 缩略图弹窗显示 成果管理

parent 66a45d27
import request from '@/utils/request'
// 查询成果管理列表
// 查询成果列表列表
export function listWjscqk(query) {
return request({
url: '/yscgWjscqk/wjscqk/list',
method: 'get',
params: query
params: query,
})
}
// 查询成果管理详细
// 查询成果列表详细
export function getWjscqk(id) {
return request({
url: '/yscgWjscqk/wjscqk/' + id,
method: 'get'
method: 'get',
})
}
// 新增成果管理
// 新增成果列表
export function addWjscqk(data) {
return request({
url: '/yscgWjscqk/wjscqk',
method: 'post',
data: data
data: data,
})
}
// 修改成果管理
// 修改成果列表
export function updateWjscqk(data) {
return request({
url: '/yscgWjscqk/wjscqk',
method: 'put',
data: data
data: data,
})
}
// 删除成果管理
// 删除成果列表
export function delWjscqk(id) {
return request({
url: '/yscgWjscqk/wjscqk/' + id,
method: 'delete'
method: 'delete',
})
}
// 查询成果管理列表
export function listYscgList(query) {
return request({
url: '/ysqqXmxx/ysqqXmxx/yscgList',
method: 'get',
params: query,
})
}
......@@ -5,7 +5,7 @@ export function listZllx(query) {
return request({
url: '/yscgZllx/zllx/list',
method: 'get',
params: query
params: query,
})
}
......@@ -13,7 +13,7 @@ export function listZllx(query) {
export function getZllx(id) {
return request({
url: '/yscgZllx/zllx/' + id,
method: 'get'
method: 'get',
})
}
......@@ -22,7 +22,7 @@ export function addZllx(data) {
return request({
url: '/yscgZllx/zllx',
method: 'post',
data: data
data: data,
})
}
......@@ -31,7 +31,7 @@ export function updateZllx(data) {
return request({
url: '/yscgZllx/zllx',
method: 'put',
data: data
data: data,
})
}
......@@ -39,6 +39,14 @@ export function updateZllx(data) {
export function delZllx(id) {
return request({
url: '/yscgZllx/zllx/' + id,
method: 'delete'
method: 'delete',
})
}
// 查询资料类型下拉选项
export function selectZllx() {
return request({
url: '/yscgZllx/zllx/selectZllx',
method: 'get',
})
}

79.2 KB | W: | H:

1.65 KB | W: | H:

src/assets/images/profile.jpg
src/assets/images/profile.jpg
src/assets/images/profile.jpg
src/assets/images/profile.jpg
  • 2-up
  • Swipe
  • Onion skin
......@@ -225,17 +225,30 @@ export const dynamicRoutes = [
},
],
},
{
path: '/yscgWjscqk/wjscqk',
component: Layout,
hidden: true,
children: [
{
path: 'index',
component: () => import('@/views/yscgWjscqk/wjscqk/index'),
name: 'WjscqkIndex',
meta: { title: '成果管理', activeMenu: '/yscgWjscqk/yscg' },
},
],
},
];
// 防止连续点击多次路由报错
let routerPush = Router.prototype.push;
let routerReplace = Router.prototype.replace;
// push
Router.prototype.push = function push(location) {
Router.prototype.push = function push (location) {
return routerPush.call(this, location).catch((err) => err);
};
// replace
Router.prototype.replace = function push(location) {
Router.prototype.replace = function push (location) {
return routerReplace.call(this, location).catch((err) => err);
};
......
......@@ -19,11 +19,11 @@
<el-tab-pane label="待验收" name="pending">
<div class="card-container">
<el-row :gutter="16" v-loading="pendingLoading">
<el-col :span="6" v-for="item in pendingList" :key="item.id" class="card-item">
<el-col :span="4" v-for="item in pendingList" :key="item.id" class="card-item">
<el-card class="project-card" shadow="hover">
<div class="card-header" @click="goNav2(item)" style="cursor: pointer;">
<h3 class="project-title">{{ item.xmmc }}</h3>
<el-tag type="warning" size="small">待验收</el-tag>
<el-tag size="small" class="status-tag status-pending">待验收</el-tag>
</div>
<div class="card-content">
<div class="content-wrapper">
......@@ -46,7 +46,7 @@
</div>
</div>
<div class="button-section">
<el-button size="small" @click="handleComplete(item)">完成</el-button>
<el-button size="mini" @click="handleComplete(item)">完成</el-button>
</div>
</div>
</div>
......@@ -66,7 +66,7 @@
<el-tab-pane label="已验收" name="completed">
<div class="card-container">
<el-row :gutter="16" v-loading="completedLoading">
<el-col :span="6" v-for="item in completedList" :key="item.id" class="card-item">
<el-col :span="4" v-for="item in completedList" :key="item.id" class="card-item">
<el-card class="project-card" shadow="hover">
<div class="card-header">
<h3 class="project-title">{{ item.xmmc }}</h3>
......@@ -314,6 +314,7 @@ export default {
padding: 3px 0;
border-radius: 4px;
transition: background-color 0.2s ease;
align-items: center;
}
.info-item:hover {
......@@ -325,6 +326,7 @@ export default {
min-width: 70px;
flex-shrink: 0;
font-weight: 500;
white-space: nowrap;
}
.value {
......@@ -334,6 +336,7 @@ export default {
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 400;
min-width: 0;
}
.button-section {
......@@ -346,11 +349,47 @@ export default {
}
.button-section .el-button {
width: 70px;
height: 28px;
width: 50px;
height: 25px;
font-size: 12px;
font-weight: 500;
border-radius: 14px;
border-radius: 20px;
background: linear-gradient(135deg, #409EFF 0%, #66b1ff 100%);
border: none;
color: #ffffff !important;
box-shadow: 0 1px 4px rgba(64, 158, 255, 0.2);
transition: all 0.2s ease-out;
position: relative;
overflow: hidden;
letter-spacing: 0.5px;
}
.button-section .el-button:hover {
background: linear-gradient(135deg, #337ecc 0%, #5a9eff 100%);
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.25);
transform: translateY(-0.5px);
color: #ffffff !important;
}
.button-section .el-button:active {
transform: translateY(0);
box-shadow: 0 1px 3px rgba(64, 158, 255, 0.2);
color: #ffffff !important;
}
.button-section .el-button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.15), transparent);
transition: left 0.3s ease-out;
}
.button-section .el-button:hover::before {
left: 100%;
}
/* Safety: if any global rule hid buttons inside cards on non-hover, override */
......@@ -367,6 +406,17 @@ export default {
visibility: visible;
}
/* 状态标签:待验收(蓝色系,贴合系统主色) */
.status-tag {
border: none !important;
font-weight: 600;
}
.status-pending {
color: #1f3b64;
background: rgba(64, 158, 255, 0.12);
}
/* 分页固定在右下角 */
.pagination-wrapper {
position: fixed;
......@@ -377,13 +427,13 @@ export default {
/* 默认大屏下每行5列 */
.el-col {
width: 25% !important;
width: 20% !important;
}
/* 响应式设计 */
@media (max-width: 1400px) {
.el-col {
width: 25% !important;
width: 20% !important;
}
}
......
<!-- 成果列表 -->
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<!-- <el-form-item label="项目id" prop="xmid">
<el-input
v-model="queryParams.xmid"
placeholder="请输入项目id"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item> -->
<el-form-item label="文件名称" prop="wjmc">
<el-input v-model="queryParams.wjmc" placeholder="请输入文件名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="资料类型" prop="zllx">
<el-input v-model="queryParams.zllx" placeholder="请输入资料类型" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<!-- <el-form-item label="文件路径" prop="wjlj">
<el-input
v-model="queryParams.wjlj"
placeholder="请输入文件路径"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建人" prop="createdBy">
<el-input
v-model="queryParams.createdBy"
placeholder="请输入创建人"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createdTime">
<el-date-picker clearable
v-model="queryParams.createdTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择创建时间">
</el-date-picker>
</el-form-item>
<el-form-item label="备用1" prop="ext1">
<el-input
v-model="queryParams.ext1"
placeholder="请输入备用1"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="备用2" prop="ext2">
<el-input
v-model="queryParams.ext2"
placeholder="请输入备用2"
clearable
@keyup.enter.native="handleQuery"
/>
<el-select v-model="queryParams.zllx" placeholder="请选择资料类型" clearable>
<el-option
v-for="item in zllxOptions"
:key="item.id"
:label="item.lxmc"
:value="item.lxmc">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="备用3" prop="ext3">
<el-input
v-model="queryParams.ext3"
placeholder="请输入备用3"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item> -->
<el-form-item>
<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>
......@@ -77,34 +30,12 @@
</el-form-item>
</el-form>
<!-- <el-row :gutter="10" class="mb8">
<el-col :span="1.5">
</el-col>
<el-col :span="1.5">
</el-col>
<el-col :span="1.5">
</el-col>
<el-col :span="1.5">
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> -->
<el-table border v-loading="loading" :data="wjscqkList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<!-- <el-table-column label="主键" align="center" prop="id" />
<el-table-column label="项目id" align="center" prop="xmid" /> -->
<el-table-column label="文件名称" align="center" prop="wjmc" min-width="120" show-overflow-tooltip />
<el-table-column label="资料类型" align="center" prop="zllx" min-width="100" show-overflow-tooltip />
<el-table-column label="文件路径" align="center" prop="wjlj" min-width="170" show-overflow-tooltip />
<!-- <el-table-column label="创建人" align="center" prop="createdBy" />
<el-table-column label="创建时间" align="center" prop="createdTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createdTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="备用1" align="center" prop="ext1" />
<el-table-column label="备用2" align="center" prop="ext2" />
<el-table-column label="备用3" align="center" prop="ext3" /> -->
<el-table-column label="上传时间" align="center" prop="createdTime" min-width="170" show-overflow-tooltip />
<el-table-column label="操作" min-width="110" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
......@@ -121,35 +52,42 @@
<!-- 添加或修改成果管理对话框 -->
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="89px">
<!-- <el-form-item label="项目id" prop="xmid">
<el-input v-model="form.xmid" placeholder="请输入项目id" />
</el-form-item> -->
<el-form-item label="文件名称" prop="wjmc">
<el-input v-model="form.wjmc" placeholder="请输入文件名称" />
</el-form-item>
<el-form-item label="资料类型" prop="zllx">
<el-input v-model="form.zllx" placeholder="请输入资料类型" />
</el-form-item>
<el-form-item label="文件路径" prop="wjlj">
<el-input v-model="form.wjlj" placeholder="请输入文件路径" />
</el-form-item>
<!-- <el-form-item label="创建人" prop="createdBy">
<el-input v-model="form.createdBy" placeholder="请输入创建人" />
<el-select v-model="form.zllx" placeholder="请选择资料类型" clearable>
<el-option
v-for="item in zllxOptions"
:key="item.id"
:label="item.lxmc"
:value="item.lxmc">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createdTime">
<el-date-picker clearable v-model="form.createdTime" type="date" value-format="yyyy-MM-dd"
placeholder="请选择创建时间">
</el-date-picker>
</el-form-item>
<el-form-item label="备用1" prop="ext1">
<el-input v-model="form.ext1" placeholder="请输入备用1" />
</el-form-item>
<el-form-item label="备用2" prop="ext2">
<el-input v-model="form.ext2" placeholder="请输入备用2" />
<el-form-item label="文件上传" prop="fileList">
<el-upload
ref="fileUpload"
:action="uploadUrl"
:headers="uploadHeaders"
:file-list="fileList"
:before-upload="handleBeforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:on-change="handleFileChange"
:data="uploadData"
drag
class="upload-dragger"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">
支持 doc、docx、pdf、xls、xlsx、ppt、pptx、txt、jpg、jpeg、png、gif 格式文件,单个文件不超过50MB<br/>
<span style="color: #409EFF;">上传新文件将自动替换已有文件</span>
</div>
</el-upload>
</el-form-item>
<el-form-item label="备用3" prop="ext3">
<el-input v-model="form.ext3" placeholder="请输入备用3" />
</el-form-item> -->
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
......@@ -161,6 +99,8 @@
<script>
import { listWjscqk, getWjscqk, delWjscqk, addWjscqk, updateWjscqk } from "@/api/yscgWjscqk/wjscqk"
import { selectZllx } from "@/api/yscgZllx/zllx"
import { getToken } from "@/utils/auth"
export default {
name: "Wjscqk",
......@@ -188,6 +128,7 @@ export default {
queryParams: {
pageNum: 1,
pageSize: 10,
id: null,
xmid: null,
wjmc: null,
zllx: null,
......@@ -202,22 +143,81 @@ export default {
form: {},
// 表单校验
rules: {
}
},
// 资料类型选项
zllxOptions: [],
// 文件列表
fileList: [],
// 上传配置
uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload",
uploadHeaders: {
Authorization: "Bearer " + getToken()
},
uploadData: {}
}
},
created() {
// 获取路由参数中的id
if (this.$route.query.xmid) {
this.queryParams.xmid = this.$route.query.xmid
}
this.getList()
this.getZllxOptions()
},
watch: {
// 监听路由参数变化
'$route.query.xmid'(newId) {
if (newId) {
this.queryParams.xmid = newId
this.getList()
}
}
},
methods: {
/** 查询成果管理列表 */
getList() {
console.log(this.queryParams.xmid,6666);
this.loading = true
listWjscqk(this.queryParams).then(response => {
this.wjscqkList = response.rows
this.total = response.total
const mergedRows = this.mergeRowsById(response.rows || [])
this.wjscqkList = mergedRows
this.total = mergedRows.length
this.loading = false
})
},
/** 将相同id的记录进行合并(合并文件名与文件路径) */
mergeRowsById(rows) {
const idToRowMap = {}
const mergeCommaSeparated = (first, second) => {
const parts = []
if (first) parts.push(...String(first).split(',').filter(Boolean))
if (second) parts.push(...String(second).split(',').filter(Boolean))
const seen = new Set()
return parts.filter(item => {
if (seen.has(item)) return false
seen.add(item)
return true
}).join(',') || null
}
rows.forEach(row => {
const key = row.id
if (key == null) {
// 无id的记录直接保留为独立项
const tempKey = `__no_id__${Math.random()}_${Date.now()}`
idToRowMap[tempKey] = { ...row }
return
}
if (!idToRowMap[key]) {
idToRowMap[key] = { ...row }
} else {
const existing = idToRowMap[key]
existing.wjmc = mergeCommaSeparated(existing.wjmc, row.wjmc)
existing.wjlj = mergeCommaSeparated(existing.wjlj, row.wjlj)
// 其它字段保持首次出现的值,或可在此按需处理
}
})
return Object.values(idToRowMap)
},
// 取消按钮
cancel() {
this.open = false
......@@ -239,6 +239,7 @@ export default {
ext2: null,
ext3: null
}
this.fileList = []
this.resetForm("form")
},
/** 搜索按钮操作 */
......@@ -260,6 +261,10 @@ export default {
/** 新增按钮操作 */
handleAdd() {
this.reset()
// 设置xmid参数
if (this.queryParams.xmid) {
this.form.xmid = this.queryParams.xmid
}
this.open = true
this.title = "添加成果管理"
},
......@@ -269,6 +274,19 @@ export default {
const id = row.id || this.ids
getWjscqk(id).then(response => {
this.form = response.data
// 确保xmid参数被保持
if (this.queryParams.xmid) {
this.form.xmid = this.queryParams.xmid
}
// 如果有文件信息,加载到文件列表中(单文件)
if (this.form.wjmc && this.form.wjlj) {
this.fileList = [{
name: this.form.wjmc,
url: this.form.wjlj,
status: 'success',
uid: Date.now()
}]
}
this.open = true
this.title = "修改成果管理"
})
......@@ -308,6 +326,75 @@ export default {
this.download('yscgWjscqk/wjscqk/export', {
...this.queryParams
}, `wjscqk_${new Date().getTime()}.xlsx`)
},
/** 获取资料类型选项 */
getZllxOptions() {
selectZllx().then(response => {
this.zllxOptions = response.rows || []
}).catch(() => {
this.zllxOptions = []
})
},
/** 上传前校验 */
handleBeforeUpload(file) {
// 校验文件类型
const allowedTypes = ['doc', 'docx', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'jpg', 'jpeg', 'png', 'gif']
const fileExt = file.name.split('.').pop().toLowerCase()
if (!allowedTypes.includes(fileExt)) {
this.$modal.msgError('文件格式不正确,请上传支持的文件格式!')
return false
}
// 校验文件大小
const isLt50M = file.size / 1024 / 1024 < 50
if (!isLt50M) {
this.$modal.msgError('上传文件大小不能超过 50MB!')
return false
}
return true
},
/** 文件状态改变 */
handleFileChange(file, fileList) {
// 如果文件列表超过1个,只保留最新的文件
if (fileList.length > 1) {
this.fileList = [fileList[fileList.length - 1]]
} else {
this.fileList = fileList
}
this.updateFormFiles()
},
/** 上传成功 */
handleUploadSuccess(response, file, fileList) {
this.fileList = fileList
this.updateFormFiles()
this.$modal.msgSuccess('文件上传成功')
},
/** 上传失败 */
handleUploadError(err, file, fileList) {
this.$modal.msgError('文件上传失败')
},
/** 删除文件 */
handleRemove(file, fileList) {
this.fileList = fileList
this.updateFormFiles()
},
/** 文件数量超出限制 */
handleExceed(files, fileList) {
// 由于已实现自动替换,此方法不再需要显示错误提示
// 新文件会自动替换旧文件
},
/** 更新表单文件信息 */
updateFormFiles() {
if (this.fileList && this.fileList.length > 0) {
// 单文件上传,直接取第一个文件
const file = this.fileList[0]
this.form.wjmc = file.name
this.form.wjlj = file.response ? file.response.url : file.url
} else {
this.form.wjmc = null
this.form.wjlj = null
}
}
}
}
......
<!-- 成果管理 -->
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="项目名称" prop="xmmc">
<el-input v-model="queryParams.xmmc" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="区块" prop="qk">
<el-input v-model="queryParams.qk" placeholder="请输入区块" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<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>
</el-form>
<el-table border v-loading="loading" :data="yscgList" :span-method="spanMethod">
<el-table-column label="项目名称" align="center" prop="xmmc" min-width="150" show-overflow-tooltip />
<el-table-column label="区块" align="center" prop="qk" min-width="120" show-overflow-tooltip />
<el-table-column label="资料类型" align="center" prop="zllxmc" min-width="120" show-overflow-tooltip />
<el-table-column label="上传情况" align="center" min-width="120">
<template slot-scope="scope">
<i :class="scope.row.zlsl > 0 ? 'el-icon-check' : 'el-icon-close'"
:style="{ color: scope.row.zlsl > 0 ? '#67C23A' : '#F56C6C', fontSize: '18px' }">
</i>
</template>
</el-table-column>
<el-table-column label="成果" align="center" prop="id" min-width="100" show-overflow-tooltip>
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">查看成果</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
</div>
</template>
<script>
import { listYscgList } from "@/api/yscgWjscqk/wjscqk"
export default {
name: "Yscg",
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 成果管理表格数据
yscgList: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
xmmc: null,
qk: null
}
}
},
created() {
this.getList()
},
methods: {
/** 查询成果管理列表 */
getList() {
this.loading = true
listYscgList(this.queryParams).then(response => {
this.yscgList = response.rows
this.total = response.total
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
},
/** 成果按钮操作 */
handleView(row) {
// 跳转到成果详情页面,传id
this.$router.push({
path: '/yscgWjscqk/wjscqk/index',
query: {
xmid: row.id
}
})
},
/** 表格行合并方法 */
spanMethod({ row, column, rowIndex, columnIndex }) {
// 项目名称列(第0列)、区块列(第1列)和成果列(第3列)需要合并
if (columnIndex === 0 || columnIndex === 1 || columnIndex === 4) {
let fieldName, currentValue
if (columnIndex === 0) {
fieldName = 'xmmc'
currentValue = row[fieldName]
} else if (columnIndex === 1) {
fieldName = 'qk'
currentValue = row[fieldName]
} else if (columnIndex === 4) {
fieldName = 'id'
currentValue = row[fieldName]
}
// 如果当前值为空,不合并
if (!currentValue) {
return [1, 1]
}
// 如果当前值与上一行相同,则隐藏当前行
if (rowIndex > 0 && this.yscgList[rowIndex - 1][fieldName] === currentValue) {
return [0, 0]
}
// 计算需要合并的行数
let rowspan = 1
for (let i = rowIndex + 1; i < this.yscgList.length; i++) {
if (this.yscgList[i][fieldName] === currentValue) {
rowspan++
} else {
break
}
}
return [rowspan, 1]
}
// 其他列不合并
return [1, 1]
}
}
}
</script>
......@@ -26,7 +26,7 @@ export default {
height: { type: Number, default: 0 },
idOverride: { type: [String, Number], default: null }
},
data() {
data () {
return {
chart: null,
resizeObserver: null,
......@@ -46,7 +46,7 @@ export default {
points: []
}
},
mounted() {
mounted () {
// 仅使用延迟初始化,确保容器有尺寸后再 init
this.deferInit()
window.addEventListener('resize', this.resizeChart)
......@@ -69,16 +69,16 @@ export default {
})
}
},
created() {
created () {
// 尽早触发接口调用,进入页面即发起请求
this.loadDht()
},
activated() {
activated () {
// 当页面被 keep-alive 缓存后再次进入时,确保尺寸和图表刷新
this.deferInit()
this.resizeChart()
},
beforeDestroy() {
beforeDestroy () {
window.removeEventListener('resize', this.resizeChart)
if (this.chart) {
this.chart.dispose()
......@@ -91,7 +91,7 @@ export default {
},
methods: {
// 等待容器有有效尺寸再初始化,避免 echarts 容器宽高为 0 报错
deferInit() {
deferInit () {
const tryInit = () => {
const el = this.$refs.chartRef
if (!el) return
......@@ -135,10 +135,10 @@ export default {
}
this.$nextTick(tryInit)
},
resizeChart() {
resizeChart () {
if (this.chart) this.chart.resize()
},
initChart() {
initChart () {
const el = this.$refs.chartRef
if (!el) return
if (this.chart) {
......@@ -153,6 +153,7 @@ export default {
// 初始渲染
this.renderChart()
this.resizeChart()
this.bindChartEvents()
// 对于缩略图模式,确保渲染成功
if (this.compact) {
......@@ -160,7 +161,7 @@ export default {
}
},
// 数据变化后自动刷新(确保缩略图及时绘制)
_requestRender() {
_requestRender () {
if (!this.chart) return
this.$nextTick(() => {
this.renderChart()
......@@ -173,7 +174,7 @@ export default {
})
},
// 调用地层/断层图接口(示例),优先从路由参数获取 id
loadDht() {
loadDht () {
if (this.loadingDht || this.dhtResult) return
try {
const route = this.$route || {}
......@@ -201,7 +202,7 @@ export default {
}
},
// 将接口返回的数据应用到图表状态
applyDhtResult() {
applyDhtResult () {
const data = this.dhtResult || {}
// 边界
this.xMin = data.xmin != null ? Number(data.xmin) : this.xMin
......@@ -247,7 +248,7 @@ export default {
} catch (e) { }
},
// 生成图表配置
buildOption() {
buildOption () {
// 若未提供边界,则根据数据自动估算
const allXs = []
const allYs = []
......@@ -321,10 +322,41 @@ export default {
}
},
// 渲染或更新图表
renderChart() {
renderChart () {
if (!this.chart) return
const option = this.buildOption()
this.chart.setOption(option, true)
// 确保事件绑定有效
this.bindChartEvents()
},
// 绑定点击/双击事件,把被点击的绿色线条映射为 segy 下标并上报
bindChartEvents () {
if (!this.chart) return
// 解除旧绑定避免重复
this.chart.off('click')
this.chart.off('dblclick')
const getLineIndexBySeries = (seriesIndex) => {
// 如果有井点散点,最前面占 1 个 series
const hasPoints = Array.isArray(this.points) && this.points.length > 0
const base = hasPoints ? 1 : 0
// 每条线我们 push 了两个 series:line 和一个灰色散点
if (seriesIndex < base) return -1
const offset = seriesIndex - base
// 偶数 -> 折线;奇数 -> 叠加的灰色散点
return Math.floor(offset / 2)
}
const emitPick = (params) => {
if (!params || (params.seriesType !== 'line' && params.seriesType !== 'scatter')) return
const idx = getLineIndexBySeries(params.seriesIndex)
if (idx >= 0 && idx < this.segyLines.length) {
this.$emit('segyLinePick', { index: idx })
}
}
this.chart.on('click', emitPick)
this.chart.on('dblclick', emitPick)
}
}
}
......
......@@ -7,20 +7,25 @@
<el-button size="small" @click="setHorizontalLayout">左右结构</el-button>
<el-button size="small" @click="handlePrevTrace">上一道</el-button>
<el-button size="small" @click="handleNextTrace">下一道</el-button>
<div ref="thumbContainer" @click="handleThumbClick">
<div ref="thumbContainer" @click="handleThumbClick" class="right-thumb">
<div class="thumb-title">缩略图</div>
<div>1234567</div>
<div ref="thumbHolder">
<YsgcIndex v-if="thumbReady" :key="routeId + '-' + thumbReady" :compact="true" :width="260"
:height="160" :id-override="routeId" />
<div ref="thumbHolder" class="thumb-holder">
<YsgcIndex v-if="thumbReady" :key="routeId + '-' + thumbReady" :compact="true" :width="260" :height="160"
:id-override="routeId" />
</div>
</div>
<!-- 缩略图弹窗 start -->
<el-dialog title="缩略图预览" :visible.sync="showThumbDialog" width="80%" append-to-body>
<div style="height: 100%;">
<YsgcIndex :id-override="routeId" @segyLinePick="onThumbLinePick" />
</div>
</el-dialog>
<!-- 缩略图弹窗 end -->
</div>
<!-- 缩略图挂载到 body(右上角),不再在模板里渲染固定块 -->
</div>
<!-- 添加加载状态和错误提示 -->
<div v-if="loadingError" class="error-message">
......@@ -43,9 +48,9 @@
</el-option>
</el-select>
</div>
<toolbar-controls :plots="plots" :data-loaded="isWidgetReady"
:show-line-style-panel="showLineStylePanel" @toggleLineStylePanel="toggleLineStylePanel"
@exporting="exporting = $event" @closePrintDialog="showPrintDialog = false" />
<toolbar-controls :plots="plots" :data-loaded="isWidgetReady" :show-line-style-panel="showLineStylePanel"
@toggleLineStylePanel="toggleLineStylePanel" @exporting="exporting = $event"
@closePrintDialog="showPrintDialog = false" />
<el-tooltip content="绘制文本" placement="bottom" effect="light">
<el-button @click="toggleDrawText" :class="{ 'active': isDrawingText }">
......@@ -59,8 +64,7 @@
</el-tooltip>
<el-tooltip :content="internalScrollbarSyncEnabled ? '内部滚动条同步已启用' : '内部滚动条同步已禁用'" placement="bottom"
effect="light">
<el-button @click="toggleInternalScrollbarSync"
:class="{ 'active': internalScrollbarSyncEnabled }">
<el-button @click="toggleInternalScrollbarSync" :class="{ 'active': internalScrollbarSyncEnabled }">
<i class="el-icon-s-operation"></i>
</el-button>
</el-tooltip>
......@@ -153,15 +157,14 @@
</div>
<div class="style-item">
<span class="style-label">大小:</span>
<el-slider v-model="textStyle.size" :min="8" :max="60" show-input
@change="handleFontSizeChange">
<el-slider v-model="textStyle.size" :min="8" :max="60" show-input @change="handleFontSizeChange">
</el-slider>
</div>
<div class="style-item">
<span class="style-label">字体:</span>
<el-select v-model="textStyle.font" placeholder="选择字体" @change="handleFontChange">
<el-option v-for="(font, index) in Object.keys(TEXT_PATTERNS)" :key="index" :label="font"
:value="font" :style="{ fontFamily: font }">
<el-option v-for="(font, index) in Object.keys(TEXT_PATTERNS)" :key="index" :label="font" :value="font"
:style="{ fontFamily: font }">
{{ font }}
</el-option>
</el-select>
......@@ -170,30 +173,25 @@
<span class="style-label">对齐:</span>
<div class="text-style-buttons">
<el-button-group style="width: 100%">
<el-button style="min-width: 20%; max-width: 20%"
:type="textStyle.align === 'left' ? 'primary' : ''"
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.align === 'left' ? 'primary' : ''"
@click="textStyle.align = 'left'; updateTextStyle()" title="左对齐">
<i class="el-icon-s-fold"></i>
</el-button>
<el-button style="min-width: 20%; max-width: 20%"
:type="textStyle.align === 'center' ? 'primary' : ''"
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.align === 'center' ? 'primary' : ''"
@click="textStyle.align = 'center'; updateTextStyle()" title="居中对齐">
<i class="el-icon-menu"></i>
</el-button>
<el-button style="min-width: 20%; max-width: 20%"
:type="textStyle.align === 'right' ? 'primary' : ''"
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.align === 'right' ? 'primary' : ''"
@click="textStyle.align = 'right'; updateTextStyle()" title="右对齐">
<i class="el-icon-s-unfold"></i>
</el-button>
<!-- 粗体按钮 -->
<el-button style="min-width: 20%; max-width: 20%"
:type="textStyle.bold ? 'primary' : ''"
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.bold ? 'primary' : ''"
@click="textStyle.bold = !textStyle.bold; updateTextStyle()" title="粗体">
<span style="font-weight: bold">B</span>
</el-button>
<!-- 斜体按钮 -->
<el-button style="min-width: 20%; max-width: 20%"
:type="textStyle.italic ? 'primary' : ''"
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.italic ? 'primary' : ''"
@click="textStyle.italic = !textStyle.italic; updateTextStyle()" title="斜体">
<span style="font-style: italic">I</span>
</el-button>
......@@ -344,7 +342,7 @@ export default {
LineStylePanel,
YsgcIndex
},
data() {
data () {
return {
// 布局模式:vertical 上下结构;horizontal 左右结构
layoutMode: 'vertical',
......@@ -700,10 +698,13 @@ export default {
_lastBottomScrollLeft: 0, // 记录底部滚动条水平位置
_lastBottomScrollTop: 0, // 记录底部滚动条垂直位置
thumbReady: false,
showThumbDialog: false, // 缩略图大图弹窗可见性
// 是否允许在未指定项目/无接口数据时加载示例数据
shouldLoadDemo: false,
};
},
computed: {
routeId() {
routeId () {
try {
const route = this.$route || {};
const params = route.params || {};
......@@ -714,7 +715,7 @@ export default {
}
}
},
mounted() {
mounted () {
// 初始化时强制设置为初始状态
this.resetToInitialState();
......@@ -727,15 +728,15 @@ export default {
// 关闭强力滚动条监控,避免影响全局布局
// this.startAggressiveScrollbarMonitor();
this.$nextTick(() => {
this.$nextTick(async () => {
['thumb-test-container', 'thumb-chart-container', 'super-visible-thumb'].forEach(id => {
const el = document.getElementById(id);
if (el) try { el.remove(); } catch (e) { }
});
if (this.$refs.thumbContainer && this.$refs.thumbContainer.parentNode !== document.body) {
document.body.appendChild(this.$refs.thumbContainer);
}
this.createScene(this.$refs.plot);
// 保持缩略图容器在组件内部,避免在DOM树中难以定位
// 初始化场景时不自动加载演示数据,等待接口结果决定
this.shouldLoadDemo = false;
this.createScene(this.$refs.plot, { autoloadDemo: false });
// 等DOM稳定后再渲染缩略图组件
setTimeout(() => {
this.thumbReady = true;
......@@ -746,6 +747,24 @@ export default {
}, 200);
}, 100);
// 已改为内嵌 YsgcIndex 缩略图,不再走旧的 ECharts 缩略图初始化
// 自动拉取接口数据并加载第一组上/下部 SEGY(jbsegy -> 顶部,xbsegy -> 底部)
try {
await this.loadThumbData();
if (Array.isArray(this.segyList) && this.segyList.length > 0) {
await this.loadCurrentSegy();
} else {
// 无接口数据:若没有路由id,允许加载演示数据;若有id但无数据,保持空白
const hasId = !!this.routeId;
if (!hasId) {
this.shouldLoadDemo = false;
try { await this.handleFileSelect(this.plots); } catch (e) { }
} else {
this.clearCurrentPlots();
this.$message.info('该项目无可用SEGY数据');
}
}
} catch (e) { /* ignore */ }
});
if (this.$refs.plot) {
......@@ -755,26 +774,8 @@ export default {
// 添加键盘事件监听
}
},
mounted() {
// 将缩略图容器移动到body,确保fixed基于视口
if (this.$refs.thumbContainer && this.$refs.thumbContainer.parentNode !== document.body) {
document.body.appendChild(this.$refs.thumbContainer);
}
this.createScene(this.$refs.plot);
// 等DOM稳定后再渲染缩略图组件
setTimeout(() => {
this.thumbReady = true;
// 再兜底:确保右上角出现内容,并直接绘制缩略图
setTimeout(() => {
this.ensureThumbRendered();
}, 200);
}, 100);
console.log('测试函数已添加:');
console.log('- window.testOuterScrollbarSync() - 测试外层滚动条同步');
console.log('- window.testInternalScrollbarHidden() - 测试内部滚动条隐藏');
},
beforeDestroy() {
// 删除重复的 mounted,避免生命周期被覆盖
beforeDestroy () {
// 移除右键菜单事件监听
if (this.$refs.plot) {
this.$refs.plot.removeEventListener('contextmenu', this.showContextMenu);
......@@ -786,7 +787,7 @@ export default {
window.removeEventListener('resize', this.updateBaseCanvasSize);
},
methods: {
ensureThumbRendered() {
ensureThumbRendered () {
try {
const holder = this.$refs.thumbHolder;
if (!holder) return;
......@@ -805,7 +806,7 @@ export default {
}
} catch (e) { /* ignore */ }
},
drawThumbDirect() {
drawThumbDirect () {
try {
const holder = this.$refs.thumbHolder;
if (!holder) return;
......@@ -829,26 +830,53 @@ export default {
animation: false
};
chart.setOption(option, true);
// 绑定双击事件:双击某条线,加载对应的上下SEGY(新/旧)
try {
// 系列下标说明:
// buildThumbOption 会在最前面插入边框(line) => 索引0
// 如果存在井点,会有一个散点系列 => 可能是索引1
// 之后按顺序依次是每一条线。我们需要把 seriesIndex 映射到 segyList 的下标
const hasPoints = Array.isArray(this.thumbPoints) && this.thumbPoints.length > 0;
const lineSeriesBaseIndex = 1 + (hasPoints ? 1 : 0); // 边框1 + 可选散点1
chart.off('dblclick');
chart.on('dblclick', (params) => {
if (params && params.seriesType === 'line') {
const seriesIndex = params.seriesIndex;
const lineIdx = seriesIndex - lineSeriesBaseIndex;
if (lineIdx >= 0 && Array.isArray(this.segyList) && lineIdx < this.segyList.length) {
const seg = this.segyList[lineIdx];
this.currentSegyIndex = lineIdx;
if (seg) {
// 加载该线对应的新/旧SEGY:jbsegy -> 顶部,xbsegy -> 底部
this.loadDualSegyFiles(seg.jbsegy, seg.xbsegy, seg.jbsegyName, seg.xbsegyName);
this.$message.success(`已加载第${lineIdx + 1}条线的数据`);
}
}
}
});
} catch (e) { /* ignore binding errors */ }
} catch (e) { /* ignore */ }
},
// 顶部操作:切换布局与上一/下一道(占位)
setVerticalLayout() {
setVerticalLayout () {
this.layoutMode = 'vertical';
this.showDualChart();
this.$nextTick(() => this.resizeAfterLayout());
},
// 已移除全局浮动缩略图容器,缩略图仅在本页右上角显示
setHorizontalLayout() {
setHorizontalLayout () {
this.layoutMode = 'horizontal';
this.showDualChart();
this.$nextTick(() => this.resizeAfterLayout());
},
resizeAfterLayout() {
resizeAfterLayout () {
try { this.updateBaseCanvasSize(); } catch (e) { }
try { this._seismicWidget && this._seismicWidget.invalidate(); } catch (e) { }
try { this._seismicWidgetBottom && this._seismicWidgetBottom.invalidate(); } catch (e) { }
},
async handlePrevTrace() {
async handlePrevTrace () {
console.log('[index2] 上一道 clicked');
if (!Array.isArray(this.segyList) || this.segyList.length === 0) {
console.log('[index2] segyList 为空,尝试重新加载');
......@@ -868,7 +896,7 @@ export default {
// 加载当前segy文件
this.loadCurrentSegy();
},
async handleNextTrace() {
async handleNextTrace () {
console.log('[index2] 下一道 clicked');
if (!Array.isArray(this.segyList) || this.segyList.length === 0) {
console.log('[index2] segyList 为空,尝试重新加载');
......@@ -889,7 +917,7 @@ export default {
this.loadCurrentSegy();
},
// 加载当前segy文件
async loadCurrentSegy() {
async loadCurrentSegy () {
if (this.segyList.length === 0) {
console.log('[index2] 没有segy数据可加载');
return;
......@@ -914,7 +942,7 @@ export default {
}
},
// 清空当前图表
clearCurrentPlots() {
clearCurrentPlots () {
console.log('[index2] 清空当前图表');
try {
// 清空主图表
......@@ -933,7 +961,7 @@ export default {
}
},
// 加载segy文件的具体实现
async loadSegyFile(segyPath) {
async loadSegyFile (segyPath) {
console.log('[index2] 开始加载segy文件:', segyPath);
try {
......@@ -956,7 +984,7 @@ export default {
}
},
// 同时加载上下两个segy(jbsegy到顶部,xbsegy到底部)
async loadDualSegyFiles(jbPath, xbPath, jbName, xbName) {
async loadDualSegyFiles (jbPath, xbPath, jbName, xbName) {
// 兜底:若无路径,则给出提示
if (!jbPath && !xbPath) {
this.$message.warning('无可加载的SEGY文件');
......@@ -970,7 +998,7 @@ export default {
this.clearCurrentPlots();
// 确保两个画布已初始化
if (this.$refs.plot) this.createScene(this.$refs.plot);
if (this.$refs.plot) this.createScene(this.$refs.plot, { autoloadDemo: false });
if (this.$refs.plot2) this.initSecondWidget(this.$refs.plot2);
// 顺序加载,分别统计成功与失败
......@@ -1003,7 +1031,7 @@ export default {
}
},
// 将指定segy加载到顶部/底部widget
async loadSegyIntoWidget(position, segyPath, displayName) {
async loadSegyIntoWidget (position, segyPath, displayName) {
// 构造可访问的完整URL
const url = this.buildSegyURL(segyPath);
......@@ -1069,7 +1097,7 @@ export default {
}
},
// 根据后端网关构造SEGY访问URL
buildSegyURL(path) {
buildSegyURL (path) {
if (!path) return '';
// 如果已经是完整URL,直接返回
if (/^https?:\/\//i.test(path)) return path;
......@@ -1083,7 +1111,7 @@ export default {
return `${base}${p}`;
},
// 拉取SEGY二进制
async fetchSegyBlob(url) {
async fetchSegyBlob (url) {
if (!url) throw new Error('无效的SEGY地址');
const res = await fetch(url, { headers: { Authorization: 'Bearer ' + getToken() } });
if (!res.ok) throw new Error(`网络请求失败: ${res.status} ${res.statusText}`);
......@@ -1092,7 +1120,7 @@ export default {
return blob;
},
// 显示单个图表(隐藏底部图表)
showSingleChart() {
showSingleChart () {
console.log('[index2] 切换到单图表显示模式');
// 隐藏底部图表容器
......@@ -1112,7 +1140,7 @@ export default {
this.layoutMode = 'single';
},
// 显示双图表(恢复上下或左右布局)
showDualChart() {
showDualChart () {
console.log('[index2] 切换到双图表显示模式');
// 显示底部图表容器
......@@ -1134,7 +1162,7 @@ export default {
}
},
// 清空场景
clearScene(container) {
clearScene (container) {
if (!container) return;
try {
......@@ -1150,161 +1178,37 @@ export default {
console.warn('[index2] 清空场景时出错:', e);
}
},
handleThumbClick() {
const id = this.routeId;
this.$router.push({ name: 'YsgcIndex', query: id ? { id } : {} });
handleThumbClick () {
this.showThumbDialog = true;
},
// 处理缩略图(绿色线)点击/双击事件
onThumbLinePick (payload) {
try {
if (!payload || typeof payload.index !== 'number') return;
const lineIdx = payload.index;
if (!Array.isArray(this.segyList) || lineIdx < 0 || lineIdx >= this.segyList.length) return;
const seg = this.segyList[lineIdx];
this.currentSegyIndex = lineIdx;
if (seg) {
this.loadDualSegyFiles(seg.jbsegy, seg.xbsegy, seg.jbsegyName, seg.xbsegyName);
this.$message.success(`已加载第${lineIdx + 1}条线的数据`);
}
} catch (e) { /* ignore */ }
},
// 确保缩略图渲染
ensureThumbRendered() {
ensureThumbRendered () {
if (!this.thumbReady) {
this.thumbReady = true;
}
},
// ====== 缩略图(ECharts) ======
async initThumb() {
async initThumb () {
// 已弃用:现在缩略图直接嵌入 YsgcIndex
return;
console.log('[index2] ===== initThumb START =====');
console.log('[index2] initThumb called by button click');
try {
// 重试机制:如果ref没找到,等待一段时间再试
let holder = this.$refs.thumbHolder;
console.log('[index2] thumbHolder ref:', holder);
console.log('[index2] thumbHolder element:', holder ? holder.outerHTML : 'null');
// 不再查找全局固定容器,仅使用本页右上角容器
if (!holder) {
console.warn('[index2] thumbHolder ref not found, retrying...');
await new Promise(resolve => setTimeout(resolve, 500));
holder = this.$refs.thumbHolder;
console.log('[index2] thumbHolder ref after retry:', holder);
}
// 最终兜底:如果依然找不到,则直接返回
if (!holder) {
console.warn('[index2] thumbHolder still not found in this page');
return;
}
if (this.thumbChart) {
try { this.thumbChart.dispose(); } catch (e) { }
this.thumbChart = null;
}
// 强制设置尺寸并直接初始化(使用 !important 提升优先级)
holder.style.setProperty('width', '260px', 'important');
holder.style.setProperty('height', '160px', 'important');
holder.style.setProperty('display', 'block', 'important');
holder.style.setProperty('visibility', 'visible', 'important');
holder.style.setProperty('min-width', '260px', 'important');
holder.style.setProperty('min-height', '160px', 'important');
holder.style.setProperty('max-width', '260px', 'important');
holder.style.setProperty('max-height', '160px', 'important');
holder.style.setProperty('flex', 'none', 'important'); // 防止flex布局影响
holder.style.setProperty('position', 'relative', 'important'); // 确保定位正确
holder.style.setProperty('overflow', 'hidden', 'important'); // 防止内容溢出
// 强制设置父容器尺寸
const parent = holder.parentElement;
if (parent) {
// 强制父容器为固定定位且可见,避免0尺寸(使用 !important)
parent.style.setProperty('position', 'fixed', 'important');
parent.style.setProperty('top', '16px', 'important');
parent.style.setProperty('right', '16px', 'important');
parent.style.setProperty('z-index', '2147483647', 'important');
parent.style.setProperty('display', 'block', 'important');
parent.style.setProperty('visibility', 'visible', 'important');
parent.style.setProperty('width', '260px', 'important');
parent.style.setProperty('height', '200px', 'important');
parent.style.setProperty('min-height', '200px', 'important');
// 再次读取尺寸
console.log('[index2] parent container size set:', parent.clientWidth, 'x', parent.clientHeight);
}
console.log('[index2] thumb holder size after force set:', holder.clientWidth, 'x', holder.clientHeight);
console.log('[index2] thumb holder offsetWidth:', holder.offsetWidth, 'x', holder.offsetHeight);
console.log('[index2] thumb holder getBoundingClientRect:', holder.getBoundingClientRect());
// 等待一帧确保样式生效
await new Promise(resolve => requestAnimationFrame(resolve));
console.log('[index2] thumb holder size after animation frame:', holder.clientWidth, 'x', holder.clientHeight);
// 等待更长时间确保DOM完全渲染
await new Promise(resolve => setTimeout(resolve, 1000));
// 再次检查尺寸
console.log('[index2] thumb holder size after 1s delay:', holder.clientWidth, 'x', holder.clientHeight);
// 强制设置容器尺寸
holder.style.width = '260px';
holder.style.height = '160px';
holder.style.minWidth = '260px';
holder.style.minHeight = '160px';
holder.style.maxWidth = '260px';
holder.style.maxHeight = '160px';
// 如果容器尺寸仍然为0,使用独立容器
if (holder.clientWidth === 0 || holder.clientHeight === 0) {
console.log('[index2] Container size is 0, force setting fixed size and continue');
holder.style.width = '260px';
holder.style.height = '160px';
}
// 初始化ECharts
this.thumbChart = echarts.init(holder, null, {
width: 260,
height: 160,
renderer: 'canvas' // 强制使用canvas渲染器
});
console.log('[index2] thumb chart init directly');
console.log('[index2] thumb chart instance:', this.thumbChart);
// 先显示一个占位图,确保容器可见
try {
this.thumbChart.setOption({
grid: { top: 10, left: 10, right: 10, bottom: 10 },
xAxis: { type: 'value', min: 0, max: 100, show: false },
yAxis: { type: 'value', min: 0, max: 100, show: false },
series: [{ type: 'line', data: [[0, 0], [100, 100]], showSymbol: false, lineStyle: { color: '#d3d3d3' } }],
animation: false
}, true);
} catch (e) { console.warn('[index2] placeholder option failed', e); }
await this.loadThumbData();
this.renderThumb();
// 立即强制渲染
this.thumbChart.resize();
console.log('[index2] thumb chart immediate resize');
// 强制重新渲染
setTimeout(() => {
if (this.thumbChart) {
this.thumbChart.resize();
console.log('[index2] thumb chart resize called');
// 强制重新设置选项,确保图表显示
this.thumbChart.setOption(this.buildThumbOption(), true);
console.log('[index2] thumb chart option reset');
}
}, 100);
// 监听尺寸变化
if (!this.thumbResizeObserver) {
this.thumbResizeObserver = new ResizeObserver(() => {
try { this.thumbChart && this.thumbChart.resize(); } catch (e) { }
});
this.thumbResizeObserver.observe(holder);
}
} catch (e) {
console.warn('[index2] initThumb failed:', e);
// 不再调用备用方法,避免产生左下角黄色框
return;
}
console.log('[index2] ===== initThumb END =====');
},
// 测试缩略图方法
testThumb() {
testThumb () {
console.log('[index2] ===== testThumb START =====');
// 创建完全独立的测试容器
......@@ -1339,7 +1243,7 @@ export default {
console.log('[index2] ===== testThumb END =====');
},
// 测试ECharts方法
testECharts() {
testECharts () {
console.log('[index2] ===== testECharts START =====');
// 清理现有容器
......@@ -1422,14 +1326,14 @@ export default {
console.log('[index2] ===== testECharts END =====');
},
// 测试按钮点击
testButtonClick() {
testButtonClick () {
console.log('[index2] ===== testButtonClick START =====');
alert('按钮点击测试成功!');
console.log('[index2] ===== testButtonClick END =====');
},
// 创建超级明显的缩略图测试
createSuperVisibleThumb() {
createSuperVisibleThumb () {
console.log('[index2] ===== createSuperVisibleThumb START =====');
// 清理现有容器
......@@ -1576,7 +1480,7 @@ export default {
console.log('[index2] ===== createSuperVisibleThumb END =====');
},
// 重置缩略图方法
resetThumb() {
resetThumb () {
console.log('[index2] ===== resetThumb START =====');
// 清理独立容器
......@@ -1621,7 +1525,7 @@ export default {
console.log('[index2] ===== resetThumb END =====');
},
// 备用缩略图初始化方法
async initThumbFallback() {
async initThumbFallback () {
console.log('[index2] ===== initThumbFallback START =====');
try {
// 创建完全独立的ECharts容器
......@@ -1735,7 +1639,7 @@ export default {
}
console.log('[index2] ===== initThumbFallback END =====');
},
async loadThumbData() {
async loadThumbData () {
try {
// 与 index.vue 相同的取数方式:优先从路由取 id
const route = this.$route || {};
......@@ -1833,7 +1737,7 @@ export default {
console.warn('[index2] loadThumbData failed:', e);
}
},
buildThumbOption() {
buildThumbOption () {
const series = [];
if (this.thumbPoints && this.thumbPoints.length) {
series.push({
......@@ -1936,7 +1840,7 @@ export default {
}]
};
},
renderThumb() {
renderThumb () {
if (!this.thumbChart) {
console.warn('[index2] renderThumb: no chart instance');
return;
......@@ -1960,7 +1864,7 @@ export default {
}
},
// 更新基准宽高,基于外层容器实际尺寸
updateBaseCanvasSize() {
updateBaseCanvasSize () {
try {
const topEl = this.$refs.topScroll;
if (topEl) {
......@@ -1980,7 +1884,7 @@ export default {
},
// 根据 GeoToolkit 模型范围动态计算滚动区域尺寸
updateScrollAreaSize() {
updateScrollAreaSize () {
try {
if (!this._seismicWidget) return;
......@@ -2054,7 +1958,7 @@ export default {
},
// 彻底禁用滚动条工具
disableScrollbarTools() {
disableScrollbarTools () {
try {
// 等待widget创建完成后再禁用
setTimeout(() => {
......@@ -2071,7 +1975,7 @@ export default {
},
// 禁用单个widget的滚动条
disableWidgetScrollbar(widget) {
disableWidgetScrollbar (widget) {
try {
if (!widget) return;
......@@ -2154,7 +2058,7 @@ export default {
},
// 强制隐藏滚动条DOM元素
forceHideScrollbarDOM(widget) {
forceHideScrollbarDOM (widget) {
try {
if (!widget || !widget.getCanvas) return;
......@@ -2225,7 +2129,7 @@ export default {
},
// 强制重置到初始状态,不显示滚动条
resetToInitialState() {
resetToInitialState () {
try {
const topEl = this.$refs.topScroll;
if (topEl) {
......@@ -2245,7 +2149,7 @@ export default {
},
// 创建下半屏镜像视图
initSecondWidget(canvas) {
initSecondWidget (canvas) {
try {
this.plotsBottom = new Plot({
'canvasElement': canvas,
......@@ -2319,7 +2223,7 @@ export default {
},
// 同步滚动条(左右与上下)- 防死循环版本
onScroll(origin) {
onScroll (origin) {
// 多重防护:防止死循环
if (this._isSyncingScroll || this._scrollSyncTimeout) return;
......@@ -2359,7 +2263,7 @@ export default {
},
// 让顶部/底部视图缩放同步
ensureZoomSyncHook() {
ensureZoomSyncHook () {
if (this._zoomHooked) return;
const top = this._seismicWidget;
const bottom = this._seismicWidgetBottom;
......@@ -2419,7 +2323,7 @@ export default {
},
// 更新内部滚动条工具
updateWidgetScrollbar(widget) {
updateWidgetScrollbar (widget) {
try {
const toolManager = widget && widget.getTool && widget.getTool();
if (!toolManager || !toolManager.getScrollbarTool) return;
......@@ -2431,7 +2335,7 @@ export default {
},
// 强制禁用内部滚动条
forceDisableInternalScrollbars(widget) {
forceDisableInternalScrollbars (widget) {
if (!widget) return;
try {
......@@ -2504,7 +2408,7 @@ export default {
},
// 定期检查并禁用内部滚动条
startScrollbarMonitor() {
startScrollbarMonitor () {
if (this._scrollbarMonitorInterval) {
clearInterval(this._scrollbarMonitorInterval);
}
......@@ -2547,7 +2451,7 @@ export default {
},
// 停止滚动条监控
stopScrollbarMonitor() {
stopScrollbarMonitor () {
if (this._scrollbarMonitorInterval) {
clearInterval(this._scrollbarMonitorInterval);
this._scrollbarMonitorInterval = null;
......@@ -2555,7 +2459,7 @@ export default {
},
// 使用CSS方法彻底禁用内部滚动条
setScrollbarCSS(widget) {
setScrollbarCSS (widget) {
if (!widget || !widget.setCss) return;
try {
......@@ -2665,7 +2569,7 @@ export default {
},
// 强制同步内部滚动条(如果必须保留内部滚动条)
enableInternalScrollbarSync() {
enableInternalScrollbarSync () {
if (!this._seismicWidget || !this._seismicWidgetBottom) return;
try {
......@@ -2684,7 +2588,7 @@ export default {
},
// 设置外层滚动条同步(GeoToolkit 3.2.80兼容模式)
setupOuterScrollbarSync() {
setupOuterScrollbarSync () {
if (!this._seismicWidget || !this._seismicWidgetBottom) return;
try {
// 清除之前的监听器
......@@ -2756,7 +2660,7 @@ export default {
},
// 清除外层滚动条监听器
clearOuterScrollbarListeners() {
clearOuterScrollbarListeners () {
try {
if (this._outerScrollListeners) {
this._outerScrollListeners.forEach(({ element, event, handler }) => {
......@@ -2772,9 +2676,9 @@ export default {
},
// 防抖函数
debounce(func, wait) {
debounce (func, wait) {
let timeout;
return function executedFunction(...args) {
return function executedFunction (...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
......@@ -2785,7 +2689,7 @@ export default {
},
// 设置DOM级别的滚动条同步
setupDOMScrollbarSync() {
setupDOMScrollbarSync () {
try {
// 清除之前的DOM监听器
this.clearDOMScrollbarListeners();
......@@ -2856,7 +2760,7 @@ export default {
},
// 获取外层滚动容器(GeoToolkit 3.2.80兼容)
getOuterScrollContainer(widget) {
getOuterScrollContainer (widget) {
try {
if (!widget || !widget.getCanvas) return null;
......@@ -2892,7 +2796,7 @@ export default {
},
// 同步GeoToolkit内部视图(GeoToolkit 3.2.80兼容)
syncGeoToolkitView(widget, scrollLeft, scrollTop) {
syncGeoToolkitView (widget, scrollLeft, scrollTop) {
try {
if (!widget) return;
......@@ -2952,7 +2856,7 @@ export default {
},
// 启动外层滚动条同步监控(GeoToolkit 3.2.80兼容)
startOuterScrollbarSyncMonitor() {
startOuterScrollbarSyncMonitor () {
if (this._outerScrollbarSyncInterval) {
clearInterval(this._outerScrollbarSyncInterval);
}
......@@ -2967,7 +2871,7 @@ export default {
},
// 强制外层滚动条同步(GeoToolkit 3.2.80兼容)
forceOuterScrollbarSync() {
forceOuterScrollbarSync () {
try {
if (!this._seismicWidget || !this._seismicWidgetBottom) return;
......@@ -3011,7 +2915,7 @@ export default {
},
// 停止外层滚动条同步监控
stopOuterScrollbarSyncMonitor() {
stopOuterScrollbarSyncMonitor () {
if (this._outerScrollbarSyncInterval) {
clearInterval(this._outerScrollbarSyncInterval);
this._outerScrollbarSyncInterval = null;
......@@ -3019,7 +2923,7 @@ export default {
},
// 强制隐藏所有内部滚动条
forceHideAllInternalScrollbars() {
forceHideAllInternalScrollbars () {
try {
// 方法1: 禁用widget滚动条
if (this._seismicWidget) {
......@@ -3045,7 +2949,7 @@ export default {
},
// 启动强力滚动条隐藏监控
startAggressiveScrollbarHideMonitor() {
startAggressiveScrollbarHideMonitor () {
if (this._scrollbarHideInterval) {
clearInterval(this._scrollbarHideInterval);
}
......@@ -3066,7 +2970,7 @@ export default {
},
// 禁用滚动条交互
disableScrollbarInteractions() {
disableScrollbarInteractions () {
try {
const rootEl = this.$el || document;
const scrollbarElements = rootEl.querySelectorAll ? rootEl.querySelectorAll('.sync-section [class*="scrollbar"], .sync-section [class*="Scrollbar"], .sync-section [class*="scroll"], .sync-section [class*="Scroll"], .sync-section [class*="geotoolkit.controls.tools.scroll"]') : [];
......@@ -3106,7 +3010,7 @@ export default {
},
// 停止滚动条隐藏监控
stopScrollbarHideMonitor() {
stopScrollbarHideMonitor () {
if (this._scrollbarHideInterval) {
clearInterval(this._scrollbarHideInterval);
this._scrollbarHideInterval = null;
......@@ -3114,7 +3018,7 @@ export default {
},
// 测试外层滚动条同步(GeoToolkit 3.2.80兼容)
testOuterScrollbarSync() {
testOuterScrollbarSync () {
try {
console.log('=== 测试外层滚动条同步 ===');
......@@ -3160,7 +3064,7 @@ export default {
},
// 测试内部滚动条是否被隐藏
testInternalScrollbarHidden() {
testInternalScrollbarHidden () {
try {
console.log('=== 测试内部滚动条是否被隐藏 ===');
......@@ -3214,7 +3118,7 @@ export default {
},
// 强制禁用内部滚动条(GeoToolkit 3.2.80兼容)
forceDisableInternalScrollbars() {
forceDisableInternalScrollbars () {
try {
if (!this._seismicWidget || !this._seismicWidgetBottom) return;
......@@ -3268,18 +3172,18 @@ export default {
},
// 启动滚动条同步状态监控
startScrollbarSyncStatusMonitor() {
startScrollbarSyncStatusMonitor () {
// 已禁用内部滚动条同步,直接返回
return;
},
// 确保滚动条同步(已禁用内部同步,直接返回)
ensureScrollbarSync() {
ensureScrollbarSync () {
return;
},
// 停止滚动条同步状态监控
stopScrollbarSyncStatusMonitor() {
stopScrollbarSyncStatusMonitor () {
if (this._scrollbarSyncStatusInterval) {
clearInterval(this._scrollbarSyncStatusInterval);
this._scrollbarSyncStatusInterval = null;
......@@ -3287,14 +3191,14 @@ export default {
},
// 添加强制隐藏滚动条的CSS(GeoToolkit 3.2.80兼容)
addForceHideScrollbarCSS() {
addForceHideScrollbarCSS () {
// 为避免影响全局左侧菜单,这里不再注入全局隐藏滚动条的样式。
// 内部滚动条隐藏交由 .sync-section 作用域 CSS 与 DOM 级隐藏来完成。
return;
},
// 启动强力滚动条监控
startAggressiveScrollbarMonitor() {
startAggressiveScrollbarMonitor () {
// 仅监控本页面容器,避免影响全局(菜单/标题)
if (this._scrollbarObserver) {
this._scrollbarObserver.disconnect();
......@@ -3332,7 +3236,7 @@ export default {
},
// 隐藏单个滚动条元素
hideScrollbarElement(element) {
hideScrollbarElement (element) {
if (!element || !element.classList) return;
const classList = Array.from(element.classList);
......@@ -3356,7 +3260,7 @@ export default {
},
// 强制隐藏所有滚动条(仅作用于给定根节点,默认当前组件内)
forceHideAllScrollbars(root) {
forceHideAllScrollbars (root) {
const rootEl = root || this.$el || document;
const query = rootEl.querySelectorAll ? rootEl.querySelectorAll.bind(rootEl) : document.querySelectorAll.bind(document);
const selectors = [
......@@ -3379,7 +3283,7 @@ export default {
},
// 停止强力滚动条监控
stopAggressiveScrollbarMonitor() {
stopAggressiveScrollbarMonitor () {
if (this._scrollbarObserver) {
this._scrollbarObserver.disconnect();
this._scrollbarObserver = null;
......@@ -3391,7 +3295,7 @@ export default {
},
// 强制禁用所有滚动条
forceDisableAllScrollbars() {
forceDisableAllScrollbars () {
try {
// 禁用widget滚动条
if (this._seismicWidget) {
......@@ -3414,7 +3318,7 @@ export default {
},
// 添加全局滚动条隐藏CSS
addGlobalScrollbarHideCSS() {
addGlobalScrollbarHideCSS () {
try {
// 检查是否已经添加过样式
if (document.getElementById('geotoolkit-scrollbar-hide')) {
......@@ -3455,7 +3359,7 @@ export default {
}
},
createScene(canvas) {
createScene (canvas, options) {
try {
const autoFormat = new AutoNumberFormat(4);
// 创建 Plot 实例,同时创建 SeismicWidget
......@@ -3579,9 +3483,10 @@ export default {
// 初始化默认的颜色映射,使用256色以获得更平滑的渐变
this._colorMap = this.colorProvider.createNamedColorMap('WhiteBlack', 256);
// 加载数据
// 根据需要加载默认演示数据
this.$nextTick(() => {
this.handleFileSelect(this.plots).then(() => {
const autoload = options && options.autoloadDemo === false ? false : this.shouldLoadDemo;
const doAfterLoad = () => {
// 数据加载完成后更新轴标题
if (this.pipeline && this._seismicWidget) {
this._seismicWidget.setOptions({
......@@ -3625,7 +3530,14 @@ export default {
// 启用内部滚动条同步
this.enableInternalScrollbarSync();
});
});
};
if (autoload) {
this.handleFileSelect(this.plots).then(doAfterLoad);
} else {
// 不加载演示数据,直接走后续初始化(保持空画布)
doAfterLoad();
}
});
} catch (error) {
this.loadingError = `创建场景失败: ${error.message}`;
......@@ -3633,7 +3545,7 @@ export default {
}
},
handleFileSelect(plot) {
handleFileSelect (plot) {
let url = process.env.VUE_APP_BASE_API + "/ndy/dz/getSegyDataFile?fileName=density.segy";
let fileName = 'density.segy';
......@@ -3798,20 +3710,20 @@ export default {
});
},
handleMouseMove(event) {
handleMouseMove (event) {
if (!this._seismicWidget || !this.isHovering) return;
// 使用内置状态栏,不需要额外的处理逻辑
},
onFileOpen(evt, plot, fileInfo) {
onFileOpen (evt, plot, fileInfo) {
evt.stopPropagation();
evt.preventDefault();
const files = evt.target.files;
return this.handleFileSelect(this.plots);
},
initAnnotationTools(widget) {
initAnnotationTools (widget) {
if (!this.checkWidgetStatus()) {
console.error('Widget status check failed');
return;
......@@ -3950,7 +3862,7 @@ export default {
}
},
toggleDrawText() {
toggleDrawText () {
if (!this.checkWidgetStatus() || !this.isWidgetReady) {
console.warn('Widget or annotation tools not ready');
return;
......@@ -4114,7 +4026,7 @@ export default {
}
},
removeAnnotation() {
removeAnnotation () {
if (this.annotationTool && this.annotationTool.isEnabled() &&
this.annotationTool.getMode() === PaintMode.Edit &&
this.annotationTool.getShape()) {
......@@ -4127,7 +4039,7 @@ export default {
}
},
updateAllStyles() {
updateAllStyles () {
if (this.annotationTool && this.annotationTool.getShape()) {
const shape = this.annotationTool.getShape();
......@@ -4210,7 +4122,7 @@ export default {
},
// 修改字体大小变化处理方法
handleFontSizeChange(value) {
handleFontSizeChange (value) {
this.textStyle.size = value;
this.updateAllStyles();
......@@ -4250,7 +4162,7 @@ export default {
},
// 修改字体变化处理方法
handleFontChange(value) {
handleFontChange (value) {
this.textStyle.font = value;
this.updateAllStyles();
......@@ -4289,15 +4201,15 @@ export default {
},
// 修改字体样式处理方法
handleFontStyleChange() {
handleFontStyleChange () {
this.updateAllStyles();
},
updateTextStyle() {
updateTextStyle () {
this.updateAllStyles();
},
updateLineStyle() {
updateLineStyle () {
// 创建新的线条样式
const lineStyle = new LineStyle({
color: this.lineStyle.color,
......@@ -4361,11 +4273,11 @@ export default {
}
},
updateFillStyle() {
updateFillStyle () {
this.updateAllStyles();
},
getFillPatternStyle(key) {
getFillPatternStyle (key) {
// 基本样式对象
const baseStyle = {
height: '24px',
......@@ -4406,7 +4318,7 @@ export default {
return baseStyle;
},
getLinePatternStyle(key) {
getLinePatternStyle (key) {
let dashStyle = '';
// 使用处理后的线条模式
......@@ -4427,7 +4339,7 @@ export default {
};
},
getProcessedLinePattern(pattern) {
getProcessedLinePattern (pattern) {
// 如果pattern是数组,直接返回
if (Array.isArray(pattern)) {
return pattern;
......@@ -4467,7 +4379,7 @@ export default {
return [];
},
debounce(fn, delay = 300) {
debounce (fn, delay = 300) {
let timer = null;
return (...args) => {
if (timer) clearTimeout(timer);
......@@ -4477,7 +4389,7 @@ export default {
};
},
checkWidgetStatus() {
checkWidgetStatus () {
if (!this._seismicWidget) {
console.warn('Widget未初始化');
this.loadingError = 'Widget未初始化';
......@@ -4507,7 +4419,7 @@ export default {
},
// 修改边框圆角更新方法
handleBorderRadiusChange(value) {
handleBorderRadiusChange (value) {
// 确保值为数字
const radius = parseInt(value) || 0;
this.textStyle.borderRadius = radius;
......@@ -4515,13 +4427,13 @@ export default {
},
// 修改固定字体更新方法
handleFixedFontChange(value) {
handleFixedFontChange (value) {
this.textStyle.fixedFont = value;
this.updateAllStyles();
},
// 添加清除所有工具状态的方法
clearAllTools() {
clearAllTools () {
this.showTextStylePanel = false;
this.showLineStylePanel = false;
this.isDrawingText = false;
......@@ -4543,7 +4455,7 @@ export default {
},
// 修改请求重绘的方法
requestRepaint() {
requestRepaint () {
// 只调用有invalidate方法的对象
if (this._seismicWidget) {
this._seismicWidget.invalidate();
......@@ -4557,7 +4469,7 @@ export default {
},
// 添加更新选择视觉反馈的方法
updateSelectionVisualFeedback() {
updateSelectionVisualFeedback () {
// 更新所有形状的选中状态
if (this.annotationLayer) {
const children = this.annotationLayer.getChildren();
......@@ -4574,7 +4486,7 @@ export default {
},
// 添加切换颜色映射的方法
switchColorMap(colorMapName) {
switchColorMap (colorMapName) {
if (!this.pipeline || !this.colorProvider) {
console.error('Pipeline or colorProvider not initialized');
return;
......@@ -4625,7 +4537,7 @@ export default {
},
// 添加注册颜色映射的方法
registerColorMaps(colorProvider) {
registerColorMaps (colorProvider) {
if (!colorProvider) {
console.error('colorProvider为空,无法注册颜色映射');
return;
......@@ -4898,7 +4810,7 @@ export default {
}
},
toggleLineStylePanel() {
toggleLineStylePanel () {
// 如果工具已经激活,则停用
if (this.showLineStylePanel) {
this.deactivatePencilTool();
......@@ -4998,7 +4910,7 @@ export default {
this.pencilTool.setEnabled(true);
},
deactivatePencilTool() {
deactivatePencilTool () {
this.showLineStylePanel = false;
if (this.pencilTool) {
this.pencilTool.setEnabled(false);
......@@ -5016,15 +4928,15 @@ export default {
}
},
zoomIn() {
zoomIn () {
this.zoomScale = Math.min(this.zoomScale * 1.2, 6);
},
zoomOut() {
zoomOut () {
this.zoomScale = Math.max(this.zoomScale / 1.2, 0.2);
},
async exportToPDF(data) {
async exportToPDF (data) {
if (!this.plots || !this.isWidgetReady) {
this.$message.warning('请等待数据加载完成');
return;
......@@ -5045,7 +4957,7 @@ export default {
/**
* 显示右键菜单
*/
showContextMenu(event) {
showContextMenu (event) {
event.preventDefault();
// 获取当前活动的工具
......@@ -5089,7 +5001,7 @@ export default {
/**
* 隐藏右键菜单
*/
hideContextMenu() {
hideContextMenu () {
if (this.contextMenu.visible) {
this.contextMenu.visible = false;
this._contextMenuTool = null;
......@@ -5100,7 +5012,7 @@ export default {
/**
* 检查菜单项是否可用
*/
isMenuItemEnabled(action) {
isMenuItemEnabled (action) {
// 获取当前活动的工具
const tool = this._contextMenuTool;
......@@ -5126,7 +5038,7 @@ export default {
/**
* 处理菜单动作
*/
handleMenuAction(action) {
handleMenuAction (action) {
if (!this.isMenuItemEnabled(action)) return;
const tool = this._contextMenuTool;
......@@ -5168,7 +5080,7 @@ export default {
this.requestRepaint();
},
copyShape(tool) {
copyShape (tool) {
const shape = tool.getShape();
if (!shape) return;
......@@ -5178,7 +5090,7 @@ export default {
[shape.clone()];
},
pasteShape() {
pasteShape () {
if (!this.clipboard || this.clipboard.length === 0) return;
// 创建偏移量,以便粘贴的形状不会完全覆盖原始形状
......@@ -5200,7 +5112,7 @@ export default {
this.requestRepaint();
},
deleteShape(tool) {
deleteShape (tool) {
const shape = tool.getShape();
if (!shape) return;
......@@ -5226,7 +5138,7 @@ export default {
this.requestRepaint();
},
handleKeyDown(event) {
handleKeyDown (event) {
// 检查是否有活动的工具
const activeTool = this.pencilTool && this.pencilTool.isEnabled() ? this.pencilTool :
(this.annotationTool && this.annotationTool.isEnabled() ? this.annotationTool : null);
......@@ -5272,7 +5184,7 @@ export default {
},
// 添加一个新方法,用于切换形状的编辑状态
toggleShapeEditMode(shape) {
toggleShapeEditMode (shape) {
if (!shape) return;
// 获取活动工具
......@@ -5296,7 +5208,7 @@ export default {
this.requestRepaint();
},
getElementIcon(mdiIcon) {
getElementIcon (mdiIcon) {
// 将Vuetify的mdi图标转换为Element UI图标
const iconMap = {
'mdi-undo': 'el-icon-refresh-left',
......@@ -5310,19 +5222,19 @@ export default {
},
// 添加鼠标悬停相关事件
initCursorTools() {
initCursorTools () {
// 实现鼠标悬停相关事件的初始化逻辑
},
// 添加鼠标移动处理方法
handleMouseMove(event) {
handleMouseMove (event) {
if (!this._seismicWidget || !this.isHovering) return;
// 使用内置状态栏,不需要额外的处理逻辑
},
// 添加保存标注数据的方法
saveAnnotation(node) {
saveAnnotation (node) {
if (!node) {
console.warn('无法保存标注:节点为空');
return;
......@@ -5406,7 +5318,7 @@ export default {
},
// 添加恢复标注数据的方法
restoreAnnotations() {
restoreAnnotations () {
// 先恢复文本标注
if (!this.annotations || !this._seismicWidget || !this.savedAnnotations) {
console.warn('无法恢复标注:缺少必要组件', {
......@@ -5488,7 +5400,7 @@ export default {
},
// 添加恢复线条标注的方法
restoreLineAnnotations() {
restoreLineAnnotations () {
if (!this.annotations || !this._seismicWidget || !this.savedLineAnnotations) {
console.warn('无法恢复线条标注:缺少必要组件');
return;
......@@ -5561,7 +5473,7 @@ export default {
},
// 添加在指定位置创建文本的方法
createTextAtPosition() {
createTextAtPosition () {
if (!this._seismicWidget || !this.annotations) {
console.warn('Widget or annotations layer not ready');
return;
......@@ -5611,7 +5523,7 @@ export default {
},
// 添加新的辅助方法
detectChanges(previousState, currentNode) {
detectChanges (previousState, currentNode) {
if (!previousState || !currentNode) return {};
const changes = {};
......@@ -5668,7 +5580,7 @@ export default {
},
// 切换内部滚动条同步
toggleInternalScrollbarSync() {
toggleInternalScrollbarSync () {
this.internalScrollbarSyncEnabled = !this.internalScrollbarSyncEnabled;
if (this.internalScrollbarSyncEnabled) {
......@@ -5686,7 +5598,7 @@ export default {
}
},
beforeDestroy() {
beforeDestroy () {
// 如果缩略图容器被移动到body,销毁前移回组件内,避免遗留
try {
if (this.$refs && this.$refs.thumbContainer && this.$refs.thumbContainer.parentNode === document.body) {
......@@ -5791,7 +5703,7 @@ export default {
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 220px;
margin-bottom: 110px;
/* 顶部操作与地震图拉开距离 */
min-width: 100%;
/* 确保有足够宽度 */
......@@ -5799,6 +5711,7 @@ export default {
/* 允许内容溢出显示 */
position: relative;
z-index: 20;
/* border: 1px solid blue; */
/* 确保不被地震图覆盖 */
}
......@@ -5811,24 +5724,17 @@ export default {
}
.right-thumb {
display: flex;
flex-direction: column;
width: 260px;
height: 200px;
/* 固定高度,确保有足够空间 */
min-height: 200px;
background: #ffffff;
cursor: pointer;
position: fixed;
top: 16px;
right: 16px;
/* 固定在视口右上角 */
top: 50px;
right: 40px;
width: 280px;
background: #fff;
border: 1px solid rgba(0, 0, 0, 0.15);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 9999;
flex-shrink: 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
/* 确保在画布之上 */
border-radius: 4px;
border: 1px solid #dcdfe6;
visibility: visible !important;
opacity: 1 !important;
}
.thumb-title {
......@@ -6013,7 +5919,7 @@ export default {
.toolbar .el-button.active {
background-color: #e6e6e6;
border-color: #adadad;
color: #409EFF;
color: #409eff;
}
/* 鼠标悬停信息面板样式 */
......@@ -6279,27 +6185,48 @@ export default {
}
.CustomRedScale {
background: linear-gradient(to right, #F62814, #DD8B66, #D7C692, #FBFAFF, #6E6E8A, #323136);
background: linear-gradient(to right,
#f62814,
#dd8b66,
#d7c692,
#fbfaff,
#6e6e8a,
#323136);
}
.BlueWhiteRed {
background: linear-gradient(to right, #0000FF, #FFFFFF, #FF0000);
background: linear-gradient(to right, #0000ff, #ffffff, #ff0000);
}
.Rainbow {
background: linear-gradient(to right, #FF0000, #FFFF00, #00FF00, #00FFFF, #0000FF, #FF00FF, #FF0000);
background: linear-gradient(to right,
#ff0000,
#ffff00,
#00ff00,
#00ffff,
#0000ff,
#ff00ff,
#ff0000);
}
.Jet {
background: linear-gradient(to right, #00008F, #0000FF, #00FFFF, #00FF00, #FFFF00, #FF7F00, #FF0000, #7F0000);
background: linear-gradient(to right,
#00008f,
#0000ff,
#00ffff,
#00ff00,
#ffff00,
#ff7f00,
#ff0000,
#7f0000);
}
.Hot {
background: linear-gradient(to right, #000000, #FF0000, #FFFF00, #FFFFFF);
background: linear-gradient(to right, #000000, #ff0000, #ffff00, #ffffff);
}
.Cool {
background: linear-gradient(to right, #00FFFF, #FF00FF);
background: linear-gradient(to right, #00ffff, #ff00ff);
}
/* 在style标签中添加 */
......@@ -6406,8 +6333,9 @@ export default {
.scrollbar-track,
.gt-scrollbar-thumb,
.gt-scrollbar-track,
[class*="thumb"],
[class*="track"] {
/* 仅在地震图可滚动区域内隐藏内部滚动条相关“thumb/track”,避免误伤缩略图 */
.sync-section [class*="thumb"]:not(.thumb-holder):not(.thumb-title):not(.right-thumb),
.sync-section [class*="track"] {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
......@@ -6482,27 +6410,6 @@ export default {
</style>
<style>
/* 让被移动到 body 下的缩略图容器也能生效(非 scoped 全局样式) */
.right-thumb {
display: flex;
flex-direction: column;
width: 260px;
height: 200px;
min-height: 200px;
background: #ffffff;
cursor: pointer;
position: fixed;
top: 16px;
right: 16px;
z-index: 9999;
flex-shrink: 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
border-radius: 4px;
border: 1px solid #dcdfe6;
visibility: visible !important;
opacity: 1 !important;
}
.thumb-holder {
flex: none;
width: 260px;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
<template>
<template>
<div class="ysgc-container"
:class="{ compact }">
<!-- <el-form :inline="true" size="small" class="toolbar" label-width="80px">
<el-form-item label="项目名称">
<el-input v-model="filters.projectName" placeholder="请输入" class="input" />
</el-form-item>
<el-form-item label="区块">
<el-input v-model="filters.block" placeholder="请输入" class="input" />
</el-form-item>
</el-form> -->
<div ref="chartRef"
class="chart"
:style="compact ? { width: (width || 260) + 'px', height: (height || 160) + 'px' } : {}"></div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import { toDht } from '@/api/ysqqXmxx/ysqqXmxx'
export default {
name: 'YsgcIndex',
props: {
compact: { type: Boolean, default: false },
width: { type: Number, default: 0 },
height: { type: Number, default: 0 },
idOverride: { type: [String, Number], default: null }
},
data () {
return {
chart: null,
resizeObserver: null,
loadingDht: false,
dhtResult: null,
segyLines: [],
filters: {
projectName: '',
block: ''
},
// 示例数据,后续可由接口/路由参数加载
xMin: null,
xMax: null,
yMin: null,
yMax: null,
verticalLineX: null,
points: []
}
},
mounted () {
// 仅使用延迟初始化,确保容器有尺寸后再 init
this.deferInit()
window.addEventListener('resize', this.resizeChart)
// 若路由中带有 id,则尝试调用接口
this.loadDht()
// 兜底:在下一帧强制渲染一次(compact 时保证首屏显示)
this.$nextTick(() => setTimeout(() => {
if (this.chart) {
this.renderChart()
this.chart.resize()
}
}, 50))
// 对于缩略图模式,确保即使在隐藏状态下也能渲染
if (this.compact) {
// 强制初始化并渲染
this.$nextTick(() => {
this.initChart()
this.renderChart()
})
}
},
created () {
// 尽早触发接口调用,进入页面即发起请求
this.loadDht()
},
activated () {
// 当页面被 keep-alive 缓存后再次进入时,确保尺寸和图表刷新
this.deferInit()
this.resizeChart()
},
beforeDestroy () {
window.removeEventListener('resize', this.resizeChart)
if (this.chart) {
this.chart.dispose()
this.chart = null
}
if (this.resizeObserver) {
try { this.resizeObserver.disconnect() } catch (e) { }
this.resizeObserver = null
}
},
methods: {
// 等待容器有有效尺寸再初始化,避免 echarts 容器宽高为 0 报错
deferInit () {
const tryInit = () => {
const el = this.$refs.chartRef
if (!el) return
// 缩略图模式:尺寸已知,直接初始化
if (this.compact) {
this.initChart()
return
}
let w = el.clientWidth
let h = el.clientHeight
console.log('[ysgc] before size', w, h)
if (w === 0) {
el.style.width = '800px'
w = el.clientWidth
}
if (h === 0) {
// 若元素当前不可见(display: none)或父容器高度为 0,clientHeight 依然为 0
// 使用 ResizeObserver 监听直到有高度后再初始化
if (!this.resizeObserver) {
this.resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
const cr = entry.contentRect
if (cr.height > 0 && cr.width > 0) {
console.log('[ysgc] ResizeObserver visible', cr.width, cr.height)
if (this.resizeObserver) {
try { this.resizeObserver.disconnect() } catch (e) { }
this.resizeObserver = null
}
this.initChart()
}
}
})
this.resizeObserver.observe(el)
}
}
console.log('[ysgc] after size', w, h)
// 即使高度为 0,也先用回退尺寸初始化,待可见后再 resize
this.initChart()
// 兜底:稍后再尝试一次 resize
setTimeout(() => this.resizeChart(), 150)
}
this.$nextTick(tryInit)
},
resizeChart () {
if (this.chart) this.chart.resize()
},
initChart () {
const el = this.$refs.chartRef
if (!el) return
if (this.chart) {
this.chart.dispose()
this.chart = null
}
// 当容器高度为 0 时,先用显式宽高初始化,等可见后再 resize
const initWidth = this.compact ? (this.width || 260) : (el.clientWidth || 800)
const initHeight = this.compact ? (this.height || 160) : (el.clientHeight || 600)
this.chart = echarts.init(el, null, { width: initWidth, height: initHeight })
// 初始渲染
this.renderChart()
this.resizeChart()
// 对于缩略图模式,确保渲染成功
if (this.compact) {
console.log('[ysgc] 缩略图初始化完成', initWidth, initHeight)
}
},
// 数据变化后自动刷新(确保缩略图及时绘制)
_requestRender () {
if (!this.chart) return
this.$nextTick(() => {
this.renderChart()
this.chart.resize()
// 确保缩略图正确显示
if (this.compact) {
console.log('[ysgc] 缩略图渲染完成')
}
})
},
// 调用地层/断层图接口(示例),优先从路由参数获取 id
loadDht () {
if (this.loadingDht || this.dhtResult) return
try {
const route = this.$route || {}
const params = route.params || {}
const query = route.query || {}
const id = this.idOverride != null ? this.idOverride : (params.zbid || query.zbid || params.id || query.id)
if (!id) return
this.loadingDht = true
toDht(id)
.then(res => {
// 根据后端返回结构做适配:优先取 data 字段
this.dhtResult = (res && (res.data !== undefined ? res.data : res)) || null
this.applyDhtResult()
if (this.chart) this._requestRender()
})
.catch(err => {
// 保留错误日志,避免打断现有流程
console.error('[ysgc] toDht error:', err)
})
.finally(() => {
this.loadingDht = false
})
} catch (e) {
console.error('[ysgc] loadDht failed:', e)
}
},
// 将接口返回的数据应用到图表状态
applyDhtResult () {
const data = this.dhtResult || {}
// 边界
this.xMin = data.xmin != null ? Number(data.xmin) : this.xMin
this.xMax = data.xmax != null ? Number(data.xmax) : this.xMax
this.yMin = data.ymin != null ? Number(data.ymin) : this.yMin
this.yMax = data.ymax != null ? Number(data.ymax) : this.yMax
// 井点
const list = Array.isArray(data.ysqqXmxxJxxList) ? data.ysqqXmxxJxxList : []
if (list.length) {
this.points = list
.filter(it => it && it.x != null && it.y != null)
.map(it => ({ name: String(it.jh || ''), x: Number(it.x), y: Number(it.y) }))
}
// 线(SEGY)
const segys = Array.isArray(data.ysqqXmxxSegy) ? data.ysqqXmxxSegy : []
console.log(segys, 'segys');
const lines = segys.map((seg, idx) => {
try {
const arr = typeof seg.x === 'string' ? JSON.parse(seg.x) : (Array.isArray(seg.x) ? seg.x : [])
// 若存在有效的 sxh,则按 sxh 升序连线;否则按原始顺序
const enriched = arr
.filter(p => p && p.x != null && p.y != null)
.map(p => ({ sxh: Number(p.sxh), x: Number(p.x), y: Number(p.y) }))
.filter(p => Number.isFinite(p.x) && Number.isFinite(p.y))
const hasSxh = enriched.every(p => Number.isFinite(p.sxh))
const ordered = hasSxh ? enriched.sort((a, b) => a.sxh - b.sxh) : enriched
const pairs = ordered.map(p => [p.x, p.y])
try {
const cloneForLog = pairs.map(it => [it[0], it[1]])
console.log('[ysgc] segy parsed', { index: idx, id: seg && seg.id, points: cloneForLog.length, sample: cloneForLog.slice(0, 5) })
console.log('[ysgc] segy full points', { index: idx, id: seg && seg.id, pointsArray: cloneForLog })
} catch (e) { }
return pairs
} catch (e) {
console.error('[ysgc] segy parse error', { index: idx, id: seg && seg.id, err: e })
return []
}
}).filter(line => line.length > 0)
this.segyLines = lines
try {
console.log('[ysgc] segyLines summary', { lines: this.segyLines.length, lengths: this.segyLines.map(l => l.length) })
} catch (e) { }
},
// 生成图表配置
buildOption () {
// 若未提供边界,则根据数据自动估算
const allXs = []
const allYs = []
for (const p of this.points) { allXs.push(p.x); allYs.push(p.y) }
for (const line of this.segyLines) {
for (const [lx, ly] of line) { allXs.push(lx); allYs.push(ly) }
}
if (allXs.length && (this.xMin == null || this.xMax == null)) {
const padX = Math.max(1, Math.round((Math.max(...allXs) - Math.min(...allXs)) * 0.02))
this.xMin = this.xMin != null ? this.xMin : Math.min(...allXs) - padX
this.xMax = this.xMax != null ? this.xMax : Math.max(...allXs) + padX
}
if (allYs.length && (this.yMin == null || this.yMax == null)) {
const padY = Math.max(1, Math.round((Math.max(...allYs) - Math.min(...allYs)) * 0.02))
this.yMin = this.yMin != null ? this.yMin : Math.min(...allYs) - padY
this.yMax = this.yMax != null ? this.yMax : Math.max(...allYs) + padY
}
const series = []
// 井点散点图
if (this.points && this.points.length) {
series.push({
type: 'scatter',
symbolSize: this.compact ? 8 : 12,
data: this.points.map(p => [p.x, p.y, p.name]),
itemStyle: { color: '#2d72ff', borderColor: '#2d72ff' },
label: this.compact ? { show: false } : { show: true, position: 'top', color: '#333', formatter: params => params.data[2] }
})
}
// 叠加 SEGY 线
for (const line of this.segyLines) {
series.push({
type: 'line',
coordinateSystem: 'cartesian2d',
data: line,
showSymbol: false,
connectNulls: true,
smooth: false,
emphasis: { scale: false },
encode: { x: 0, y: 1 },
lineStyle: { color: '#00aa66', width: 2 },
tooltip: { show: false }
})
// 调试:在每条线之上叠加小点,便于核对路径
series.push({
type: 'scatter',
data: line,
symbolSize: 3,
itemStyle: { color: '#888' },
tooltip: { show: false }
})
}
// 垂直参考线(可选)
if (this.verticalLineX != null && this.yMin != null && this.yMax != null) {
series.push({
type: 'line',
data: [[this.verticalLineX, this.yMin], [this.verticalLineX, this.yMax]],
showSymbol: false,
lineStyle: { color: '#222', width: 2 },
tooltip: { show: false }
})
}
return {
backgroundColor: this.compact ? '#f2f2f2' : '#ffffff',
grid: this.compact ? { top: 6, left: 6, right: 6, bottom: 6, containLabel: false } : { top: 40, left: 50, right: 40, bottom: 50, containLabel: true },
xAxis: this.compact ? { type: 'value', min: this.xMin, max: this.xMax, scale: true, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { show: false }, splitLine: { show: false } } : { type: 'value', min: this.xMin, max: this.xMax, scale: false, axisLine: { show: true }, axisTick: { show: true }, axisLabel: { color: '#666' }, splitLine: { show: true, lineStyle: { color: '#9bb3e7', opacity: 0.6 } } },
yAxis: this.compact ? { type: 'value', min: this.yMin, max: this.yMax, scale: true, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { show: false }, splitLine: { show: false } } : { type: 'value', min: this.yMin, max: this.yMax, scale: false, axisLine: { show: true }, axisTick: { show: true }, axisLabel: { color: '#666' }, splitLine: { show: true, lineStyle: { color: '#9bb3e7', opacity: 0.6 } } },
series,
tooltip: { trigger: 'item' }
}
},
// 渲染或更新图表
renderChart () {
if (!this.chart) return
const option = this.buildOption()
this.chart.setOption(option, true)
}
}
}
</script>
<style lang="scss" scoped>
.ysgc-container {
display: flex;
flex-direction: column;
height: 88vh;
overflow: hidden;
/* 避免出现滚动条 */
}
.toolbar {
display: flex;
align-items: center;
gap: 80px;
padding: 10px 0 8px 10px;
}
.field {
display: flex;
align-items: center;
}
.label {
font-size: 20px;
font-weight: 600;
margin-right: 16px;
}
.input {
width: 180px;
}
.chart {
flex: 1;
min-height: 0;
/* 让 flex 子项在容器内收缩,避免溢出 */
width: 100%;
margin: 0;
/* 取消外边距,防止产生滚动条 */
background: #fff;
border: 1px solid #e0e0e0;
}
</style>
\ No newline at end of file
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